Ingress によるコンテナ ネイティブの負荷分散

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

コンテナ ネイティブの負荷分散のメリット、要件、制限事項については、コンテナ ネイティブの負荷分散をご覧ください。

コンテナ ネイティブの負荷分散を使用する

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

VPC ネイティブ クラスタの作成

コンテナ ネイティブの負荷分散を使用するには、有効なエイリアス IP を使用したクラスタを作成する必要があります。

たとえば、次のコマンドではゾーン us-central1-a でサブネットワークが自動プロビジョニングされるクラスタ neg-demo-cluster が作成されます。

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

Deployment を作成する

次に、クラスタにワークロードをデプロイします。

次の Deployment のサンプル、neg-demo-app では、コンテナ化された HTTP サーバーの単一インスタンスが実行されます。お使いの GKE のバージョンで可能な場合は、Pod の readiness フィードバックを使うワークロードの使用をおすすめします。GKE バージョンの要件の詳細については、Pod の readiness をご覧ください。クラスタを更新して、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: k8s.gcr.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: k8s.gcr.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:
  - 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/v1beta1
kind: Ingress
metadata:
  name: neg-demo-ing
spec:
  backend:
    serviceName: neg-demo-svc # Name of the Service targeted by the Ingress
    servicePort: 80 # Should match the port used by the Service

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

kubectl apply -f neg-demo-ing.yaml

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

Ingress を確認する

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

Ingress のステータスを取得するには、次のコマンドを実行します。

kubectl describe ingress neg-demo-ing

コマンド出力で、ADDCREATE のイベントを探します。

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 アドレスにアクセスする

HTTP(S) ロードバランサが構成されるまで数分待ちます。

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

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

kubectl get ingress neg-demo-ing

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

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

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

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

gcloud compute backend-services list

Service の名前を含むバックエンドの名前(neg-demo-svc など)をコピーします。その後、次のようにしてバックエンド サービスのヘルス ステータスを取得します。

gcloud compute backend-services get-health backend-service-name --global

Ingress の機能を確認する

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

次のコマンドは、neg-demo-app Deployment を 1 つのインスタンスから 2 つのインスタンスにスケーリングします。

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

ロールアウトが完了するまで数分待ちます。ロールアウトの完了を確認するには、次のコマンドを実行します。

kubectl get deployment neg-demo-app

コマンド出力で、使用可能なレプリカが 2 つあることを確認します。

NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
neg-demo-app   2         2         2            2           26m

さらに、次のコマンドを実行して、ロードバランサからの個別のレスポンスの数をカウントします。

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

ip-address は Ingress の IP アドレスです。Ingress の IP アドレスは kubectl describe ingress neg-demo-ing から取得できます。

このコマンド出力で、個別のレスポンスの数がレプリカと同数である、すなわちすべてのバックエンド Pod でトラフィックが処理されていることが示されているか確認します。

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

クリーンアップ

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

クラスタの削除

gcloud

gcloud container clusters delete neg-demo-cluster

Console

  1. Cloud Console で 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 の詳しい使用方法については、エイリアス IP を使用した VPC ネイティブ クラスタの作成をご覧ください。

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

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

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

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

解決策

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

詳細については、Pod 終了に関するドキュメントPod 終了のベスト プラクティスに関する投稿をご覧ください。

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

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

gcloud compute backend-services list

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

gcloud compute backend-services get-health backend-service-name

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

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

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

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

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

停滞時のロールアウト

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

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

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

kubectl get my-pod -o wide

READINESS GATES 列を確認します。

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

kubectl get my-pod -o yaml

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

解決策

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

既知の問題

Google Kubernetes Engine のコンテナ ネイティブの負荷分散には、次のような既知の問題があります。

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

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

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

gcloud compute network-endpoint-groups list

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

NEG を削除するには、次のコマンドを実行します。ここで、neg は NEG の名前です。

gcloud compute network-endpoint-groups delete neg

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

ワークロードをクラスタにデプロイするときや、既存のワークロードを更新するときに、コンテナ ネイティブのロードバランサでは、ワークロードのロールアウトの完了に要する時間よりも、新しいエンドポイントの伝播に要する時間のほうが長くなる場合があります。このガイドでデプロイするサンプルの 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 のヘルスチェックを制御します。コンテナ ネイティブの負荷分散には、独自の負荷分散ヘルスチェックがあります。

次のステップ