このページでは、複数の Google Kubernetes Engine(GKE)クラスタ(またはフリート)間で上り(内向き)トラフィックのロード バランシングを行う Kubernetes Gateway リソースをデプロイする方法について説明します。マルチクラスタ Gateway をデプロイする前に、マルチクラスタ Gateway の有効化を参照して環境を準備してください。
上り(内向き)トラフィックのロード バランシングを行う Gateway を単一の GKE クラスタにデプロイする場合は、Gateway のデプロイをご覧ください。
マルチクラスタ Gateway
マルチクラスタ Gateway は、複数の Kubernetes クラスタ間でトラフィックのロード バランシングを行う Gateway リソースです。GKE では、gke-l7-cross-regional-internal-managed-mc、gke-l7-global-external-managed-mc、gke-l7-regional-external-managed-mc、gke-l7-rilb-mc、gke-l7-gxlb-mc の GatewayClass は、さまざまな GKE クラスタ、Kubernetes Namespace、さまざまなリージョンにまたがって、HTTP ルーティング、トラフィック分割、トラフィック ミラーリング、ヘルスチェック ベースのフェイルオーバーなどを実施するマルチクラスタ Gateway をデプロイします。マルチクラスタ Gateway を使用すると、インフラストラクチャ管理者に対して、多数のクラスタ間やチーム間でアプリケーション ネットワーキングを簡単に、安全に、スケーラブルにします。
このページでは、GKE Gateway Controller を使用してマルチクラスタ Gateway をデプロイする方法を示す 3 つの例を紹介します。
- 例 1: インターネット トラフィック用の 2 つの GKE クラスタ間でロード バランシングを行う外部マルチクラスタ Gateway。
- 例 2: 限定公開のレイヤ 7 クロスリージョン Gateway。
- 例 3: 内部 VPC トラフィック用の 2 つの GKE クラスタ間で Blue/Green の重み付けに基づくトラフィック分割とトラフィック ミラーリング。
- 例 4: 最大容量に基づいて異なるバックエンドに対してリクエストのロード バランシングを行う容量ベースの Gateway。
それぞれの例では、同じストアとサイトのアプリケーションを使用して実際のシナリオをモデル化しています。このシナリオでは、別々のチームがオンライン ショッピング サービスとウェブサイト サービスを所有して管理し、これらのサービスを共有 GKE クラスタのフリート全体にデプロイしています。それぞれの例で、マルチクラスタ Gateway によって可能になるトポロジとユースケースについて説明します。
マルチクラスタ Gateway をデプロイする前に環境を用意する必要があります。続行する前に、マルチクラスタ Gateway の有効化の手順を行ってください。
最後に、お使いの環境で使用する前に、GKE Gateway Controller の制限事項と既知の問題をご確認ください。
マルチクラスタ、マルチリージョン、外部 Gateway
このチュートリアルでは、2 つの GKE クラスタのアプリケーション間で外部トラフィックを分散させる外部マルチクラスタ Gateway を作成します。
以下の手順を実施します。
- gke-west-1クラスタと- gke-east-1クラスタにサンプル- storeアプリケーションをデプロイします。
- フリートにエクスポートする各クラスタで Service を設定します(マルチクラスタ Service)。
- 外部のマルチクラスタ Gateway と HTTPRoute を構成クラスタ(gke-west-1)にデプロイします。
アプリケーションと Gateway リソースがデプロイされると、パスベースのルーティングを使用して 2 つの GKE クラスタ間のトラフィックを制御できます。
- /westへのリクエストは、- gke-west-1クラスタ内の- storePod に転送されます。
- /eastへのリクエストは、- gke-east-1クラスタ内の- storePod に転送されます。
- 他のパスへのリクエストは、正常性、容量、リクエスト元クライアントへの近接性に応じて、いずれかのクラスタに転送されます。
デモ アプリケーションのデプロイ
- マルチクラスタ Gateway の有効化でデプロイされた 3 つのクラスタすべてで、 - storeDeployment と Namespace を作成します。- kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.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.yaml kubectl apply --context gke-east-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml- 各クラスタに次のリソースをデプロイします。 - namespace/store created deployment.apps/store created- このページの例はすべて、この手順でデプロイしたアプリを使用します。残りの 3 つの手順を試す前に、アプリが 3 つのクラスタすべてにデプロイされていることを確認してください。この例では、クラスタ - gke-west-1と- gke-east-1のみを使用しています。別の例では- gke-west-2を使用しています。
マルチクラスタ サービス
Service は、Pod をクライアントに公開する方法です。GKE Gateway Controller はコンテナ ネイティブのロード バランシングを使用するため、ClusterIP または Kubernetes のロード バランシングを使用して Pod にアクセスすることはありません。トラフィックはロードバランサから Pod の IP アドレスに直接送信されます。ただし、Service は引き続き、Pod のグループ化の論理 ID として重要な役割を果たします。
マルチクラスタ Service(MCS)はクラスタにまたがる Service の API 標準であり、その GKE コントローラが GKE クラスタ全体にサービス ディスカバリを提供します。マルチクラスタ Gateway コントローラは MCS API リソースを使用して Pod を Service にグループ化し、クラスタ全体または複数のクラスタ間でのアドレス指定を可能にします。
マルチクラスタ Services API は、次のカスタム リソースを定義します。
- ServiceExports は Kubernetes Service にマッピングされ、フリートに登録されているすべてのクラスタに、その Service のエンドポイントをエクスポートします。Service に対応する ServiceExport がある場合、Service はマルチクラスタ Gateway でアドレス指定できます。
- ServiceImports は、マルチクラスタ Service コントローラによって自動的に生成されます。ServiceExport と ServiceImport はペアで提供されます。ServiceExport がフリートに存在する場合、対応する ServiceImport が作成され、ServiceExport にマッピングされた Service にクラスタ間でアクセスできるようになります。
Service のエクスポートの仕組みは次のとおりです。ストア Service は、クラスタ内の Pod のグループを選択する gke-west-1 に存在します。ServiceExport がクラスタに作成され、gke-west-1 の Pod にフリート内の他のクラスタからアクセスできるようになります。ServiceExport は、ServiceExport リソースと同じ名前および Namespace を持つ Service にマッピングされ、公開されます。
apiVersion: v1
kind: Service
metadata:
  name: store
  namespace: store
spec:
  selector:
    app: store
  ports:
  - port: 8080
    targetPort: 8080
---
kind: ServiceExport
apiVersion: net.gke.io/v1
metadata:
  name: store
  namespace: store
