このドキュメントでは、2 つの GKE クラスタ間でサンプル store アプリケーションの Blue/Green デプロイを行う方法について説明します。Blue/Green デプロイは、リスクを最小限に抑えながらアプリケーションを新しい GKE クラスタに移行するための効果的な戦略です。トラフィックを現在のクラスタ(Blue)から新しいクラスタ(Green)に段階的に移行することで、完全な切り替えを行う前に、本番環境で新しい環境を検証できます。
このチュートリアルでは、サンプル store アプリケーションを使用して、オンライン ショッピング サービスを複数のチームが所有して管理し、共有 GKE クラスタのフリート全体にデプロイしている実際のシナリオをシミュレートします。
始める前に
マルチクラスタ Gateway をデプロイする前に環境を用意する必要があります。続行する前に、マルチクラスタ Gateway 用に環境を準備するの手順を行ってください。
GKE クラスタをデプロイします。
クラスタをフリートに登録します(まだ登録されていない場合)。
マルチクラスタ Service コントローラとマルチクラスタ Gateway コントローラを有効にします。
最後に、お使いの環境でコントローラを使用する前に、GKE Gateway コントローラの制限事項と既知の問題をご確認ください。
Gateway による Blue/Green マルチクラスタ ルーティング
gke-l7-global-external-managed-*、gke-l7-regional-external-managed-*、gke-l7-rilb-* の GatewayClass では、トラフィック分割、ヘッダー マッチング、ヘッダー操作、トラフィック ミラーリングなど、多くの高度なトラフィック ルーティング機能を使用できます。この例では、重み付けに基づいてトラフィック分割を行い、2 つの GKE クラスタ間のトラフィックの比率を明示的に制御しています。
この例では、サービス オーナーがアプリケーションを新しい GKE クラスタに移動または拡張する際に行う具体的な手順を説明します。Blue/Green デプロイの目標は、新しいクラスタが正しく動作していることを確認する複数の検証手順を行い、リスクを減らすことです。この例では、デプロイの 4 つのステージについて説明します。
- 100% - ヘッダーに基づくカナリア: HTTP ヘッダーのルーティングを使用して、テストまたは合成トラフィックのみを新しいクラスタに送信します。
- 100% - トラフィックのミラーリング: カナリア クラスタへのユーザー トラフィックをミラーリングします。ユーザー トラフィックの 100% がこのクラスタにコピーされ、カナリア クラスタの容量がテストされます。
- 90%~10%: 10% のカナリア トラフィック分割で新しいクラスタをライブ トラフィックにゆっくり公開します。
- 0%~100%: エラーが観察された場合は、元に戻すオプションを使用して、新しいクラスタに完全にカットオーバーします。
この例は前のものと似ていますが、内部マルチクラスタ Gateway をデプロイしている点が異なります。この例では、VPC 内からのみプライベートでのアクセスが可能な内部アプリケーション ロードバランサをデプロイしています。別の Gateway を介してデプロイする場合を除き、前の手順でデプロイしたクラスタとアプリケーションを使用します。
前提条件
次の例では、外部マルチクラスタ ゲートウェイのデプロイの一部の手順を使用しています。この例を続行する前に、次のことが完了していることを確認してください。
-
この例では、設定済みの
gke-west-1クラスタとgke-west-2クラスタを使用します。gke-l7-rilb-mcGatewayClass はリージョンであり、同じリージョン内のクラスタ バックエンドのみをサポートしているため、これらのクラスタは同じリージョンにあります。 各クラスタに必要な Service と ServiceExport をデプロイします。前の例で Service と ServiceExports をデプロイした場合は、その一部がデプロイされています。
kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store-west-1-service.yaml kubectl apply --context gke-west-2 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store-west-2-service.yaml各クラスタに同様のリソースセットをデプロイします。
service/store created serviceexport.net.gke.io/store created service/store-west-2 created serviceexport.net.gke.io/store-west-2 created
プロキシ専用サブネットの構成
内部 Gateway をデプロイしているリージョンごとに、プロキシ専用サブネットを設定します(まだ設定していない場合)。このサブネットは、ロードバランサのプロキシに内部 IP アドレスを提供するために使用されます。--purpose を REGIONAL_MANAGED_PROXY にのみ指定して設定する必要があります。
内部アプリケーション ロードバランサを管理する Gateway を作成する前に、プロキシ専用サブネットを作成する必要があります。内部アプリケーション ロードバランサを使用する Virtual Private Cloud(VPC)ネットワークの各リージョンに、プロキシ専用サブネットを作成する必要があります。
プロキシ専用サブネットを作成するには、gcloud compute networks subnets create コマンドを使用します。
gcloud compute networks subnets create SUBNET_NAME \
--purpose=REGIONAL_MANAGED_PROXY \
--role=ACTIVE \
--region=REGION \
--network=VPC_NETWORK_NAME \
--range=CIDR_RANGE
次のように置き換えます。
SUBNET_NAME: プロキシ専用サブネットの名前。REGION: プロキシ専用サブネットのリージョン。VPC_NETWORK_NAME: サブネットを含む VPC ネットワークの名前。CIDR_RANGE: サブネットのプライマリ IP アドレス範囲。サブネット マスクの長さは/26以下にして、リージョン内のプロキシで 64 個以上の IP アドレスを使用できるようにします。推奨のサブネット マスクは/23です。
Gateway のデプロイ
次の Gateway は gke-l7-rilb-mc GatewayClass から作成されます。これは、同じリージョン内の GKE クラスタのみをターゲットにできるリージョン内部 Gateway です。
次の
Gatewayマニフェストを構成クラスタに適用します(この例ではgke-west-1)。cat << EOF | kubectl apply --context gke-west-1 -f - kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-http namespace: store spec: gatewayClassName: gke-l7-rilb-mc listeners: - name: http protocol: HTTP port: 80 allowedRoutes: kinds: - kind: HTTPRoute EOFGateway が正常に起動したことを確認します。次のコマンドを使用して、この Gateway のイベントのみを表示します。
kubectl get events --field-selector involvedObject.kind=Gateway,involvedObject.name=internal-http --context=gke-west-1 --namespace store次のような出力の場合は、Gateway のデプロイに成功しています。
LAST SEEN TYPE REASON OBJECT MESSAGE 5m18s Normal ADD gateway/internal-http store/internal-http 3m44s Normal UPDATE gateway/internal-http store/internal-http 3m9s Normal SYNC gateway/internal-http SYNC on store/internal-http was a success
ヘッダーに基づくカナリア
ヘッダーに基づくカナリアでは、サービス オーナーは実際のユーザー以外からの合成テスト トラフィックを照合できます。これは、ユーザーを直接公開することなく、アプリケーションの基本ネットワークが機能していることを簡単に検証できる方法です。
次の
HTTPRouteマニフェストを構成クラスタに適用します(この例ではgke-west-1)。cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: # Matches for env=canary and sends it to store-west-2 ServiceImport - matches: - headers: - name: env value: canary backendRefs: - group: net.gke.io kind: ServiceImport name: store-west-2 port: 8080 # All other traffic goes to store-west-1 ServiceImport - backendRefs: - group: net.gke.io kind: ServiceImport name: store-west-1 port: 8080 EOFデプロイされると、この HTTPRoute は次のルーティング動作を構成します。
env: canaryHTTP ヘッダーを使用しないstore.example.internalへの内部リクエストは、gke-west-1クラスタのstorePod に転送されます。env: canaryHTTP ヘッダーを使用するstore.example.internalへの内部リクエストは、gke-west-2クラスタのstorePod に転送されます。
Gateway IP アドレスにトラフィックを送信し、HTTPRoute が正しく機能していることを確認します。
internal-httpから内部 IP アドレスを取得します。kubectl get gateways.gateway.networking.k8s.io internal-http -o=jsonpath="{.status.addresses[0].value}" --context gke-west-1 --namespace store次の手順の VIP は、出力として受け取る IP アドレスに置き換えます。
env: canaryHTTP ヘッダーを使用して、Gateway にリクエストを送信します。これにより、トラフィックがgke-west-2に転送されていることを確認します。GKE クラスタと同じ VPC のプライベート クライアントを使用して、リクエストが正しく転送されていることを確認します。次のコマンドは、Gateway IP アドレスへのプライベート アクセスがあるマシンで実行する必要があります。そうしないと、機能しません。curl -H "host: store.example.internal" -H "env: canary" http://VIPこの出力で、リクエストが
gke-west-2クラスタから Pod によって処理されたことを確認できます。{ "cluster_name": "gke-west-2", "host_header": "store.example.internal", "node_name": "gke-gke-west-2-default-pool-4cde1f72-m82p.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-9kdb5", "pod_name_emoji": "😂", "project_id": "agmsb-k8s", "timestamp": "2021-05-31T01:21:55", "zone": "us-west1-a" }
トラフィック ミラーリング
このステージでは、トラフィックを目的のクラスタに送信するだけでなく、カナリア クラスタにトラフィックをミラーリングします。
ミラーリングを使用すると、クライアントへのレスポンスに影響を与えることなく、トラフィックの負荷がアプリケーションのパフォーマンスに与える影響を判断するのに役立ちます。すべての種類のロールアウトで必要になるとは限りませんが、パフォーマンスや負荷に影響を及ぼす可能性のある大規模な変更をロールアウトする場合に役立ちます。
次の
HTTPRouteマニフェストを構成クラスタに適用します(この例ではgke-west-1)。cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: # Sends all traffic to store-west-1 ServiceImport - backendRefs: - name: store-west-1 group: net.gke.io kind: ServiceImport port: 8080 # Also mirrors all traffic to store-west-2 ServiceImport filters: - type: RequestMirror requestMirror: backendRef: group: net.gke.io kind: ServiceImport name: store-west-2 port: 8080 EOFプライベート クライアントを使用して、
internal-httpGateway にリクエストを送信します。/mirrorパスを使用して、後の手順でアプリケーション ログを確認する際にこのリクエストを一意に識別できるようにします。curl -H "host: store.example.internal" http://VIP/mirrorこの出力で、クライアントが
gke-west-1クラスタの Pod からレスポンスを受信していることを確認します。{ "cluster_name": "gke-west-1", "host_header": "store.example.internal", "node_name": "gke-gke-west-1-default-pool-65059399-ssfq.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-brg5w", "pod_name_emoji": "🎖", "project_id": "agmsb-k8s", "timestamp": "2021-05-31T01:24:51", "zone": "us-west1-a" }これにより、プライマリ クラスタがトラフィックに応答していることを確認します。また、移行先のクラスタがミラーリング対象トラフィックを受信していることも確認する必要があります。
gke-west-2クラスタのstorePod のアプリケーション ログを確認します。このログは、Pod がロードバランサからミラーリングされたトラフィックを受け取ったことを確認するものです。kubectl logs deployment/store --context gke-west-2 -n store | grep /mirrorこの出力では、
gke-west-2クラスタの Pod も同じリクエストを受信していますが、これらのリクエストに対するレスポンスがクライアントに送信されていないことを確認できます。ログに記録された IP アドレスは、Pod と通信しているロードバランサの内部 IP アドレスになります。Found 2 pods, using pod/store-5c65bdf74f-vpqbs [2023-10-12 21:05:20,805] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:20] "GET /mirror HTTP/1.1" 200 - [2023-10-12 21:05:27,158] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:27] "GET /mirror HTTP/1.1" 200 - [2023-10-12 21:05:27,805] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:27] "GET /mirror HTTP/1.1" 200 -
トラフィック分割
トラフィック分割は、新しいコードをロールアウトするか、新しい環境に安全にデプロイする最も一般的な方法の 1 つです。サービス オーナーは、カナリア バックエンドに送信されるトラフィックの明示的な割合を設定します。これは通常、トラフィック全体に対してかなり少ない量になります。実際のユーザー リクエストに対して許容可能なリスク量も判断できます。
ごく少量のトラフィックでトラフィック分割を行うと、サービス オーナーはアプリケーションの状態とレスポンスを検査できます。すべてのシグナルが正常と思われる場合は、カットオーバーに進みます。
次の
HTTPRouteマニフェストを構成クラスタに適用します(この例ではgke-west-1)。cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: - backendRefs: # 90% of traffic to store-west-1 ServiceImport - name: store-west-1 group: net.gke.io kind: ServiceImport port: 8080 weight: 90 # 10% of traffic to store-west-2 ServiceImport - name: store-west-2 group: net.gke.io kind: ServiceImport port: 8080 weight: 10 EOFプライベート クライアントを使用して、継続的 curl リクエストを
internal- httpGateway に送信します。while true; do curl -H "host: store.example.internal" -s VIP | grep "cluster_name"; sleep 1; done出力は次のようになります。90 / 10 のトラフィック分割が発生していることがわかります。
"cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-2", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", ...
トラフィックのカットオーバー
Blue/Green 移行の最終ステージでは、新しいクラスタへの完全なカットオーバーを行い、古いクラスタを削除します。サービス オーナーが 2 つ目のクラスタを既存のクラスタにオンボーディングした場合、最後のステップは異なります。最後のステップでは、両方のクラスタにトラフィックが送信されます。そのシナリオでは、gke-west-1 クラスタと gke-west-2 クラスタの両方の Pod を持つ単一の store ServiceImport をおすすめします。これにより、ロードバランサは、近接性、健全性、容量に基づいて、アクティブ / アクティブなアプリケーションのトラフィックの送信先を決定できます。
次の
HTTPRouteマニフェストを構成クラスタに適用します(この例ではgke-west-1)。cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: - backendRefs: # No traffic to the store-west-1 ServiceImport - name: store-west-1 group: net.gke.io kind: ServiceImport port: 8080 weight: 0 # All traffic to the store-west-2 ServiceImport - name: store-west-2 group: net.gke.io kind: ServiceImport port: 8080 weight: 100 EOFプライベート クライアントを使用して、継続的 curl リクエストを
internal- httpGateway に送信します。while true; do curl -H "host: store.example.internal" -s VIP | grep "cluster_name"; sleep 1; done出力は次のようになります。すべてのトラフィックが
gke-west-2に送信されるようになります。"cluster_name": "gke-west-2", "cluster_name": "gke-west-2", "cluster_name": "gke-west-2", "cluster_name": "gke-west-2", ...
この最後のステップで、1 つの GKE クラスタから別の GKE クラスタへの Blue/Green アプリケーションを完全に移行します。
クリーンアップ
このドキュメントの演習を完了したら、アカウントで不要な請求が発生しないように、以下の手順でリソースを削除します。
他の目的で登録する必要がない場合は、フリートからクラスタの登録を解除します。
multiclusterservicediscovery機能を無効にします。gcloud container fleet multi-cluster-services disableマルチクラスタ Ingress を無効にします。
gcloud container fleet ingress disableAPI を無効にします。
gcloud services disable \ multiclusterservicediscovery.googleapis.com \ multiclusteringress.googleapis.com \ trafficdirector.googleapis.com \ --project=PROJECT_ID
トラブルシューティング
正常なアップストリームがない
症状:
Gateway を作成してもバックエンド サービスにアクセスできない場合(503 レスポンス コード)、次の問題が発生している可能性があります。
no healthy upstream
理由:
このエラー メッセージは、ヘルスチェック プローバーが正常なバックエンド サービスを見つけられないことを示します。バックエンド サービスは正常である可能性がありますが、ヘルスチェックのカスタマイズが必要になる場合もあります。
回避策:
この問題を解決するには、HealthCheckPolicy を使用して、アプリケーションの要件(/health など)に基づいてヘルスチェックをカスタマイズします。
次のステップ
- Gateway コントローラの詳細を確認する。