Ingress によるコンテナ ネイティブのロード バランシング


このページでは、Google Kubernetes Engine(GKE)でコンテナ ネイティブのロード バランシングを使用する方法について説明します。コンテナ ネイティブのロード バランシングにより、ロードバランサは Kubernetes Pod を直接ターゲットにして、トラフィックを Pod に均等に分配できます。

コンテナ ネイティブのロード バランシングのメリット、要件、制限事項の詳細については、コンテナ ネイティブのロード バランシングをご覧ください。

始める前に

始める前に、次の作業が完了していることを確認してください。

  • Google Kubernetes Engine API を有効にする。
  • Google Kubernetes Engine API の有効化
  • このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、gcloud components update を実行して最新のバージョンを取得する。

コンテナ ネイティブのロード バランシングを使用する

以下のセクションでは、GKE でのコンテナ ネイティブのロード バランシングの構成について説明します。GKE クラスタ 1.17 以降と一定の条件下では、コンテナ ネイティブのロード バランシングがデフォルトであり、明示的な cloud.google.com/neg: '{"ingress": true}' Service のアノテーションは必要ありません。

VPC ネイティブ クラスタを作成する

コンテナ ネイティブのロード バランシングを使用するには、GKE クラスタでエイリアス IP を有効にする必要があります。

たとえば、次のコマンドは、自動プロビジョニングされたサブネットワークを使用して、GKE クラスタ neg-demo-cluster を作成します。

  • Autopilot モードでは、エイリアス IP アドレスがデフォルトで有効になっています。

    gcloud container clusters create-auto neg-demo-cluster \
        --location=COMPUTE_LOCATION
    

    COMPUTE_LOCATION は、クラスタの Compute Engine のロケーションに置き換えます。

  • Standard モードの場合は、クラスタの作成時にエイリアス IP アドレスを有効にします。

    gcloud container clusters create neg-demo-cluster \
        --enable-ip-alias \
        --create-subnetwork="" \
        --network=default \
        --zone=us-central1-a
    

Deployment を作成する

次の Deployment のサンプル(neg-demo-app)では、コンテナ化された HTTP サーバーの単一インスタンスが実行されます。Pod の readiness フィードバックを使用するワークロードを使用することをおすすめします。

Pod の readiness フィードバックを使用する

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
        ports:
        - containerPort: 9376
          protocol: TCP
  

ハードコードされた遅延を使用する

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: neg-demo-app # Label for the Deployment
  name: neg-demo-app # Name of Deployment
spec:
  minReadySeconds: 60 # Number of seconds to wait after a Pod is created and its status is Ready
  selector:
    matchLabels:
      run: neg-demo-app
  template: # Pod template
    metadata:
      labels:
        run: neg-demo-app # Labels Pods from this Deployment
    spec: # Pod specification; each Pod created by this Deployment has this specification
      containers:
      - image: registry.k8s.io/serve_hostname:v1.4 # Application to run in Deployment's Pods
        name: hostname # Container name
      # Note: The following line is necessary only on clusters running GKE v1.11 and lower.
      # For details, see https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing#align_rollouts
        ports:
        - containerPort: 9376
          protocol: TCP
      terminationGracePeriodSeconds: 60 # Number of seconds to wait for connections to terminate before shutting down Pods
  

この Deployment では、各コンテナで HTTP サーバーが実行されます。HTTP サーバーは、アプリケーション サーバーのホスト名(サーバーが実行されている Pod の名前)をレスポンスとして返します。

このマニフェストを neg-demo-app.yaml として保存し、Deployment を作成します。

kubectl apply -f neg-demo-app.yaml

コンテナ ネイティブのロードバランサに Service を作成する

Deployment を作成したら、Pod を Service にグループ化する必要があります。

次のサンプルの Service neg-demo-svc は、前のセクションで作成したサンプルの Deployment をターゲットにしています。

apiVersion: v1
kind: Service
metadata:
  name: neg-demo-svc # Name of Service
  annotations:
    cloud.google.com/neg: '{"ingress": true}' # Creates a NEG after an Ingress is created
spec: # Service's specification
  type: ClusterIP
  selector:
    run: neg-demo-app # Selects Pods labelled run: neg-demo-app
  ports:
  - name: http
    port: 80 # Service's port
    protocol: TCP
    targetPort: 9376