次の図に、ServiceExport がデプロイされた後の流れを示します。ServiceExport と Service のペアが存在する場合、マルチクラスタ Service コントローラは、対応する ServiceImport をフリート内のすべての GKE クラスタにデプロイします。ServiceImport は、すべてのクラスタの store Service のローカル表現です。これにより、gke-east-1 の client Pod が ClusterIP またはヘッドレス Service を使用して gke-west-1 の store Pod にアクセスできるようになります。このように使用すると、マルチクラスタ Service はクラスタ間で East-West のロード バランシングを行います。内部 LoadBalancer Service は必要ありません。クラスタ間のロード バランシングにマルチクラスタ Service を使用するには、マルチクラスタ Service の構成をご覧ください。
マルチクラスタ Gateway でも ServiceImport は使用されますが、クラスタ間のロード バランシングでは使用されません。Gateway は、別のクラスタに存在する Service または複数のクラスタにまたがる Service の論理 ID として ServiceImport を使用します。次の HTTPRoute は Service リソースではなく ServiceImport を参照します。ServiceImport を参照することで、1 つ以上のクラスタで実行されているバックエンド Pod のグループにトラフィックが転送されています。
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
  name: store-route
  namespace: store
  labels:
    gateway: multi-cluster-gateway
spec:
  parentRefs:
  - kind: Gateway
    namespace: store
    name: external-http
  hostnames:
  - "store.example.com"
  rules:
  - backendRefs:
    - group: net.gke.io
      kind: ServiceImport
      name: store
      port: 8080
次の図は、HTTPRoute が gke-west-1 と gke-east-1 の store Pod に store.example.com トラフィックを転送する方法を示しています。ロードバランサは、これを 1 つのバックエンドのプールとして扱います。一方のクラスタの Pod が異常な状態または到達不能になるか、トラフィック容量がなくなると、トラフィックの負荷はもう一方のクラスタの残りの Pod に分散されます。新しいクラスタの追加または削除は、store Service と ServiceExport を使用して行います。これにより、ルーティング構成を明示的に変更することなく、バックエンド Pod を透過的に追加または削除できます。
Service のエクスポート
この時点で、アプリケーションは両方のクラスタで実行されています。次に、各クラスタに Service と ServiceExport をデプロイして、アプリケーションを公開してエクスポートします。
- 次のマニフェストを - gke-west-1クラスタに適用して、- storeService、- store-west-1Service、ServiceExport を作成します。- cat << EOF | kubectl apply --context gke-west-1 -f - apiVersion: v1 kind: Service metadata: name: store namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store namespace: store --- apiVersion: v1 kind: Service metadata: name: store-west-1 namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store-west-1 namespace: store EOF
- 次のマニフェストを - gke-east-1クラスタに適用して、- storeService、- store-east-1Service、ServiceExport を作成します。- cat << EOF | kubectl apply --context gke-east-1 -f - apiVersion: v1 kind: Service metadata: name: store namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store namespace: store --- apiVersion: v1 kind: Service metadata: name: store-east-1 namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store-east-1 namespace: store EOF
- クラスタに正しい ServiceExport が作成されていることを確認します。 - kubectl get serviceexports --context CLUSTER_NAME --namespace store- CLUSTER_NAME を - gke-west-1と- gke-east-1に置き換えます。出力は次のようになります。- # gke-west-1 NAME AGE store 2m40s store-west-1 2m40s # gke-east-1 NAME AGE store 2m25s store-east-1 2m25s- これは、 - storeService には両方のクラスタに対する- storePod が含まれているのに対し、- store-west-1Service と- store-east-1Service には、それぞれのクラスタの- storeのみが含まれていることを示しています。この重複する Service は、複数のクラスタまたは単一クラスタの Pod のサブセットで Pod をターゲットにするために使用されます。
- 数分後、フリート内のすべてのクラスタに、付属の - ServiceImportsがマルチクラスタ Service コントローラによって自動的に作成されたことを確認します。- kubectl get serviceimports --context CLUSTER_NAME --namespace store- CLUSTER_NAME を - gke-west-1と- gke-east-1に置き換えます。出力は次のようになります。- # gke-west-1 NAME TYPE IP AGE store ClusterSetIP ["10.112.31.15"] 6m54s store-east-1 ClusterSetIP ["10.112.26.235"] 5m49s store-west-1 ClusterSetIP ["10.112.16.112"] 6m54s # gke-east-1 NAME TYPE IP AGE store ClusterSetIP ["10.72.28.226"] 5d10h store-east-1 ClusterSetIP ["10.72.19.177"] 5d10h store-west-1 ClusterSetIP ["10.72.28.68"] 4h32m- これは、フリートの両方のクラスタから 3 つの Service すべてにアクセスできることを示しています。ただし、フリートごとにアクティブな構成クラスタは 1 つのみであるため、これらの ServiceImport を参照する Gateway と HTTPRoute は - gke-west-1にのみデプロイできます。構成クラスタ内の HTTPRoute がこれらの ServiceImport をバックエンドとして参照している場合、Gateway はエクスポート元のクラスタに関係なく Service にトラフィックを転送できます。
Gateway と HTTPRoute のデプロイ
アプリケーションがデプロイされたら、gke-l7-global-external-managed-mc GatewayClass を使用して Gateway を構成できます。この Gateway は、ターゲット クラスタ間でトラフィックを分散するように構成された外部アプリケーション ロードバランサを作成します。
- 次の - Gatewayマニフェストを構成クラスタに適用します(この例では- gke-west-1)。- cat << EOF | kubectl apply --context gke-west-1 -f - kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: external-http namespace: store spec: gatewayClassName: gke-l7-global-external-managed-mc listeners: - name: http protocol: HTTP port: 80 allowedRoutes: kinds: - kind: HTTPRoute EOF- この Gateway 構成では、命名規則 - gkemcg1-NAMESPACE-GATEWAY_NAME-HASHに従って外部のアプリケーション ロードバランサ リソースがデプロイされます。- この構成で作成されるデフォルトのリソースは次のとおりです。 - 1 つのロード バランサ: gkemcg1-store-external-http-HASH
- 1 つのパブリック IP アドレス: gkemcg1-store-external-http-HASH
- 1 つの転送ルール: gkemcg1-store-external-http-HASH
- 2 つのバックエンド サービス: - デフォルトの 404 バックエンド サービス: gkemcg1-store-gw-serve404-HASH
- デフォルトの 500 バックエンド サービス: gkemcg1-store-gw-serve500-HASH
 
- デフォルトの 404 バックエンド サービス: 
- 1 つのヘルスチェック: - デフォルトの 404 ヘルスチェック: gkemcg1-store-gw-serve404-HASH
 
- デフォルトの 404 ヘルスチェック: 
- 0 個のルーティング ルール(URLmap は空)
 - この段階では、GATEWAY_IP:80 に対するリクエストでデフォルト ページに「 - fault filter abort」というメッセージが表示されます。
