編集者 – この記事は全10回のシリーズの一部です:
- 本番環境対応のKubernetesで複雑さを軽減
- 高度なトラフィック管理でKubernetesのレジリエンスを向上させる方法
- Kubernetesの可視性を向上させる方法
- トラフィック管理ツールを使用してKubernetesを保護する6つの方法
- Ingress Controller選定ガイド 第1部:要件の特定
- Ingress Controller選定ガイド 第2部:リスクと将来への備え
- Ingress Controller選定ガイド 第3回:オープンソース、デフォルト、商用の比較
- Ingress Controller選定ガイド、第4回:NGINX Ingress Controllerの選択肢
- サービスメッシュの選び方
- 動的なKubernetesクラウド環境におけるNGINX Ingress Controllerのパフォーマンステスト(本記事)
一連のブログ記事をまとめた無料のeBook「Kubernetesをテスト環境から本番環境へ」もダウンロードできます。
コンテナ化されたアプリを本番環境で運用する企業が増える中、Kubernetesはコンテナオーケストレーションの標準ツールとしての地位を確立しつつあります。同時に、COVID‑19のパンデミックを受けた在宅勤務の取り組みによりインターネットトラフィックの増加が加速し、クラウドコンピューティングの需要は数年前倒しで拡大しました。顧客が大規模なネットワーク障害や過負荷に直面しているため、企業はインフラストラクチャのアップグレードを急いでいます。
クラウドベースのマイクロサービス環境で求められるレベルのパフォーマンスを実現するには、次世代ハイパースケールデータセンターの拡張性とパフォーマンスを活用できる、高速かつ完全に動的なソフトウェアが必要です。Kubernetesでコンテナを管理する多くの組織は、アプリをユーザーに提供するために、NGINXベースのIngressコントローラーを利用しています。
このブログでは、実際の運用環境に近いマルチクラウド環境で3つのNGINX Ingress Controllerを対象に実施したパフォーマンステストの結果を紹介します。このテストでは、インターネット経由のクライアント接続のレイテンシを測定しました。
- Kubernetesコミュニティによってメンテナンスされ、NGINX Open Sourceをベースとする「NGINX Ingress Controller」です。以前のブログと同様、ここでは「コミュニティ版Ingressコントローラー」と呼びます。テストでは、Google Container Registryから取得したイメージを使用し、バージョン0.34.1を検証しました。
- NGINX Open SourceをベースとするNGINX Ingress Controller(NGINXが保守するバージョン1.8.0)。
- NGINX PlusベースのNGINX Ingress Controller、NGINXが保守するバージョン1.8.0。
テスト手順と収集したメトリクス
負荷生成プログラム「wrk2」を使用してクライアントをエミュレートし、所定の期間にわたってHTTPSリクエストを継続的に送信しました。テスト対象のIngressコントローラー(コミュニティ版Ingressコントローラー、NGINX Open SourceベースのNGINX Ingress Controller、またはNGINX PlusベースのNGINX Ingress Controller)は、Kubernetes Podにデプロイされたバックエンドアプリケーションにリクエストを転送し、アプリケーションが生成したレスポンスをクライアントに返しました。一定量のクライアントトラフィックを継続的に生成してIngressコントローラーにストレステストを実施し、以下のパフォーマンス指標を収集しました。
- レイテンシー:クライアントがリクエストを生成してからレスポンスを受信するまでの時間です。レイテンシーはパーセンタイル分布で示します。たとえば、レイテンシーテストのサンプルが100件ある場合、99パーセンタイル値は、100回のテスト実行で測定されたレスポンスのうち、2番目に遅いレイテンシーです。
- 接続タイムアウト – Ingressコントローラーが一定時間内にリクエストへ応答できなかったため、通知なく切断または破棄されたTCP接続。
- 読み取りエラー – Ingressコントローラーのソケットが閉じられたために失敗した、接続からの読み取り試行。
- 接続エラー – クライアントとIngressコントローラー間で確立されなかったTCP接続。
トポロジー
すべてのテストで、AWSのクライアントマシン上で動作するwrk2ユーティリティを使用してリクエストを生成しました。AWSクライアントは、Google Kubernetes Engine(GKE)環境のGKE-node-1にKubernetes DaemonSetとしてデプロイされたIngressコントローラーの外部IPアドレスに接続しました。Ingressコントローラーには、Kubernetes Secretを参照するSSL終端とレイヤー7ルーティングを設定し、Kubernetes ServiceのTypeをLoadBalancerとして公開しました。バックエンドアプリケーションは、GKE-node-2上でKubernetes Deploymentとして実行しました。