Service のアノテーション cloud.google.com/neg: '{"ingress": true}' によってコンテナネイティブのロード バランシングが有効化されます。ただし、ロードバランサは Service の Ingress を作成するまで作成されません。

このマニフェストを neg-demo-svc.yaml として保存し、Service を作成します。

kubectl apply -f neg-demo-svc.yaml

Service の Ingress を作成する

次の Ingress のサンプル(neg-demo-ing)では、作成した Service をターゲットとしています。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  defaultBackend:
    service:
      name: neg-demo-svc # Name of the Service targeted by the Ingress
      port:
        number: 80 # Should match the port used by the Service

このマニフェストを neg-demo-ing.yaml として保存し、Ingress を作成します。

kubectl apply -f neg-demo-ing.yaml

Ingress を作成すると、プロジェクト内にアプリケーション ロードバランサが作成され、クラスタが実行される各ゾーンにネットワーク エンドポイント グループ(NEG)が作成されます。NEG 内のエンドポイントと Service のエンドポイントは同期の状態が維持されます。

Ingress を確認する

ワークロードをデプロイし、Pod を Service にグループ化して Service の Ingress を作成したら、Ingress によってコンテナ ネイティブのロードバランサが正常にプロビジョニングされていることを確認します。

Ingress のステータスを取得します。

kubectl describe ingress neg-demo-ing

出力には、ADD イベントと CREATE イベントが含まれます。

Events:
Type     Reason   Age                From                     Message
----     ------   ----               ----                     -------
Normal   ADD      16m                loadbalancer-controller  default/neg-demo-ing
Normal   Service  4s                 loadbalancer-controller  default backend set to neg-demo-svc:32524
Normal   CREATE   2s                 loadbalancer-controller  ip: 192.0.2.0

ロードバランサをテストする

以降のセクションでは、コンテナ ネイティブのロードバランサの機能をテストする方法について説明します。

Ingress の IP アドレスにアクセスする

アプリケーション ロードバランサが構成されるまで数分待ちます。

Ingress の IP アドレスにアクセスすることで、コンテナ ネイティブのロードバランサが機能していることを確認できます。

Ingress の IP アドレスを取得するには、次のコマンドを実行します。

kubectl get ingress neg-demo-ing

コマンド出力の ADDRESS 列に、Ingress の IP アドレスが表示されます。この IP アドレスに、ウェブブラウザでアクセスします。

バックエンド サービスのヘルス ステータスをチェックする

ロードバランサのバックエンド サービスのヘルス ステータスを取得することもできます。

  1. プロジェクトで実行されているバックエンド サービスのリストを取得します。

    gcloud compute backend-services list
    

    Service の名前を含むバックエンド サービスの名前(neg-demo-svc など)を記録します。

  2. バックエンド サービスの稼働状況を取得します。

    gcloud compute backend-services get-health BACKEND_SERVICE_NAME --global
    

    BACKEND_SERVICE_NAME は、バックエンド サービスの名前に置き換えます。

Ingress をテストする

ロードバランサが期待どおりに機能しているかどうかをテストするもう 1 つの方法は、サンプルの Deployment をスケーリングし、テスト リクエストを Ingress に送信して、正しい数のレプリカが応答するかどうかを確認することです。

  1. neg-demo-app Deployment を 1 つのインスタンスから 2 つのインスタンスにスケーリングします。

    kubectl scale deployment neg-demo-app --replicas 2
    

    このコマンドの完了までに数分かかることがあります。

  2. ロールアウトが完了したことを確認します。

    kubectl get deployment neg-demo-app
    

    出力には、使用可能な 2 つのレプリカが含まれます。

    NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    neg-demo-app   2         2         2            2           26m
    
  3. Ingress の IP アドレスを取得します。

    kubectl describe ingress neg-demo-ing
    

    このコマンドで 404 エラーが返された場合は、ロードバランサが起動するまで数分待ってからもう一度お試しください。

  4. ロードバランサからの個別のレスポンスの数をカウントします。

    for i in `seq 1 100`; do \
      curl --connect-timeout 1 -s IP_ADDRESS && echo; \
    done  | sort | uniq -c
    

    IP_ADDRESS は、Ingress の IP アドレスに置き換えます。

    出力は次のようになります。

    44 neg-demo-app-7f7dfd7bc6-dcn95
    56 neg-demo-app-7f7dfd7bc6-jrmzf
    

    この出力では、個別のレスポンスの数はレプリカの数と同じです。これは、すべてのバックエンド Pod がトラフィックを処理していることを示しています。

クリーンアップ

このページのタスクを完了したら、アカウントで不要な請求が発生しないように、以下の手順でリソースを削除します。

クラスタの削除

gcloud

gcloud container clusters delete neg-demo-cluster

コンソール

  1. Google Cloud コンソールで Google Kubernetes Engine のページに移動します。

    Google Kubernetes Engine に移動

  2. neg-demo-cluster を選択し、 [削除] をクリックします。

  3. 確認のメッセージが表示されたら、[削除] をクリックします。

トラブルシューティング

ネットワーク構成を確認する方法は次のとおりです。以降のセクションでは、コンテナ ネイティブのロード バランシングに関連する特定の問題の解決方法について説明します。

  • ネットワーク エンドポイント グループを一覧取得する方法については、ロード バランシングのドキュメントをご覧ください。

  • サービスの neg-status アノテーションのサービスに対応する NEG の名前とゾーンを確認できます。サービスの仕様を取得するには、次のコマンドを実行します。

    kubectl get svc SVC_NAME -o yaml
    

    metadata:annotations:cloud.google.com/neg-status アノテーションには、サービスの対応する NEG の名前と NEG のゾーンが一覧表示されます。

  • NEG に対応するバックエンド サービスのヘルスは、次のコマンドで確認できます。

    gcloud compute backend-services --project PROJECT_NAME \
        get-health BACKEND_SERVICE_NAME --global
    

    バックエンド サービスは NEG と同じ名前です。

  • サービスのイベントログを出力するには、次のコマンドを実行します。

    kubectl describe svc SERVICE_NAME
    

    サービスの名前文字列には、対応する GKE サービスの名前と名前空間が含まれます。

エイリアス IP を使用したクラスタを作成できない

現象

エイリアス IP を使用したクラスタを作成しようとすると、次のようなエラーが発生することがあります。

ResponseError: code=400, message=IP aliases cannot be used with a legacy network.
考えられる原因

作成しようとしているエイリアス IP 付きのクラスタでレガシー ネットワークも使用している場合、このエラーが発生します。

解決策

エイリアス IP と以前のネットワークを同時に有効にしたクラスタを作成しないでください。エイリアス IP の使用方法については、VPC ネイティブ クラスタを作成するをご覧ください。

トラフィックがエンドポイントに到達しない

現象
502 / 503 エラーが発生するか、接続が拒否されます。
考えられる原因

新しいエンドポイントは、通常ロードバランサへの接続後に到達可能になりますが、そのためにはヘルスチェックにレスポンスを返す必要があります。トラフィックがエンドポイントに到達しない場合、502 エラーが発生することがあります。また、接続が拒否されることもあります。

502 エラーと接続の拒否は SIGTERM を処理しないコンテナが原因である可能性もあります。コンテナが SIGTERM を明示的に処理しない場合は、直ちに終了し、リクエストの処理を停止します。ロードバランサは、終了したコンテナに受信トラフィックを送信し続け、エラーにつながります。

コンテナ ネイティブのロードバランサには、バックエンドのエンドポイントが 1 つだけ含まれます。ローリング アップデート中は、新しいエンドポイントがプログラムされる前に古いエンドポイントがプログラム解除されます。

コンテナ ネイティブのロードバランサのプロビジョニング後、バックエンド Pod が初めて新しいゾーンにデプロイされます。ゾーンに 1 つ以上のエンドポイントがある場合、ロードバランサのインフラストラクチャはゾーンでプログラムされます。ゾーンに新しいエンドポイントが追加されると、ロードバランサ インフラストラクチャがプログラムされ、サービスの中断が発生します。

解決策

コンテナを構成して SIGTERM を処理し、終了猶予期間中はリクエストへの応答を続行します。デフォルトでは 30 秒です。SIGTERM を受信したときにヘルスチェックが失敗するように Pod を構成します。これにより、エンドポイントのデプログラミング中に、Pod へのトラフィックの送信を停止するようにロードバランサに通知します。

エンドポイントのプログラム解除中に SIGTERM を受け取ったとき、アプリケーションが正常にシャットダウンせず、リクエストへの応答を停止している場合は、preStop フックを使用して SIGTERM を処理し、トラフィックの処理を継続できます。

lifecycle:
  preStop:
    exec:
      # if SIGTERM triggers a quick exit; keep serving traffic instead
      command: ["sleep","60"]

Pod の終了に関するドキュメントをご覧ください。

ロードバランサのバックエンドにインスタンスが 1 つしかない場合は、新しいインスタンスが完全にプログラムされる前にそのインスタンスを破棄しないようにロールアウト手順を構成してください。Deployment ワークロードが管理するアプリケーション Pod では、maxUnavailable パラメータが 0 に設定されているロールアウト手順を構成することでこれを実現できます。

strategy:
  rollingUpdate:
    maxSurge: 1
    maxUnavailable: 0

エンドポイントに到達しないトラフィックをトラブルシューティングするには、ファイアウォール ルールで 130.211.0.0/2235.191.0.0/16 の範囲のエンドポイントへの受信 TCP トラフィックが許可されていることを確認します。詳細については、Cloud Load Balancing ドキュメントのヘルスチェックの追加をご覧ください。

プロジェクトのバックエンド サービスを確認します。関連するバックエンド サービスの名前文字列には、対応する GKE サービスの名前と名前空間が含まれます。

gcloud compute backend-services list

バックエンド サービスからバックエンドのヘルス ステータスを取得します。

gcloud compute backend-services get-health BACKEND_SERVICE_NAME

すべてのバックエンドでヘルス ステータスが異常な場合は、ファイアウォール、Ingress、Service のいずれかの構成が不適切である可能性があります。

一部のバックエンドで短期間ヘルス ステータスが異常な場合は、ネットワーク プログラミングのレイテンシが原因である可能性があります。

一部のバックエンドがバックエンド サービスのリストに表示されない場合は、プログラミングのレイテンシが原因である可能性があります。これは、次のコマンドを実行して確認できます。ここで、NEG_NAME はバックエンド サービスの名前です(NEG とバックエンド サービスは同じ名前を共有します)。

gcloud compute network-endpoint-groups list-network-endpoints NEG_NAME

予期されるすべてのエンドポイントが NEG に存在することを確認します。

コンテナ ネイティブのロードバランサによって少数のバックエンド(たとえば 1 つの Pod)が選択される場合、レプリカの数を増やし、GKE クラスタがまたがるすべてのゾーンにバックエンド Pod を分散することを検討してください。これにより、基盤となるロードバランサのインフラストラクチャが完全にプログラムされます。それ以外の場合は、バックエンド Pod を単一のゾーンに制限することを検討してください。

エンドポイントのネットワーク ポリシーを構成する場合は、プロキシ専用サブネットからの上り(内向き)が許可されていることを確認します。

停滞時のロールアウト

現象
更新した Deployment がロールアウトし、最新のレプリカの数が目的のレプリカの数と一致しません。
考えられる原因

デプロイのヘルスチェックが失敗しています。コンテナ イメージが不適切であるか、ヘルスチェックが正しく構成されていない可能性があります。Pod のローリング置換は、新しく開始した Pod が Pod の readiness ゲートを通過してから行います。これは、Pod がロードバランサのヘルスチェックに応答している場合にのみ発生します。Pod が応答しない場合、またはヘルスチェックが正しく構成されていない場合は、readiness ゲートの条件を満たせず、ロールアウトを続行できません。

kubectl 1.13 以降を使用している場合は、次のコマンドで Pod の readiness ゲートのステータスを確認できます。

kubectl get pod POD_NAME -o wide

READINESS GATES 列を確認します。

この列は、kubectl 1.12 以下には存在しません。READY 状態であるとマークされた Pod は、readiness ゲートに失敗した可能性があります。これを確認するには、次のコマンドを使用します。

kubectl get pod POD_NAME -o yaml

readiness ゲートとそのステータスが出力に一覧表示されます。

解決策

Deployment の Pod の仕様のコンテナ イメージが正しく機能し、ヘルスチェックに応答できることを確認します。ヘルスチェックが正しく構成されていることを確認します。

デグレード モードのエラー

現象

GKE バージョン 1.29.2-gke.1643000 以降では、NEG が更新されると、ログ エクスプローラでサービスに関して次の警告が表示されることがあります。

Entering degraded mode for NEG <service-namespace>/<service-name>-<neg-name>... due to sync err: endpoint has missing nodeName field
考えられる原因

これらの警告は、EndpointSlice オブジェクトに基づく NEG の更新中に GKE がエンドポイントの構成ミスを検出し、より詳細な計算プロセス(デグレード モード)をトリガーしたことを示します。GKE は、構成の誤りを修正するか、無効なエンドポイントを NEG の更新から除外することで、引き続きベスト エフォート方式で NEG を更新します。

よくあるエラーには、次のようなものがあります。

  • endpoint has missing pod/nodeName field
  • endpoint corresponds to an non-existing pod/node
  • endpoint information for attach/detach operation is incorrect
解決策

通常、これは一時的な状態が原因で発生するイベントであり、自動的に修正されます。ただし、カスタム EndpointSlice オブジェクトの誤った構成によってイベントが発生した場合は解決されません。構成の誤りを確認するには、サービスに対応する EndpointSlice オブジェクトを調べます。

kubectl get endpointslice -l kubernetes.io/service-name=<service-name>

イベントのエラーに基づいてエンドポイントを検証します。

この問題を解決するには、EndpointSlice オブジェクトを手動で変更する必要があります。更新によって NEG がトリガーされ、再度更新されます。誤った構成がなくなると、出力は次のようになります。

NEG <service-namespace>/<service-name>-<neg-name>... is no longer in degraded mode

既知の問題

GKE のコンテナ ネイティブのロード バランシングには、次のような既知の問題があります。

不完全なガベージ コレクション

GKE のガベージ コレクションは、コンテナ ネイティブのロードバランサを 2 分ごとに収集します。ロードバランサが完全に削除される前にクラスタが削除された場合は、ロードバランサの NEG を手動で削除する必要があります。

次のコマンドを実行して、プロジェクト内の NEG を表示します。

gcloud compute network-endpoint-groups list

コマンド出力で、関連する NEG を探します。

NEG を削除するには、次のコマンドを実行します。NEG_NAME は NEG の名前に置き換えます。

gcloud compute network-endpoint-groups delete NEG_NAME

ワークロードのロールアウトとエンドポイントの伝播を調整する

ワークロードをクラスタにデプロイするときや、既存のワークロードを更新するときに、コンテナ ネイティブのロードバランサでは、ワークロードのロールアウトの完了に要する時間よりも、新しいエンドポイントの伝播に要する時間のほうが長くなる場合があります。このガイドでデプロイするサンプルの Deployment では、ロールアウトとエンドポイントの伝播の調整に terminationGracePeriodSecondsminReadySeconds という 2 つのフィールドを使用しています。

terminationGracePeriodSeconds を指定すると、Pod の削除がスケジュール設定された後に接続が終了するまで待ってから、Pod を正常にシャットダウンできます。

minReadySeconds では、Pod が作成された後のレイテンシ期間を追加します。新しい Pod がコンテナのクラッシュを発生させることなく Ready ステータスになるまでの最短秒数を指定します。この期間が経過すると Pod は使用可能と見なされます。

ワークフローのロールアウトでサービスが中断しないように、ワークロードの minReadySecondsterminationGracePeriodSeconds の値は 60 秒以上に構成する必要があります。

terminationGracePeriodSeconds はすべての Pod 仕様で、minReadySeconds は Deployment と DaemonSets で使用できます。

ロールアウトを微調整する方法について詳しくは、RollingUpdateStrategy をご覧ください。

Pod readinessProbeinitialDelaySeconds が認識されない

コンテナ ネイティブのロードバランサによって Pod の readinessProbeinitialDelaySeconds 構成が認識されると思われるかもしれませんが、readinessProbe は kubelet によって実装され、initialDelaySeconds 構成はコンテナ ネイティブのロードバランサではなく kubelet のヘルスチェックを制御します。コンテナ ネイティブのロード バランシングには、独自のヘルスチェックがあります。

次のステップ