- 1 つのロード バランサ: 
- 次の - HTTPRouteマニフェストを構成クラスタに適用します(この例では- gke-west-1)。- cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: public-store-route namespace: store labels: gateway: external-http spec: hostnames: - "store.example.com" parentRefs: - name: external-http rules: - matches: - path: type: PathPrefix value: /west backendRefs: - group: net.gke.io kind: ServiceImport name: store-west-1 port: 8080 - matches: - path: type: PathPrefix value: /east backendRefs: - group: net.gke.io kind: ServiceImport name: store-east-1 port: 8080 - backendRefs: - group: net.gke.io kind: ServiceImport name: store port: 8080 EOF- この段階では、GATEWAY_IP:80 に対するリクエストでデフォルト ページに「 - fault filter abort」というメッセージが表示されます。- デプロイされると、この HTTPRoute は次のルーティング動作を構成します。 - store-west-1ServiceExport によって選択された Pod は- gke-west-1クラスタ内にのみ存在するため、- /westへのリクエストは- gke-west-1クラスタ内の- storePod に転送されます。
- store-east-1ServiceExport によって選択された Pod は- gke-east-1クラスタ内にのみ存在するため、- /eastへのリクエストは- gke-east-1クラスタ内の- storePod に転送されます。
- 他のパスへのリクエストは、正常性、容量、リクエスト元クライアントへの近接性に応じて、いずれかのクラスタ内の storePod に転送されます。
- GATEWAY_IP:80 に対するリクエストでデフォルト ページに「fault filter abort」というメッセージが表示されます。
 - 特定のクラスタ内に異常な Pod が存在する場合(または Pod が存在しない場合)、 - storeService へのトラフィックは実際に- storePod が存在するクラスタにのみ送信されます。特定のクラスタに ServiceExport と Service が存在しても、そのクラスタにトラフィックが送信されるとは限りません。Pod が存在し、ロードバランサのヘルスチェックに肯定的なレスポンスを返す必要があります。そうでない場合、ロードバランサは他のクラスタ内の正常な- storePod にトラフィックを送信します。- 新しいリソースは次の構成で作成されます。 - 3 つのバックエンド サービス: - storeバックエンド サービス:- gkemcg1-store-store-8080-HASH
- store-east-1バックエンド サービス:- gkemcg1-store-store-east-1-8080-HASH
- store-west-1バックエンド サービス:- gkemcg1-store-store-west-1-8080-HASH
 
- 3 つのヘルスチェック: - storeヘルスチェック:- gkemcg1-store-store-8080-HASH
- store-east-1ヘルスチェック:- gkemcg1-store-store-east-1-8080-HASH
- store-west-1ヘルスチェック:- gkemcg1-store-store-west-1-8080-HASH
 