クラウドマシンのタイプとソフトウェア構成の詳細については、「付録」を参照してください。
テスト方法
クライアントのデプロイ
AWSクライアントマシンで、次のwrk2(バージョン4.0.0)スクリプトを実行しました。このスクリプトは2つのwrkスレッドを起動し、GKEにデプロイされたIngressコントローラーに合計1,000の接続を確立します。各3分間のテスト実行中、スクリプトは1秒あたり30,000リクエスト(RPS)を生成します。これは、本番環境におけるIngressコントローラーの負荷を適切に再現するものと考えています。
wrk -t2 -c1000 -d180s -L -R30000 https://app.example.com:443/各項目の意味は次のとおりです。
-t– スレッド数を設定します(2)-c– TCP接続数を設定します(1000)-d– テスト実行時間を秒単位で設定します(180秒、つまり3分)-L– 分析ツールにエクスポートするための詳細なレイテンシパーセンタイル情報を生成します-R– RPS数を設定します(30,000)
TLS暗号化には、2,048ビットの鍵長を持つRSAとPerfect Forward Secrecyを使用しました。
バックエンドアプリケーション(アクセス先:https://app.example.com:443)からの各レスポンスは、約1 KBの基本的なサーバーメタデータと、HTTPステータスコード「200 OK」で構成されます。
バックエンドアプリケーションのデプロイ
バックエンドアプリケーションを静的および動的にデプロイし、それぞれテストを実施しました。
静的デプロイでは、Podレプリカを5つ用意し、Kubernetes APIを使用した変更は適用しませんでした。
動的デプロイでは、次のスクリプトを使用して、バックエンドのnginxデプロイを5つのPodレプリカから7つに定期的にスケールアウトし、その後5つにスケールインしました。これにより、動的なKubernetes環境を再現し、Ingressコントローラーがエンドポイントの変更にどの程度効果的に適応できるかをテストしました。
while [ 1 -eq 1 ]
do
kubectl scale deployment nginx --replicas=5
sleep 12
kubectl scale deployment nginx --replicas=7
sleep 10
doneパフォーマンス結果
静的デプロイメントのレイテンシ結果
グラフが示すように、バックエンドアプリケーションを静的にデプロイした場合、3つのIngressコントローラーはいずれも同程度のパフォーマンスを達成しました。これは、すべてNGINX Open Sourceをベースとしており、静的デプロイではIngressコントローラーによる再構成が不要であることを考えれば、妥当な結果です。

動的デプロイメントのレイテンシ結果
このグラフは、バックエンドアプリケーションのレプリカPod数を5から7に増やした後、再び5に戻すスケーリングを定期的に行う動的デプロイメントにおいて、各Ingressコントローラーで発生したレイテンシーを示しています(詳細については「バックエンドアプリケーションのデプロイメント」を参照してください)。
この環境で良好なパフォーマンスを発揮するのはNGINX PlusベースのIngressコントローラーだけであり、99.99パーセンタイルに至るまでレイテンシーはほとんど発生していません。コミュニティ版とNGINX Open SourceベースのIngressコントローラーでは、比較的低いパーセンタイルから顕著なレイテンシーが発生しますが、その傾向は大きく異なります。コミュニティ版Ingressコントローラーでは、レイテンシーが緩やかながら着実に増加し、99パーセンタイルで約5,000 ms(5秒)に達した後、横ばいになります。NGINX Open SourceベースのIngressコントローラーでは、レイテンシーが99パーセンタイルまでに約32秒へと急増し、99.99パーセンタイルまでに再び増加して60秒に達します。
詳しくは「動的デプロイにおけるタイムアウトとエラーの結果」で説明しますが、コミュニティ版およびNGINX Open SourceベースのIngressコントローラーで発生するレイテンシーは、バックエンドアプリケーションのエンドポイントの変更に応じてNGINXの設定が更新され、再読み込みされた後に発生するエラーとタイムアウトが原因です。

前のグラフと同じテスト条件における、コミュニティ版IngressコントローラーとNGINX PlusベースのIngressコントローラーの結果を、より詳細に示します。NGINX PlusベースのIngressコントローラーでは、99.99パーセンタイルまでレイテンシーがほとんど発生せず、その後増加し始め、99.9999パーセンタイルでは254 ms近くに達します。コミュニティ版Ingressコントローラーのレイテンシーは徐々に増加し、99パーセンタイルで5,000 msに達した後、横ばいになります。

動的デプロイメントのタイムアウトとエラーの結果
この表は、レイテンシの測定結果の原因をより詳しく示しています。
NGINX Open Source コミュニティ NGINX Plus接続エラー 33365 0 0接続タイムアウト 309 8809 0読み取りエラー 4650 0 0
NGINX Open SourceベースのIngressコントローラーでは、バックエンドアプリケーションのエンドポイントが変更されるたびにNGINXの設定を更新して再読み込みする必要があるため、多数の接続エラー、接続タイムアウト、読み取りエラーが発生します。接続/ソケットエラーは、NGINXの再読み込みにかかるわずかな時間に、クライアントがNGINXプロセスに割り当てられなくなったソケットへ接続しようとした場合に発生します。接続タイムアウトは、クライアントがIngressコントローラーとの接続を確立したものの、バックエンドエンドポイントがすでに利用できなくなっている場合に発生します。エラーとタイムアウトはいずれもレイテンシーに深刻な影響を及ぼし、99パーセンタイルでは32秒、99.99パーセンタイルでは60秒まで急増します。
コミュニティ版Ingressコントローラーでは、バックエンドアプリケーションのスケールアップおよびスケールダウンに伴うエンドポイントの変更により、8,809件の接続タイムアウトが発生しました。コミュニティ版Ingressコントローラーは、エンドポイントの変更時に設定を再読み込みしなくて済むよう、Luaコードを使用しています。結果から、NGINX内でLuaハンドラーを実行してエンドポイントの変更を検出することで、エンドポイントが変更されるたびに設定を再読み込みする必要があるNGINX Open Sourceベースのバージョンにおけるパフォーマンス上の制約の一部が解消されることがわかります。それでも接続タイムアウトは発生し、パーセンタイルが高くなるほどレイテンシーが大幅に増加します。
NGINX PlusベースのIngressコントローラーでは、エラーもタイムアウトも発生せず、動的環境がパフォーマンスに及ぼす影響はほとんどありませんでした。これは、エンドポイントの変更時にNGINX Plus APIを使用してNGINXの設定を動的に更新するためです。前述のとおり、最大レイテンシは254 msで、99.9999パーセンタイルでのみ発生しました。
まとめ
パフォーマンステストの結果から、動的なKubernetesクラウド環境でタイムアウトやエラーを完全に排除するには、Ingressコントローラーがイベントハンドラーや設定の再読み込みを使用せず、バックエンドエンドポイントの変更に動的に対応する必要があることが分かりました。この結果から、動的な環境でNGINXを動的に再構成するには、NGINX Plus APIが最適なソリューションであると言えます。テストでは、変化の激しいKubernetes環境において、ユーザーの満足度を維持するために必要な、エラーのないパフォーマンスを実現したのは、NGINX PlusベースのIngressコントローラーだけでした。
付録
クラウドマシンの仕様
マシン クラウドプロバイダー マシンタイプClient AWS m5a.4xlargeGKE-node-1 GCP e2-standard-32GKE-node-2 GCP e2-standard-32
NGINX Open SourceおよびNGINX Plus Ingress Controllerの構成
Kubernetesの設定
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ingress
namespace: nginx-ingress
spec:
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
#annotations:
#prometheus.io/scrape: "true"
#prometheus.io/port: "9113"
spec:
serviceAccountName: nginx-ingress
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: true
containers:
- image: gcr.io/nginx-demos/nap-ingress:edge
imagePullPolicy: Always
name: nginx-plus-ingress
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
- name: readiness-port
containerPort: 8081
#- name: prometheus
#containerPort: 9113
readinessProbe:
httpGet:
path: /nginx-ready
port: readiness-port
periodSeconds: 1
securityContext:
allowPrivilegeEscalation: true
runAsUser: 101 #nginx
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
args:
- -nginx-plus
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret注記:
- この構成はNGINX Plus向けです。
nginx‑plusへの参照は、NGINX Open Source向けの構成で必要に応じて調整されています。 - NGINX App Protectはイメージ(
gcr.io/nginx-demos/nap-ingress:edge)に含まれていますが、無効化されています(-enable-app-protectフラグは省略されています)。
ConfigMap
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-config
namespace: nginx-ingress
data:
worker-connections: "10000"
worker-rlimit-nofile: "10240"
keepalive: "100"
keepalive-requests: "100000000"Community NGINX Ingress Controllerの設定
Kubernetesの設定
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
helm.sh/chart: ingress-nginx-2.11.1
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.34.1
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
spec:
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-6nzr
hostNetwork: true
containers:
- name: controller
image: us.gcr.io/k8s-artifacts-prod/ingress-nginx/controller:v0.34.1@sha256:0e072dddd1f7f8fc8909a2ca6f65e76c5f0d2fcfb8be47935ae3457e8bbceb20
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
args:
- /nginx-ingress-controller
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 1
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
- name: webhook
containerPort: 8443
protocol: TCP
volumeMounts:
- name: webhook-cert
mountPath: /usr/local/certificates/
readOnly: true
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admissionConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
max-worker-connections: "10000"
max-worker-open-files: "10204"
upstream-keepalive-connections: "100"
keep-alive-requests: "100000000"バックエンドアプリの設定
Kubernetesの設定
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
nodeSelector:
kubernetes.io/hostname: gke-rawdata-cluster-default-pool-3ac53622-t2dz
containers:
- name: nginx
image: nginx
ports:
- containerPort: 8080
volumeMounts:
- name: main-config-volume
mountPath: /etc/nginx
- name: app-config-volume
mountPath: /etc/nginx/conf.d
readinessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 3
volumes:
- name: main-config-volume
configMap:
name: main-conf
- name: app-config-volume
configMap:
name: app-conf
---ConfigMaps
apiVersion: v1
kind: ConfigMap
metadata:
name: main-conf
namespace: default
data:
nginx.conf: |+
user nginx;
worker_processes 16;
worker_rlimit_nofile 102400;
worker_cpu_affinity auto 1111111111111111;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 100000;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
tcp_nodelay on;
access_log off;
include /etc/nginx/conf.d/*.conf;
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-conf
namespace: default
data:
app.conf: "server {listen 8080;location / {default_type text/plain;expires -1;return 200 'Server address: $server_addr:$server_portnServer name:$hostnamenDate: $time_localnURI: $request_urinRequest ID: $request_idn';}location /healthz {return 200 'I am happy and healthy :)';}}"
---サービス
apiVersion: v1
kind: Service
metadata:
name: app-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: nginx
---著者について

関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります