ブログ | NGINX

動的 Kubernetes クラウド環境での NGINX Ingress コントローラーのパフォーマンス テスト

アミール・ラウダットのサムネイル
アミール・ラウダット
2020年9月22日公開

編集者– この投稿は10 部構成のシリーズの一部です。

  1. プロダクショングレードのKubernetesで複雑さを軽減
  2. 高度なトラフィック管理で Kubernetes の回復力を向上させる方法
  3. Kubernetes の可視性を向上させる方法
  4. トラフィック管理ツールを使用して Kubernetes を保護する 6 つの方法
  5. Ingress コントローラーの選択ガイド、パート 1: 要件を特定する
  6. Ingress コントローラーの選択ガイド、パート 2: リスクと将来への備え
  7. Ingress コントローラーの選択ガイド、パート 3: オープンソース vs. デフォルト vs. コマーシャル
  8. Ingress コントローラーの選択ガイド、パート 4: NGINX Ingress コントローラー オプション
  9. サービスメッシュの選択方法
  10. 動的 Kubernetes クラウド環境での NGINX Ingress コントローラーのパフォーマンス テスト (この投稿)

また、ブログの完全セットを無料の電子書籍「 Taking Kubernetes from Test to Production」としてダウンロードすることもできます。

コンテナ化されたアプリケーションを本番環境で実行する企業が増えるにつれて、Kubernetes はコンテナ オーケストレーションの標準ツールとしての地位を固め続けています。 同時に、COVID-19パンデミックによる在宅勤務の取り組みによりインターネットトラフィックの増加が加速したため、クラウドコンピューティングの需要は数年早まっています。 顧客が大規模なネットワーク障害や過負荷を経験しているため、企業はインフラストラクチャのアップグレードに急いで取り組んでいます。

クラウドベースのマイクロサービス環境で必要なレベルのパフォーマンスを達成するには、次世代のハイパースケール データ センターの拡張性とパフォーマンスを活用する、高速で完全に動的なソフトウェアが必要です。 Kubernetes を使用してコンテナを管理する多くの組織は、NGINX ベースの Ingress コントローラーを使用してユーザーにアプリを配信しています。

このブログでは、現実的なマルチクラウド環境における 3 つの NGINX Ingress コントローラーのパフォーマンス テストの結果を報告し、インターネット経由のクライアント接続のレイテンシを測定します。

テストプロトコルと収集されたメトリクス

負荷生成プログラムwrk2 を使用してクライアントをエミュレートし、定義された期間に HTTPS 経由で継続的にリクエストを送信しました。 テスト対象の Ingress コントローラー (コミュニティ Ingress コントローラー、NGINX Open Source に基づく NGINX Ingress コントローラー、または NGINX Plus に基づく NGINX Ingress コントローラー) は、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 コントローラーは、SSL ターミネーション (Kubernetes Secretを参照) とレイヤー 7 ルーティング用に設定され、タイプLoadBalancerの Kubernetesサービスを介して公開されました。 バックエンド アプリケーションは、GKE-node-2上の Kubernetes Deploymentとして実行されました。

クラウド マシンの種類とソフトウェア構成の詳細については、付録を参照してください。

テスト方法

クライアントの展開

AWS クライアントマシンで次のwrk2 (バージョン 4.0.0) スクリプトを実行しました。 2 つのwrkスレッドが生成され、それらが一緒に GKE にデプロイされた Ingress コントローラへの 1000 の接続を確立します。 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 暗号化には、 2048 ビットのキー サイズと Perfect Forward Secrecy を備えた RSA を使用しました。

バックエンドアプリケーション( https://app.example.com:443でアクセス)からの各応答は、約1KBの基本的なサーバーメタデータと、 200OK HTTP ステータス コード。

バックエンドアプリケーションの展開

バックエンド アプリケーションの静的デプロイメントと動的デプロイメントの両方でテスト実行を実施しました。

静的デプロイメントでは、5 つの Pod レプリカがあり、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 オープンソースに基づいており、静的デプロイメントでは Ingress コントローラーから再構成する必要がないことを考えると、これは理にかなっています。

動的展開のレイテンシ結果

このグラフは、バックエンド アプリケーションを 5 つのレプリカ ポッドから 7 つまで定期的にスケーリングした動的デプロイメントで、各 Ingress コントローラによって発生したレイテンシを示しています (詳細については、 「バックエンド アプリケーションのデプロイメント」を参照してください)。

この環境では、 NGINX Plus ベースのIngress コントローラーだけが優れたパフォーマンスを発揮し、99.99 パーセンタイルまで実質的に遅延が発生しないことは明らかです。 コミュニティと NGINX オープンソース ベースの Ingress コントローラーはどちらも、かなり低いパーセンタイルで顕著な遅延が発生しますが、パターンはかなり異なります。 コミュニティ Ingress コントローラーの場合、レイテンシは緩やかに、しかし着実に 99 パーセンタイルまで上昇し、そこで約 5000 ミリ秒 (5 秒) で安定します。 NGINX オープンソースベースの Ingress コントローラの場合、レイテンシは 99 パーセンタイルで約 32 秒に急上昇し、99.99 パーセンタイルでは再び60 秒に上昇します

「動的デプロイメントのタイムアウトとエラー結果」でさらに説明するように、コミュニティと NGINX オープンソースベースの Ingress コントローラーで発生する遅延は、バックエンド アプリケーションのエンドポイントの変更に応じて NGINX 構成が更新および再ロードされた後に発生するエラーとタイムアウトによって発生します。

以下は、前のグラフと同じテスト条件でのコミュニティ Ingress コントローラーとNGINX Plus ベースのIngress コントローラーの結果の詳細なビューです。 NGINX Plus ベースのIngress コントローラーは、99.99 パーセンタイルまでは実質的に遅延が発生しませんが、99.9999 パーセンタイルでは 254 ミリ秒に向かって上昇し始めます。 コミュニティ Ingress コントローラーのレイテンシ パターンは、99 パーセンタイルで 5000 ミリ秒のレイテンシまで着実に増加し、その時点でレイテンシは安定します。

動的デプロイメントのタイムアウトとエラー結果

この表には、遅延結果の原因がより詳しく示されています。

  NGINX オープンソース コミュニティ NGINX プラス
接続エラー 33365 0 0
接続タイムアウト 309 8809 0
読み取りエラー 4650 0 0

NGINX オープンソース ベースの Ingress コントローラーでは、バックエンド アプリケーションのエンドポイントが変更されるたびに NGINX 構成を更新して再ロードする必要があるため、多くの接続エラー、接続タイムアウト、読み取りエラーが発生します。 接続/ソケット エラーは、NGINX がリロードするのにかかる短い時間の間に、クライアントが NGINX プロセスに割り当てられなくなったソケットに接続しようとすると発生します。 接続タイムアウトは、クライアントが Ingress コントローラーへの接続を確立したが、バックエンド エンドポイントが利用できなくなった場合に発生します。 エラーとタイムアウトはどちらもレイテンシに重大な影響を及ぼし、99 パーセンタイルでは 32 秒、99.99 パーセンタイルでは再び60 秒に急上昇します。

コミュニティ Ingress コントローラーでは、バックエンド アプリケーションのスケールアップとスケールダウンに伴うエンドポイントの変更により、8,809 回の接続タイムアウトが発生しました。 コミュニティ Ingress コントローラーは、エンドポイントが変更されたときに構成の再読み込みを回避するために Lua コードを使用します。 結果は、NGINX 内で Lua ハンドラーを実行してエンドポイントの変更を検出すると、エンドポイントを変更するたびに構成を再ロードする必要があることから生じる NGINX オープンソース ベース バージョンのパフォーマンス制限の一部に対処できることを示しています。 それでも、接続タイムアウトは依然として発生し、高いパーセンタイルでは大きな遅延が発生します。

NGINX Plus ベースのIngress コントローラーでは、エラーやタイムアウトは発生せず、動的環境はパフォーマンスにほとんど影響を与えませんでした。 これは、エンドポイントが変更されたときに NGINX Plus API を使用して NGINX 構成を動的に更新するためです。 前述のように、最高のレイテンシは 254 ミリ秒で、99.9999 パーセンタイルでのみ発生しました。

結論

パフォーマンス結果から、動的な Kubernetes クラウド環境でタイムアウトとエラーを完全に排除するには、Ingress コントローラーがイベント ハンドラーや構成の再読み込みなしでバックエンド エンドポイントの変更に動的に調整する必要があることがわかります。 結果に基づくと、 NGINX Plus API は、動的な環境で NGINX を動的に再構成するための最適なソリューションであると言えます。 私たちのテストでは、 NGINX Plus ベースのIngress コントローラーのみが、ユーザーの満足を維持するために必要な、高度に動的な Kubernetes 環境で完璧なパフォーマンスを達成しました。

付録

クラウドマシンの仕様

機械 クラウドプロバイダー マシンタイプ
クライアント アマゾン m5a.4xlarge
GKE ノード 1 グーグル e2-標準-32
GKE ノード 2 グーグル e2-標準-32

NGINX オープンソースおよび NGINX Plus Ingress コントローラーの構成

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 Open Source の構成では、必要に応じてnginx‑plusへの参照が調整されました。
  • NGINX App Protect はイメージ ( gcr.io/nginx-demos/nap-ingress:edge ) に含まれていますが、無効になっています ( -enable-app-protectフラグが省略されています)。

構成マップ

kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
  namespace: nginx-ingress
data:
  worker-connections: "10000"
  worker-rlimit-nofile: "10240"
  keepalive: "100"
  keepalive-requests: "100000000"

コミュニティ NGINX Ingress コントローラーの設定

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-admission

構成マップ

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
---

構成マップ

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_port\nServer name:$hostname\nDate: $time_local\nURI: $request_uri\nRequest ID: $request_id\n';}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
---

「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 q。"