- URLmap の 1 つのルーティング ルール:
- store.example.comルーティング ルール:
- 1 つのホスト: store.example.com
- 新しいバックエンド サービスに転送する複数の matchRules
 
 
次の図は、両方のクラスタにデプロイしたリソースを示しています。gke-west-1 は Gateway 構成クラスタであるため、Gateway コントローラが Gateway、HTTPRoute、ServiceImport を監視するクラスタになります。各クラスタには store ServiceImport と、そのクラスタに固有の別の ServiceImport が存在します。どちらも同じ Pod を参照します。これにより、HTTPRoute はトラフィックの送信先を正確に指定できます。送信先は、特定のクラスタの store Pod か、すべてのクラスタの store Pod になります。
これは論理的なリソースモデルであり、トラフィック フローの描写ではありません。トラフィックはロードバランサからバックエンド Pod に直接送信されます。どのクラスタが構成クラスタかは直接関係しません。
デプロイの検証
これで、マルチクラスタ Gateway にリクエストを発行し、トラフィックを複数の GKE クラスタに分散できるようになりました。
- Gateway のステータスとイベントを調べて、Gateway と HTTPRoute が正常にデプロイされたことを確認します。 - kubectl describe gateways.gateway.networking.k8s.io external-http --context gke-west-1 --namespace store- 出力は次のようになります。 - Name: external-http Namespace: store Labels: <none> Annotations: networking.gke.io/addresses: /projects/PROJECT_NUMBER/global/addresses/gkemcg1-store-external-http-laup24msshu4 networking.gke.io/backend-services: /projects/PROJECT_NUMBER/global/backendServices/gkemcg1-store-gw-serve404-80-n65xmts4xvw2, /projects/PROJECT_NUMBER/global/backendServices/gke... networking.gke.io/firewalls: /projects/PROJECT_NUMBER/global/firewalls/gkemcg1-l7-default-global networking.gke.io/forwarding-rules: /projects/PROJECT_NUMBER/global/forwardingRules/gkemcg1-store-external-http-a5et3e3itxsv networking.gke.io/health-checks: /projects/PROJECT_NUMBER/global/healthChecks/gkemcg1-store-gw-serve404-80-n65xmts4xvw2, /projects/PROJECT_NUMBER/global/healthChecks/gkemcg1-s... networking.gke.io/last-reconcile-time: 2023-10-12T17:54:24Z networking.gke.io/ssl-certificates: networking.gke.io/target-http-proxies: /projects/PROJECT_NUMBER/global/targetHttpProxies/gkemcg1-store-external-http-94oqhkftu5yz networking.gke.io/target-https-proxies: networking.gke.io/url-maps: /projects/PROJECT_NUMBER/global/urlMaps/gkemcg1-store-external-http-94oqhkftu5yz API Version: gateway.networking.k8s.io/v1 Kind: Gateway Metadata: Creation Timestamp: 2023-10-12T06:59:32Z Finalizers: gateway.finalizer.networking.gke.io Generation: 1 Resource Version: 467057 UID: 1dcb188e-2917-404f-9945-5f3c2e907b4c Spec: Gateway Class Name: gke-l7-global-external-managed-mc Listeners: Allowed Routes: Kinds: Group: gateway.networking.k8s.io Kind: HTTPRoute Namespaces: From: Same Name: http Port: 80 Protocol: HTTP Status: Addresses: Type: IPAddress Value: 34.36.127.249 Conditions: Last Transition Time: 2023-10-12T07:00:41Z Message: The OSS Gateway API has deprecated this condition, do not depend on it. Observed Generation: 1 Reason: Scheduled Status: True Type: Scheduled Last Transition Time: 2023-10-12T07:00:41Z Message: Observed Generation: 1 Reason: Accepted Status: True Type: Accepted Last Transition Time: 2023-10-12T07:00:41Z Message: Observed Generation: 1 Reason: Programmed Status: True Type: Programmed Last Transition Time: 2023-10-12T07:00:41Z Message: The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use. GKE Gateway will stop emitting it in a future update, use "Programmed" instead. Observed Generation: 1 Reason: Ready Status: True Type: Ready Listeners: Attached Routes: 1 Conditions: Last Transition Time: 2023-10-12T07:00:41Z Message: Observed Generation: 1 Reason: Programmed Status: True Type: Programmed Last Transition Time: 2023-10-12T07:00:41Z Message: The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use. GKE Gateway will stop emitting it in a future update, use "Programmed" instead. Observed Generation: 1 Reason: Ready Status: True Type: Ready Name: http Supported Kinds: Group: gateway.networking.k8s.io Kind: HTTPRoute Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal UPDATE 35m (x4 over 10h) mc-gateway-controller store/external-http Normal SYNC 4m22s (x216 over 10h) mc-gateway-controller SYNC on store/external-http was a success
- Gateway が正常にデプロイされたら、 - external-httpGateway から外部 IP アドレスを取得します。- kubectl get gateways.gateway.networking.k8s.io external-http -o=jsonpath="{.status.addresses[0].value}" --context gke-west-1 --namespace store- 次の手順の - VIPは、出力として受け取る IP アドレスに置き換えます。
- ドメインのルートパスにトラフィックを送信します。これにより、クラスタ - gke-west-1と- gke-east-1にある- storeServiceImport にトラフィックがロード バランシングされます。ロードバランサは、最も近いリージョンにトラフィックを送信します。このため、他のリージョンからのレスポンスが表示されない場合があります。- curl -H "host: store.example.com" http://VIP- この出力で、リクエストが - gke-east-1クラスタから Pod によって処理されたことを確認できます。- { "cluster_name": "gke-east-1", "zone": "us-east1-b", "host_header": "store.example.com", "node_name": "gke-gke-east-1-default-pool-7aa30992-t2lp.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-dg22z", "pod_name_emoji": "⏭", "project_id": "agmsb-k8s", "timestamp": "2021-06-01T17:32:51" }
- 次に、 - /westパスにトラフィックを送信します。これにより、- gke-west-1クラスタで実行されている Pod のみが存在する- store-west-1ServiceImport にトラフィックが転送されます。- store-west-1などのクラスタ固有の ServiceImport を使用することで、アプリケーション オーナーはロードバランサに決定を委ねるのではなく、特定のクラスタにトラフィックを明示的に送信できるようになります。- curl -H "host: store.example.com" http://VIP/west- この出力で、リクエストが - gke-west-1クラスタから Pod によって処理されたことを確認できます。- { "cluster_name": "gke-west-1", "zone": "us-west1-a", "host_header": "store.example.com", "node_name": "gke-gke-west-1-default-pool-65059399-2f41.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-d25m5", "pod_name_emoji": "🍾", "project_id": "agmsb-k8s", "timestamp": "2021-06-01T17:39:15", }
- 最後に、 - /eastパスにトラフィックを送信します。- curl -H "host: store.example.com" http://VIP/east- この出力で、リクエストが - gke-east-1クラスタから Pod によって処理されたことを確認できます。- { "cluster_name": "gke-east-1", "zone": "us-east1-b", "host_header": "store.example.com", "node_name": "gke-gke-east-1-default-pool-7aa30992-7j7z.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-hz6mw", "pod_name_emoji": "🧜🏾", "project_id": "agmsb-k8s", "timestamp": "2021-06-01T17:40:48" }
リージョン間で内部マルチクラスタ Gateway をデプロイする
複数のリージョンの GKE クラスタ間で内部レイヤ 7 ロード バランシングを提供するマルチクラスタ Gateway をデプロイできます。これらの Gateway は gke-l7-cross-regional-internal-managed-mc GatewayClass を使用します。この GatewayClass は、クロスリージョン内部アプリケーション ロードバランサをプロビジョニングします。これは Google Cloud によって管理され、VPC ネットワーク内のクライアントがアクセスできる内部 VIP を有効にします。これらの Gateway は、Gateway を使用して特定のリージョンのアドレスをリクエストするだけで、選択したリージョンのフロントエンドで公開できます。内部 VIP は単一の IP アドレスにすることも、複数のリージョンの IP アドレスにすることもできます。リージョンごとに 1 つの IP アドレスが Gateway で指定されます。トラフィックは、リクエストを処理できる最も近い正常なバックエンド GKE クラスタに転送されます。
始める前に
- プロジェクト ID を使用して - gcloud環境を構成し、プロジェクトとシェルを設定します。- export PROJECT_ID="YOUR_PROJECT_ID" gcloud config set project ${PROJECT_ID}
- 複数のリージョンに GKE クラスタを作成します。 - この例では、 - us-west1の- gke-west-1と- us-east1の- gke-east-1の 2 つのクラスタを使用します。Gateway API が有効(- --gateway-api=standard)で、クラスタがフリートに登録されていることを確認します。- gcloud container clusters create gke-west-1 \ --location=us-west1-a \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --project=${PROJECT_ID} \ --enable-fleet \ --gateway-api=standard gcloud container clusters create gke-east-1 \ --location=us-east1-c \ --workload-pool=${PROJECT_ID}.svc.id.goog \ --project=${PROJECT_ID} \ --enable-fleet \ --gateway-api=standard- アクセスしやすいようにコンテキストの名前を変更します。 - gcloud container clusters get-credentials gke-west-1 \ --location=us-west1-a \ --project=${PROJECT_ID} gcloud container clusters get-credentials gke-east-1 \ --location=us-east1-c \ --project=${PROJECT_ID} kubectl config rename-context gke_${PROJECT_ID}_us-west1-a_gke-west-1 gke-west1 kubectl config rename-context gke_${PROJECT_ID}_us-east1-c_gke-east-1 gke-east1
- マルチクラスタ Service(MCS)とマルチクラスタ Ingress(MCI / Gateway)を有効にします。 - gcloud container fleet multi-cluster-services enable --project=${PROJECT_ID} # Set the config membership to one of your clusters (e.g., gke-west-1) # This cluster will be the source of truth for multi-cluster Gateway and Route resources. gcloud container fleet ingress enable \ --config-membership=projects/${PROJECT_ID}/locations/us-west1/memberships/gke-west-1 \ --project=${PROJECT_ID}
- プロキシ専用サブネットを構成します。プロキシ専用サブネットは、GKE クラスタが配置される各リージョンと、ロードバランサが動作するリージョンに必要です。クロスリージョン内部アプリケーション ロードバランサでは、このサブネットの目的を - GLOBAL_MANAGED_PROXYに設定する必要があります。- # Proxy-only subnet for us-west1 gcloud compute networks subnets create us-west1-proxy-only-subnet \ --purpose=GLOBAL_MANAGED_PROXY \ --role=ACTIVE \ --region=us-west1 \ --network=default \ --range=10.129.0.0/23 # Choose an appropriate unused CIDR range # Proxy-only subnet for us-east1 gcloud compute networks subnets create us-east1-proxy-only-subnet \ --purpose=GLOBAL_MANAGED_PROXY \ --role=ACTIVE \ --region=us-east1 \ --network=default \ --range=10.130.0.0/23 # Choose an appropriate unused CIDR range- デフォルトのネットワークを使用していない場合は、 - defaultを VPC ネットワークの名前に置き換えます。CIDR 範囲が一意であり、重複していないことを確認します。
- storeなどのデモ アプリケーションを両方のクラスタにデプロイします。- gke-networking-recipesの- store.yamlファイルの例では、- storeNamespace と Deployment が作成されます。- kubectl apply --context gke-west1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml kubectl apply --context gke-east1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
- 各クラスタで Kubernetes - Serviceリソースと- ServiceExportリソースを作成して、各クラスタから Service をエクスポートします。これにより、フリート全体で Service を検出できるようになります。次の例では、各クラスタから汎用の- storeService とリージョン固有の Service(- store-west-1、- store-east-1)をエクスポートします。これらはすべて- storeNamespace 内にあります。- gke-west1に適用:- cat << EOF | kubectl apply --context gke-west1 -f - apiVersion: v1 kind: Service metadata: name: store namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store namespace: store --- apiVersion: v1 kind: Service metadata: name: store-west-1 # Specific to this cluster namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store-west-1 # Exporting the region-specific service namespace: store EOF- gke-east1に適用:- cat << EOF | kubectl apply --context gke-east1 -f - apiVersion: v1 kind: Service metadata: name: store namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store namespace: store --- apiVersion: v1 kind: Service metadata: name: store-east-1 # Specific to this cluster namespace: store spec: selector: app: store ports: - port: 8080 targetPort: 8080 --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store-east-1 # Exporting the region-specific service namespace: store EOF
- ServiceImport を確認します。 - ServiceImportリソースが各クラスタの- storeNamespace 内に作成されていることを確認します。作成には数分かかることがあります。- bash kubectl get serviceimports --context gke-west1 -n store kubectl get serviceimports --context gke-east1 -n store- store、- store-west-1、- store-east-1(または、伝播に基づく関連エントリ)が一覧表示されます。
内部マルチリージョン Gateway を構成する
gke-l7-cross-regional-internal-managed-mc GatewayClass を参照する Gateway リソースを定義します。このマニフェストは、指定の構成クラスタ(gke-west-1 など)に適用します。
spec.addresses フィールドでは、特定リージョンのエフェメラル IP アドレスをリクエストすることも、事前に割り振られた静的 IP アドレスを使用することもできます。
- エフェメラル IP アドレスを使用するには、次の - Gatewayマニフェストを- cross-regional-gateway.yamlとして保存します。- # cross-regional-gateway.yaml kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-cross-region-gateway namespace: store # Namespace for the Gateway resource spec: gatewayClassName: gke-l7-cross-regional-internal-managed-mc addresses: # Addresses across regions. Address value is allowed to be empty or matching # the region name. - type: networking.gke.io/ephemeral-ipv4-address/us-west1 value: "us-west1" - type: networking.gke.io/ephemeral-ipv4-address/us-east1 value: "us-east1" listeners: - name: http protocol: HTTP port: 80 allowedRoutes: kinds: - kind: HTTPRoute # Only allow HTTPRoute to attach- 前の YAML ファイルの一部のフィールドの定義を以下に示します。 - metadata.namespace: Gateway リソースが作成される Namespace(例:- store)。
- spec.gatewayClassName: GatewayClass の名前。必ず- gke-l7-cross-regional-internal-managed-mcを指定します。
- spec.listeners.allowedRoutes.kinds: 関連付けることができる Route オブジェクトの種類(- HTTPRouteなど)。
- spec.addresses:- type: networking.gke.io/ephemeral-ipv4-address/REGION: エフェメラル IP アドレスをリクエストします。
- value: アドレスのリージョンを指定します(- "us-west1"、- "us-east1"など)。
 
 
- マニフェストを構成クラスタ(例: - gke-west1)に適用します。- kubectl apply --context gke-west1 -f cross-regional-gateway.yaml
HTTPRoute を Gateway に接続する
トラフィック ルーティングを管理する HTTPRoute リソースを定義して、構成クラスタに適用します。
- 次の - HTTPRouteマニフェストを- store-route.yamlとして保存します。- # store-route.yaml kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: store-route namespace: store labels: gateway: cross-regional-internal spec: parentRefs: - name: internal-cross-region-gateway namespace: store # Namespace where the Gateway is deployed hostnames: - "store.example.internal" # Hostname clients will use rules: - matches: # Rule for traffic to /west - path: type: PathPrefix value: /west backendRefs: - group: net.gke.io # Indicates a multi-cluster ServiceImport kind: ServiceImport name: store-west-1 # Targets the ServiceImport for the west cluster port: 8080 - matches: # Rule for traffic to /east - path: type: PathPrefix value: /east backendRefs: - group: net.gke.io kind: ServiceImport name: store-east-1 # Targets the ServiceImport for the east cluster port: 8080 - backendRefs: # Default rule for other paths (e.g., /) - group: net.gke.io kind: ServiceImport name: store # Targets the generic 'store' ServiceImport (any region) port: 8080- 前の YAML ファイルの一部のフィールドの定義を以下に示します。 - spec.parentRefs: このルートを- storeNamespace の- internal-cross-region-gatewayに関連付けます。
- spec.hostnames: クライアントが Service にアクセスするために使用するホスト名を表します。
- spec.rules: ルーティング ロジックを定義します。この例では、パスベースのルーティングを使用します。- /westトラフィックは- store-west-1ServiceImport に転送されます。
- /eastトラフィックは- store-east-1ServiceImport に転送されます。
- /などの他のすべてのトラフィックは、汎用の- storeServiceImport に送信されます。
 
- backendRefs:- group: net.gke.ioと- kind: ServiceImportはマルチクラスタ Service をターゲットにします。
 
 
- HTTPRouteマニフェストを構成クラスタに適用します。- kubectl apply --context gke-west1 -f store-route.yaml
Gateway と Route のステータスを確認する
- Gateway のステータスを確認します。 - kubectl get gateway internal-cross-region-gateway -n store -o yaml --context gke-west1- type:Programmed- andstatus: "True" の条件を探します。- . You should see IP addresses assigned in thestatus.addresses- field, corresponding to the regions you specified (e.g., one forus-west1- and one forus-east1`).
- HTTPRoute のステータスを確認します。 - kubectl get httproute store-route -n store -o yaml --context gke-west1- status.parents[].conditionsで、- type: Accepted(または- ResolvedRefs)および- status: "True"の条件を探します。
トラフィックを確認する
IP アドレスを Gateway に割り当てたら、VPC ネットワーク内のいずれかのリージョン(Gateway の IP アドレスに接続できるリージョン)にあるクライアント VM からトラフィックをテストできます。
- Gateway の IP アドレスを取得します。 - 次のコマンドは、JSON 出力の解析を試みます。正確な構造に基づいて - jsonpathを調整する必要がある場合があります。- kubectl get gateway cross-region-gateway -n store --context gke-west1 -o=jsonpath="{.status.addresses[*].value}".- このコマンドの出力には、 - VIP1_WESTや- VIP2_EASTなどの VIP が含まれているはずです。
- VPC 内のクライアント VM からテスト リクエストを送信します。 - # Assuming VIP_WEST is an IP in us-west1 and VIP_EAST is an IP in us-east1 # Traffic to /west should ideally be served by gke-west-1 curl -H "host: store.example.internal" http://VIP_WEST/west curl -H "host: store.example.internal" http://VIP_EAST/west # Still targets store-west-1 due to path # Traffic to /east should ideally be served by gke-east-1 curl -H "host: store.example.internal" http://VIP_WEST/east # Still targets store-east-1 due to path curl -H "host: store.example.internal" http://VIP_EAST/east # Traffic to / (default) could be served by either cluster curl -H "host: store.example.internal" http://VIP_WEST/ curl -H "host: store.example.internal" http://VIP_EAST/- レスポンスには、リクエストを処理したバックエンド Pod を示す - storeアプリケーションの詳細(- cluster_nameや- zoneなど)が含まれているはずです。
静的 IP アドレスを使用する
エフェメラル IP アドレスの代わりに、事前に割り振られた静的内部 IP アドレスを使用できます。
- 使用するリージョンに静的 IP アドレスを作成します。 - gcloud compute addresses create cross-region-gw-ip-west --region us-west1 --subnet default --project=${PROJECT_ID} gcloud compute addresses create cross-region-gw-ip-east --region us-east1 --subnet default --project=${PROJECT_ID}- デフォルトのサブネットを使用していない場合は、 - defaultを、割り振る IP アドレスを持つサブネットの名前に置き換えます。これらのサブネットは、プロキシ専用サブネットではなく、通常のサブネットです。
- cross-regional-gateway.yamlファイルの- spec.addressesセクションを変更して、Gateway マニフェストを更新します。- # cross-regional-gateway-static-ip.yaml kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-cross-region-gateway # Or a new name if deploying alongside namespace: store spec: gatewayClassName: gke-l7-cross-regional-internal-managed-mc addresses: - type: networking.gke.io/named-address-with-region # Use for named static IP value: "regions/us-west1/addresses/cross-region-gw-ip-west" - type: networking.gke.io/named-address-with-region value: "regions/us-east1/addresses/cross-region-gw-ip-east" listeners: - name: http protocol: HTTP port: 80 allowedRoutes: kinds: - kind: HTTPRoute
- 更新された Gateway マニフェストを適用します。 - kubectl apply --context gke-west1 -f cross-regional-gateway.yaml
デフォルト以外のサブネットに関する特別な考慮事項
デフォルト以外のサブネットを使用する場合は、次の点に注意してください。
- 同じ VPC ネットワーク: 静的 IP アドレス、プロキシ専用サブネット、GKE クラスタなど、ユーザーが作成したすべてのリソースが同じ VPC ネットワーク内に存在する必要があります。 
- アドレス サブネット: Gateway の静的 IP アドレスを作成すると、指定したリージョンの通常のサブネットから割り振られます。 
- クラスタ サブネットの命名: 各リージョンに、MCG 構成クラスタが存在するサブネットと同じ名前のサブネットが必要です。 - たとえば、gke-west-1構成クラスタがprojects/YOUR_PROJECT/regions/us-west1/subnetworks/my-custom-subnetにある場合、アドレスをリクエストするリージョンにもmy-custom-subnetサブネットが必要です。us-east1リージョンとus-centra1リージョンでアドレスをリクエストする場合は、これらのリージョンにもmy-custom-subnetという名前のサブネットが存在する必要があります。
 
- たとえば、
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 EOF
- Gateway が正常に起動したことを確認します。次のコマンドを使用して、この 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 アプリケーションを完全に移行します。
容量ベースのロード バランシングをデプロイする
このセクションの演習では、異なるリージョンの 2 つの GKE クラスタにアプリケーションをデプロイし、グローバル ロード バランシングと Service 容量のコンセプトを説明します。生成されたトラフィックを 1 秒あたりのリクエスト数(RPS)のさまざまなレベルで送信し、クラスタ間やリージョン間でのトラフィックのロード バランシングの方法を示します。
次の図は、デプロイするトポロジと、トラフィックが Service の容量を超えた場合にクラスタとリージョン間でトラフィックがオーバーフローする仕組みを示しています。
トラフィック管理の詳細については、GKE トラフィック管理をご覧ください。
環境を準備する
- マルチクラスタ Gateway の有効化に沿って環境を準備します。 
- GatewayClass リソースが構成クラスタにインストールされていることを確認します。 - kubectl get gatewayclasses --context=gke-west-1- 出力は次のようになります。 - NAME CONTROLLER ACCEPTED AGE gke-l7-global-external-managed networking.gke.io/gateway True 16h gke-l7-global-external-managed-mc networking.gke.io/gateway True 14h gke-l7-gxlb networking.gke.io/gateway True 16h gke-l7-gxlb-mc networking.gke.io/gateway True 14h gke-l7-regional-external-managed networking.gke.io/gateway True 16h gke-l7-regional-external-managed-mc networking.gke.io/gateway True 14h gke-l7-rilb networking.gke.io/gateway True 16h gke-l7-rilb-mc networking.gke.io/gateway True 14h
アプリケーションをデプロイする
両方のクラスタにサンプルのウェブ アプリケーション サーバーをデプロイします。
kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml
kubectl apply --context gke-east-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml
出力は次のようになります。
namespace/store created
deployment.apps/store created
Service、Gateway、HTTPRoute をデプロイする
- 次の - Serviceマニフェストを- gke-west-1クラスタと- gke-east-1クラスタの両方に適用します。- cat << EOF | kubectl apply --context gke-west-1 -f - apiVersion: v1 kind: Service metadata: name: store namespace: traffic-test annotations: networking.gke.io/max-rate-per-endpoint: "10" spec: ports: - port: 8080 targetPort: 8080 name: http selector: app: store type: ClusterIP --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store namespace: traffic-test EOF- cat << EOF | kubectl apply --context gke-east-1 -f - apiVersion: v1 kind: Service metadata: name: store namespace: traffic-test annotations: networking.gke.io/max-rate-per-endpoint: "10" spec: ports: - port: 8080 targetPort: 8080 name: http selector: app: store type: ClusterIP --- kind: ServiceExport apiVersion: net.gke.io/v1 metadata: name: store namespace: traffic-test EOF- Service は、1 秒あたり 10 リクエストに設定された - max-rate-per-endpointでアノテーションされます。クラスタあたり 2 つのレプリカがある場合、各 Service にはクラスタごとに 20 RPS の容量があります。- Service の Service 容量レベルを選択する方法の詳細については、Service の容量を決定するをご覧ください。 
- 次の - Gatewayマニフェストを構成クラスタに適用します(この例では- gke-west-1)。- cat << EOF | kubectl apply --context gke-west-1 -f - kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: store namespace: traffic-test spec: gatewayClassName: gke-l7-global-external-managed-mc listeners: - name: http protocol: HTTP port: 80 allowedRoutes: kinds: - kind: HTTPRoute EOF- このマニフェストでは、一般公開された IP アドレスを持つ外部アプリケーション ロードバランサをデプロイする、グローバルな外部マルチクラスタ Gateway について記述します。 
- 次の - HTTPRouteマニフェストを構成クラスタに適用します(この例では- gke-west-1)。- cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: store namespace: traffic-test labels: gateway: store spec: parentRefs: - kind: Gateway namespace: traffic-test name: store rules: - backendRefs: - name: store group: net.gke.io kind: ServiceImport port: 8080 EOF- このマニフェストでは、すべてのトラフィックをストア ServiceImport に転送するルーティング ルールで Gateway を構成する HTTPRoute について記述します。 - storeServiceImport は、両方のクラスタで- storeService Pod をグループ化し、ロードバランサでそれらの Pod を 1 つの Service としてアドレス指定できます。- 数分後に Gateway のイベントをチェックして、デプロイが完了したかどうかを確認できます。 - kubectl describe gateway store -n traffic-test --context gke-west-1- 出力は次のようになります。 - ... Status: Addresses: Type: IPAddress Value: 34.102.159.147 Conditions: Last Transition Time: 2023-10-12T21:40:59Z Message: The OSS Gateway API has deprecated this condition, do not depend on it. Observed Generation: 1 Reason: Scheduled Status: True Type: Scheduled Last Transition Time: 2023-10-12T21:40:59Z Message: Observed Generation: 1 Reason: Accepted Status: True Type: Accepted Last Transition Time: 2023-10-12T21:40:59Z Message: Observed Generation: 1 Reason: Programmed Status: True Type: Programmed Last Transition Time: 2023-10-12T21:40:59Z Message: The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use. GKE Gateway will stop emitting it in a future update, use "Programmed" instead. Observed Generation: 1 Reason: Ready Status: True Type: Ready Listeners: Attached Routes: 1 Conditions: Last Transition Time: 2023-10-12T21:40:59Z Message: Observed Generation: 1 Reason: Programmed Status: True Type: Programmed Last Transition Time: 2023-10-12T21:40:59Z Message: The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use. GKE Gateway will stop emitting it in a future update, use "Programmed" instead. Observed Generation: 1 Reason: Ready Status: True Type: Ready Name: http Supported Kinds: Group: gateway.networking.k8s.io Kind: HTTPRoute Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 12m mc-gateway-controller traffic-test/store Normal SYNC 6m43s mc-gateway-controller traffic-test/store Normal UPDATE 5m40s (x4 over 12m) mc-gateway-controller traffic-test/store Normal SYNC 118s (x6 over 10m) mc-gateway-controller SYNC on traffic-test/store was a success- この出力は、Gateway が正常にデプロイされたことを示しています。Gateway をデプロイしてからトラフィックが通過し始めるまでに数分かかることがあります。次のステップで使用するため、この出力の IP アドレスをメモしておきます。 
トラフィックを確認する
curl コマンドを使用して、Gateway の IP アドレスをテストし、トラフィックがアプリケーションに渡されることを確認します。
curl GATEWAY_IP_ADDRESS
出力は次のようになります。
{
  "cluster_name": "gke-west-1",
  "host_header": "34.117.182.69",
  "pod_name": "store-54785664b5-mxstv",
  "pod_name_emoji": "👳🏿",
  "project_id": "project",
  "timestamp": "2021-11-01T14:06:38",
  "zone": "us-west1-a"
}
この出力には、リクエストが提供されたリージョンを示す Pod メタデータが表示されます。
負荷テストを使用してトラフィックを確認する
ロードバランサが機能していることを確認するには、gke-west-1 クラスタにトラフィック生成ツールをデプロイします。トラフィック生成ツールは、さまざまな負荷レベルでトラフィックを生成し、ロードバランサの容量とオーバーフロー機能を実証します。次の手順では、3 つのレベルの負荷を示します。
- 10 RPS。これは gke-west-1内のストア Service の容量を下回っています。
- 30 RPS。これは gke-west-1ストア Service の容量を超えているため、トラフィックがgke-east-1にオーバーフローします。
- 60 RPS。これは、両方のクラスタで Service の容量を超えています。
ダッシュボードを構成
- Gateway の基盤となっている URL マップの名前を取得します。 - kubectl get gateway store -n traffic-test --context=gke-west-1 -o=jsonpath="{.metadata.annotations.networking\.gke\.io/url-maps}"- 出力は次のようになります。 - /projects/PROJECT_NUMBER/global/urlMaps/gkemcg1-traffic-test-store-armvfyupay1t
- Google Cloud コンソールで、Metrics Explorer のページに移動します。 
- [指標を選択] で、[CODE: MQL] をクリックします。 
- 次のクエリを入力して、2 つのクラスタ全体にわたるストア Service のトラフィック指標を確認します。 - fetch https_lb_rule | metric 'loadbalancing.googleapis.com/https/backend_request_count' | filter (resource.url_map_name == 'GATEWAY_URL_MAP') | align rate(1m) | every 1m | group_by [resource.backend_scope], [value_backend_request_count_aggregate: aggregate(value.backend_request_count)]- GATEWAY_URL_MAPは、前の手順での URL マップ名に置き換えます。
- [クエリを実行] をクリックします。次のセクションで負荷生成ツールをデプロイしてから、指標がグラフに表示されるまで、少なくとも 5 分は待ちます。 
10 RPS でテストする
- Pod を - gke-west-1クラスタにデプロイします。- kubectl run --context gke-west-1 -i --tty --rm loadgen \ --image=cyrilbkr/httperf \ --restart=Never \ -- /bin/sh -c 'httperf \ --server=GATEWAY_IP_ADDRESS \ --hog --uri="/zone" --port 80 --wsess=100000,1,1 --rate 10'- GATEWAY_IP_ADDRESSは、前の手順で取得した Gateway IP アドレスに置き換えます。- 出力は次のようになります。これは、トラフィック生成ツールがトラフィックを送信していることを示しています。 - If you don't see a command prompt, try pressing enter.- 負荷生成ツールは Gateway に継続的に 10 RPS を送信します。トラフィックが Google Cloud リージョン内から送信されていても、ロードバランサは米国西海岸から送信されるクライアント トラフィックとして扱います。現実的なクライアント多様性をシミュレートするために、負荷生成ツールは各 HTTP リクエストを新しい TCP 接続として送信します。つまり、トラフィックはバックエンド Pod 間でより均等に分散されます。 - 生成ツールがダッシュボードのトラフィックを生成するまでに最大 5 分かかります。 
- Metrics Explorer ダッシュボードを表示します。各クラスタにロードバランシングされるトラフィックの量を示す 2 つの行が表示されます。  - us-west1-aが約 10 RPS のトラフィックを受信しているのに対し、- us-east1-bはトラフィックを受信していないことがわかります。トラフィック生成ツールは- us-west1で実行されているため、すべてのトラフィックは- gke-west-1クラスタ内の Service に送信されます。
- Ctrl+C を使用して負荷生成ツールを停止してから、Pod を削除します。 - kubectl delete pod loadgen --context gke-west-1
30 RPS でテストする
- 負荷生成ツールを再度デプロイしますが、送信は 30 RPS となるよう構成します。 - kubectl run --context gke-west-1 -i --tty --rm loadgen \ --image=cyrilbkr/httperf \ --restart=Never \ -- /bin/sh -c 'httperf \ --server=GATEWAY_IP_ADDRESS \ --hog --uri="/zone" --port 80 --wsess=100000,1,1 --rate 30'- 生成ツールがダッシュボードのトラフィックを生成するまでに最大 5 分かかります。 
- Cloud Ops ダッシュボードを表示します。  - 約 20 RPS が - us-west1-aに送信され、10 RPS が- us-east1-bに送信されていることがわかります。これは、- gke-west-1の Service が完全に使用されており、10 RPS のトラフィックが- gke-east-1の Service へオーバーフローしていることを示しています。
- Ctrl+C を使用して負荷生成ツールを停止してから、Pod を削除します。 - kubectl delete pod loadgen --context gke-west-1
60 RPS でテストする
- 60 RPS を送信するように構成された負荷生成ツールをデプロイします。 - kubectl run --context gke-west-1 -i --tty --rm loadgen \ --image=cyrilbkr/httperf \ --restart=Never \ -- /bin/sh -c 'httperf \ --server=GATEWAY_IP_ADDRESS \ --hog --uri="/zone" --port 80 --wsess=100000,1,1 --rate 60'
- 5 分待ってから、Cloud Ops ダッシュボードを表示します。両方のクラスタが約 30 RPS を受信していることがわかります。すべての Service はグローバルで過剰に使用されているため、トラフィックのスピルオーバーはなく、各 Service は可能な限りすべてのトラフィックを吸収します。  
- Ctrl+C を使用して負荷生成ツールを停止してから、Pod を削除します。 - kubectl delete pod loadgen --context gke-west-1
クリーンアップ
このページの演習を完了したら、アカウントで不要な請求が発生しないように以下の手順でリソースを削除します。
- 他の目的で登録する必要がない場合は、フリートからクラスタの登録を解除します。 
- multiclusterservicediscovery機能を無効にします。- gcloud container fleet multi-cluster-services disable
- マルチクラスタ Ingress を無効にします。 - gcloud container fleet ingress disable
- API を無効にします。 - gcloud services disable \ multiclusterservicediscovery.googleapis.com \ multiclusteringress.googleapis.com \ trafficdirector.googleapis.com \ --project=PROJECT_ID
共有 VPC でマルチクラスタ Gateway を使用する
マルチクラスタ Gateway は、ユースケースに応じて異なるトポロジで共有 VPC 環境にデプロイすることもできます。
次の表に、共有 VPC 環境内でサポートされているマルチクラスタ Gateway トポロジを示します。
| シナリオ | フリート ホスト プロジェクト | 構成クラスタ | ワークロード クラスタ | 
|---|---|---|---|
| 1 | 共有 VPC ホスト プロジェクト | 共有 VPC ホスト プロジェクト | 共有 VPC ホスト プロジェクト | 
| 2 | 共有 VPC サービス プロジェクト | 共有 VPC サービス プロジェクト (フリート サービス プロジェクトと同じ) | 共有 VPC サービス プロジェクト (フリート サービス プロジェクトと同じ) | 
共有 VPC 環境でマルチクラスタ Gateway を作成するには、次の手順を使用します。
- 共有 VPC を使用したマルチクラスタ Service を設定する手順に沿って操作します。 
- 構成クラスタにサービスを作成してエクスポートします。 
- マルチクラスタの内部 Gateway を使用する場合は、プロキシ専用サブネットを作成します。 
これらの手順を完了したら、トポロジに応じてデプロイを検証できます。
トラブルシューティング
内部 Gateway のプロキシ専用サブネットが存在しない
内部 Gateway に次のイベントがある場合、そのリージョンにはプロキシ専用サブネットは存在しません。この問題を解決するには、プロキシ専用サブネットをデプロイします。
generic::invalid_argument: error ensuring load balancer: Insert: Invalid value for field 'resource.target': 'regions/us-west1/targetHttpProxies/gkegw-x5vt-default-internal-http-2jzr7e3xclhj'. A reserved and active subnetwork is required in the same region and VPC as the forwarding rule.
正常なアップストリームがない
症状:
Gateway を作成してもバックエンド サービスにアクセスできない場合(503 レスポンス コード)、次の問題が発生している可能性があります。
no healthy upstream
理由:
このエラー メッセージは、ヘルスチェック プローバーが正常なバックエンド サービスを見つけられないことを示します。バックエンド サービスは正常である可能性がありますが、ヘルスチェックのカスタマイズが必要になる場合もあります。
回避策:
この問題を解決するには、HealthCheckPolicy を使用して、アプリケーションの要件(/health など)に基づいてヘルスチェックをカスタマイズします。
次のステップ
- Gateway コントローラの詳細を確認する。