マルチクラスタ Gateway のデプロイ


このページでは、複数の Google Kubernetes Engine(GKE)クラスタ(またはフリート)間で上り(内向き)トラフィックのロード バランシングを行う Kubernetes Gateway リソースをデプロイする方法について説明します。マルチクラスタ Gateway をデプロイする前に、マルチクラスタ Gateway の有効化を参照して環境を準備してください。

上り(内向き)トラフィックのロード バランシングを行う Gateway を単一の GKE クラスタにデプロイする場合は、Gateway のデプロイをご覧ください。

マルチクラスタ Gateway

マルチクラスタ Gateway は、複数の Kubernetes クラスタ間でトラフィックのロード バランシングを行う Gateway リソースです。GKE では、gke-l7-global-external-managed-mcgke-l7-regional-external-managed-mcgke-l7-rilb-mcgke-l7-gxlb-mc の GatewayClass は、さまざまな GKE クラスタ、Kubernetes Namespace、さまざまなリージョンにまたがって、HTTP ルーティング、トラフィック分割、トラフィック ミラーリング、ヘルスチェック ベースのフェイルオーバーなどを実施するマルチクラスタ Gateway をデプロイします。マルチクラスタ Gateway を使用すると、インフラストラクチャ管理者に対して、多数のクラスタ間やチーム間でアプリケーション ネットワーキングを簡単に、安全に、スケーラブルにします。

マルチクラスタ Gateway は、複数の Kubernetes クラスタ間でトラフィックのロード バランシングを行う Gateway リソースです。

このページでは、GKE Gateway Controller を使用してマルチクラスタ Gateway をデプロイする方法を示す 3 つの例を紹介します。

  • 例 1: インターネット トラフィック用の 2 つの GKE クラスタ間でロード バランシングを行う外部マルチクラスタ Gateway。
  • 例 2: 内部 VPC トラフィック用の 2 つの GKE クラスタ間で Blue/Green の重み付けに基づくトラフィック分割とトラフィック ミラーリング。
  • 例 3: 最大容量に基づいて異なるバックエンドに対してリクエストのロード バランシングを行う容量ベースの Gateway。

それぞれの例で、同じストアとサイトのアプリケーションを使用して、オンライン ショッピング サービスとウェブサイト サービスを複数のチームが所有して管理し、共有 GKE クラスタのフリート全体にデプロイしている実際のシナリオをモデル化します。それぞれの例で、マルチクラスタ Gateway によって可能になるトポロジとユースケースについて説明します。

マルチクラスタ Gateway をデプロイする前に環境を用意する必要があります。続行する前に、マルチクラスタ Gateway の有効化の手順を行ってください。

  1. GKE クラスタをデプロイします。

  2. フリートにクラスタを登録します。

  3. マルチクラスタ Service コントローラとマルチクラスタ Gateway コントローラを有効にします。

最後に、お使いの環境で使用する前に、GKE Gateway Controller の制限事項と既知の問題をご確認ください。

マルチクラスタ、マルチリージョン、外部 Gateway

このチュートリアルでは、2 つの GKE クラスタのアプリケーション間で外部トラフィックを分散させる外部マルチクラスタ Gateway を作成します。

store.example.com は 2 つの GKE クラスタにデプロイされ、マルチクラスタ Gateway を使用してインターネットに公開されます

以下の手順を実施します。

  1. gke-west-1 クラスタと gke-east-1 クラスタにサンプル store アプリケーションをデプロイします
  2. フリートにエクスポートする各クラスタで Service を設定します(マルチクラスタ Service)。
  3. 外部のマルチクラスタ Gateway と HTTPRoute を構成クラスタ(gke-west-1)にデプロイします。

アプリケーションと Gateway リソースがデプロイされると、パスベースのルーティングを使用して 2 つの GKE クラスタ間のトラフィックを制御できます。

  • /west へのリクエストは、gke-west-1 クラスタ内の store Pod に転送されます。
  • /east へのリクエストは、gke-east-1 クラスタ内の store Pod に転送されます。
  • 他のパスへのリクエストは、正常性、容量、リクエスト元クライアントへの近接性に応じて、いずれかのクラスタに転送されます。

デモ アプリケーションのデプロイ

  1. マルチクラスタ Gateway の有効化でデプロイされた 3 つのクラスタすべてで、store Deployment と 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-1gke-east-1 のみを使用しています。別の例では gke-west-2 を使用しています。

マルチクラスタ Service

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-1client Pod が ClusterIP またはヘッドレス Service を使用して gke-west-1store Pod にアクセスできるようになります。このように使用すると、マルチクラスタ Service はクラスタ間で East-West のロード バランシングを行います。内部 LoadBalancer Service は必要ありません。クラスタ間のロード バランシングにマルチクラスタ Service を使用するには、マルチクラスタ Service の構成をご覧ください。

マルチクラスタ Service は、クラスタ間で Service をエクスポートすることでクラスタ間の通信を可能にします。

マルチクラスタ Gateway でも ServiceImport は使用されますが、クラスタ間のロード バランシングでは使用されません。Gateway は、別のクラスタに存在する Service または複数のクラスタにまたがる Service の論理 ID として ServiceImport を使用します。次の HTTPRoute は Service リソースではなく ServiceImport を参照します。ServiceImport を参照することで、1 つ以上のクラスタで実行されているバックエンド Pod のグループにトラフィックが転送されています。

kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
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-1gke-east-1store Pod に store.example.com トラフィックを転送する方法を示しています。ロードバランサは、これを 1 つのバックエンドのプールとして扱います。一方のクラスタの Pod が異常な状態または到達不能になるか、トラフィック容量がなくなると、トラフィックの負荷はもう一方のクラスタの残りの Pod に分散されます。新しいクラスタの追加または削除は、store Service と ServiceExport を使用して行います。これにより、ルーティング構成を明示的に変更することなく、バックエンド Pod を透過的に追加または削除できます。

MCS リソース

Service のエクスポート

この時点で、アプリケーションは両方のクラスタで実行されています。次に、各クラスタに Service と ServiceExport をデプロイして、アプリケーションを公開してエクスポートします。

  1. 次のマニフェストを gke-west-1 クラスタに適用して、store Service、store-west-1 Service、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
    
  2. 次のマニフェストを gke-east-1 クラスタに適用して、store Service、store-east-1 Service、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
    
  3. クラスタに正しい ServiceExport が作成されていることを確認します。

    kubectl get serviceexports --context CLUSTER_NAME --namespace store
    

    CLUSTER_NAMEgke-west-1gke-east-1 に置き換えます。出力は次のようになります。

    # gke-west-1
    NAME           AGE
    store          2m40s
    store-west-1   2m40s
    
    # gke-east-1
    NAME           AGE
    store          2m25s
    store-east-1   2m25s
    

    これは、store Service には両方のクラスタに対する store Pod が含まれているのに対し、store-west-1 Service と store-east-1 Service には、それぞれのクラスタの store のみが含まれていることを示しています。この重複する Service は、複数のクラスタまたは単一クラスタの Pod のサブセットで Pod をターゲットにするために使用されます。

  4. 数分後、フリート内のすべてのクラスタに、付属の ServiceImports がマルチクラスタ Service コントローラによって自動的に作成されたことを確認します。

    kubectl get serviceimports --context CLUSTER_NAME --namespace store
    

    CLUSTER_NAMEgke-west-1gke-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 は、ターゲット クラスタ間でトラフィックを分散するように構成された外部アプリケーション ロードバランサを作成します。

  1. 次の Gateway マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    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
    • 1 つのヘルスチェック:
      • デフォルトの 404 ヘルスチェック: gkemcg1-store-gw-serve404-HASH
    • 0 個のルーティング ルール(URLmap は空)

    この段階では、GATEWAY_IP:80 に対するリクエストでデフォルト ページに「fault filter abort」というメッセージが表示されます。

  2. 次の HTTPRoute マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    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-1 ServiceExport によって選択された Pod は gke-west-1 クラスタ内にのみ存在するため、/west へのリクエストは gke-west-1 クラスタ内の store Pod に転送されます。
    • store-east-1 ServiceExport によって選択された Pod は gke-east-1 クラスタ内にのみ存在するため、/east へのリクエストは gke-east-1 クラスタ内の store Pod に転送されます。
    • 他のパスへのリクエストは、正常性、容量、リクエスト元クライアントへの近接性に応じて、いずれかのクラスタ内の store Pod に転送されます。
    • GATEWAY_IP:80 に対するリクエストでデフォルト ページに「fault filter abort」というメッセージが表示されます。

    HTTPRoute では、重複する Service を使用してクラスタの異なるサブセットに転送できます。

    特定のクラスタ内に異常な Pod が存在する場合(または Pod が存在しない場合)、store Service へのトラフィックは実際に store Pod が存在するクラスタにのみ送信されます。特定のクラスタに ServiceExport と Service が存在しても、そのクラスタにトラフィックが送信されるとは限りません。Pod が存在し、ロードバランサのヘルスチェックに肯定的なレスポンスを返す必要があります。そうでない場合、ロードバランサは他のクラスタ内の正常な store Pod にトラフィックを送信します。

    新しいリソースは次の構成で作成されます。

    • 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 になります。

両方のクラスタに対する Gateway とマルチクラスタ Service のリソースモデル

これは論理的なリソースモデルであり、トラフィック フローの描写ではありません。トラフィックはロードバランサからバックエンド Pod に直接送信されます。どのクラスタが構成クラスタかは直接関係しません。

デプロイの検証

これで、マルチクラスタ Gateway にリクエストを発行し、トラフィックを複数の GKE クラスタに分散できるようになりました。

  1. 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/v1beta1
    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
    
  2. Gateway が正常にデプロイされたら、external-http Gateway から外部 IP アドレスを取得します。

    kubectl get gateways.gateway.networking.k8s.io external-http -o=jsonpath="{.status.addresses[0].value}" --context gke-west-1 --namespace store
    

    次の手順の VIP は、出力として受け取る IP アドレスに置き換えます。

  3. ドメインのルートパスにトラフィックを送信します。これにより、クラスタ gke-west-1gke-east-1 にある store ServiceImport にトラフィックがロード バランシングされます。ロードバランサは、最も近いリージョンにトラフィックを送信します。このため、他のリージョンからのレスポンスが表示されない場合があります。

    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"
    }
    
  4. 次に、/west パスにトラフィックを送信します。これにより、gke-west-1 クラスタで実行されている Pod のみが存在する store-west-1 ServiceImport にトラフィックが転送されます。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",
    }
    
  5. 最後に、/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 による Blue/Green マルチクラスタ ルーティング

gke-l7-global-external-managed-*gke-l7-regional-external-managed-*gke-l7-rilb-* の GatewayClass では、トラフィック分割、ヘッダー マッチング、ヘッダー操作、トラフィック ミラーリングなど、多くの高度なトラフィック ルーティング機能を使用できます。この例では、重み付けに基づいてトラフィック分割を行い、2 つの GKE クラスタ間のトラフィックの比率を明示的に制御しています。

この例では、サービス オーナーがアプリケーションを新しい GKE クラスタに移動または拡張する際に行う具体的な手順を説明します。Blue/Green デプロイの目標は、新しいクラスタが正しく動作していることを確認する複数の検証手順を行い、リスクを減らすことです。この例では、デプロイの 4 つのステージについて説明します。

  1. 100% - ヘッダーに基づくカナリア: HTTP ヘッダーのルーティングを使用して、テストまたは合成トラフィックのみを新しいクラスタに送信します。
  2. 100% - トラフィックのミラーリング: カナリア クラスタへのユーザー トラフィックをミラーリングします。ユーザー トラフィックの 100% がこのクラスタにコピーされ、カナリア クラスタの容量がテストされます。
  3. 90%10%: 10% のカナリア トラフィック分割で新しいクラスタをライブ トラフィックにゆっくり公開します。
  4. 0%100%: エラーが観察された場合は、元に戻すオプションを使用して、新しいクラスタに完全にカットオーバーします。

2 つの GKE クラスタ間での Blue/Green トラフィック分割

この例は前のものと似ていますが、内部マルチクラスタ Gateway をデプロイしている点が異なります。この例では、VPC 内からのみプライベートでのアクセスが可能な内部アプリケーション ロードバランサをデプロイしています。別の Gateway を介してデプロイする場合を除き、前の手順でデプロイしたクラスタとアプリケーションを使用します。

前提条件

次の例では、外部マルチクラスタ Gateway のデプロイの一部の手順を使用しています。この例を続行する前に、次のことが完了していることを確認してください。

  1. マルチクラスタ Gateway の有効化

  2. デモ アプリケーションのデプロイ

    この例では、設定済みの gke-west-1 クラスタと gke-west-2 クラスタを使用します。gke-l7-rilb-mc GatewayClass はリージョンであり、同じリージョン内のクラスタ バックエンドのみをサポートしているため、これらのクラスタは同じリージョンにあります。

  3. 各クラスタに必要な 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 アドレスを提供するために使用されます。--purposeREGIONAL_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 から作成されています。これはリージョンの内部 Gateway で、同じリージョン内の GKE クラスタのみをターゲットにできます。

  1. 次の Gateway マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    metadata:
      name: internal-http
      namespace: store
    spec:
      gatewayClassName: gke-l7-rilb-mc
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    EOF
    
  2. 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
    

ヘッダーに基づくカナリア

ヘッダーに基づくカナリアでは、サービス オーナーは実際のユーザー以外からの合成テスト トラフィックを照合できます。これは、ユーザーを直接公開することなく、アプリケーションの基本ネットワークが機能していることを簡単に検証できる方法です。

  1. 次の HTTPRoute マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    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: canary HTTP ヘッダーを使用しない store.example.internal への内部リクエストは、gke-west-1 クラスタの store Pod に転送されます。
    • env: canary HTTP ヘッダーを使用する store.example.internal への内部リクエストは、gke-west-2 クラスタの store Pod に転送されます。

    HTTPRoute では、HTTP ヘッダーに基づいて異なるクラスタに転送できます。

    Gateway IP アドレスにトラフィックを送信し、HTTPRoute が正しく機能していることを確認します。

  2. 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 アドレスに置き換えます。

  3. env: canary HTTP ヘッダーを使用して、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"
    }
    

トラフィック ミラーリング

このステージでは、トラフィックを目的のクラスタに送信するだけでなく、カナリア クラスタにトラフィックをミラーリングします。

ミラーリングを使用すると、クライアントへのレスポンスに影響を与えることなく、トラフィックの負荷がアプリケーションのパフォーマンスに与える影響を判断するのに役立ちます。すべての種類のロールアウトで必要になるとは限りませんが、パフォーマンスや負荷に影響を及ぼす可能性のある大規模な変更をロールアウトする場合に役立ちます。

  1. 次の HTTPRoute マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    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
    
  2. プライベート クライアントを使用して、internal-http Gateway にリクエストを送信します。 /mirror パスを使用して、後の手順でアプリケーション ログを確認する際にこのリクエストを一意に識別できるようにします。

    curl -H "host: store.example.internal" http://VIP/mirror
    
  3. この出力で、クライアントが 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"
    }
    

    これにより、プライマリ クラスタがトラフィックに応答していることを確認します。また、移行先のクラスタがミラーリング対象トラフィックを受信していることも確認する必要があります。

  4. gke-west-2 クラスタの store Pod のアプリケーション ログを確認します。このログは、Pod がロードバランサからミラーリングされたトラフィックを受け取ったことを確認するものです。

    kubectl logs deployment/store --context gke-west-2 -n store | grep /mirror
    
  5. この出力では、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 つです。サービス オーナーは、カナリア バックエンドに送信されるトラフィックの明示的な割合を設定します。これは通常、トラフィック全体に対してかなり少ない量になります。実際のユーザー リクエストに対して許容可能なリスク量も判断できます。

ごく少量のトラフィックでトラフィック分割を行うと、サービス オーナーはアプリケーションの状態とレスポンスを検査できます。すべてのシグナルが正常と思われる場合は、カットオーバーに進みます。

  1. 次の HTTPRoute マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    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
    
  2. プライベート クライアントを使用して、継続的 curl リクエストを internal- http Gateway に送信します。

    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 をおすすめします。これにより、ロードバランサは、近接性、健全性、容量に基づいて、アクティブ / アクティブなアプリケーションのトラフィックの送信先を決定できます。

  1. 次の HTTPRoute マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    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
    
  2. プライベート クライアントを使用して、継続的 curl リクエストを internal- http Gateway に送信します。

    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 トラフィック管理をご覧ください。

環境を準備する

  1. マルチクラスタ Gateway の有効化に沿って環境を準備します。

  2. 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 をデプロイする

  1. 次の 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 の容量を決定するをご覧ください。

  2. 次の Gateway マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    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 について記述します。

  3. 次の HTTPRoute マニフェストを構成クラスタに適用します(この例では gke-west-1)。

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1beta1
    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 について記述します。store ServiceImport は、両方のクラスタで store Service 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 の容量を超えています。

ダッシュボードを構成する

  1. 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
    
  2. Google Cloud コンソールで、[Metrics Explorer] ページに移動します。

    Metrics Explorer に移動

  3. [指標を選択] で、[CODE: MQL] をクリックします。

  4. 次のクエリを入力して、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. [クエリを実行] をクリックします。次のセクションで負荷生成ツールをデプロイしてから、指標がグラフに表示されるまで、少なくとも 5 分は待ちます。

10 RPS でテストする

  1. 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 分かかります。

  2. Metrics Explorer ダッシュボードを表示します。各クラスタにロードバランシングされるトラフィックの量を示す 2 つの行が表示されます。

    クラスタにロードバランシングされたトラフィックを示すグラフ

    us-west1-a が約 10 RPS のトラフィックを受信しているのに対し、us-east1-b はトラフィックを受信していないことがわかります。トラフィック生成ツールは us-west1 で実行されているため、すべてのトラフィックは gke-west-1 クラスタ内の Service に送信されます。

  3. Ctrl+C を使用して負荷生成ツールを停止してから、Pod を削除します。

    kubectl delete pod loadgen --context gke-west-1
    

30 RPS でテストする

  1. 負荷生成ツールを再度デプロイしますが、送信は 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 分かかります。

  2. Cloud Ops ダッシュボードを表示します。

    gke-east-1 に対してオーバーフローするトラフィックを示すグラフ

    約 20 RPS が us-west1-a に送信され、10 RPS が us-east1-b に送信されていることがわかります。これは、gke-west-1 の Service が完全に使用されており、10 RPS のトラフィックが gke-east-1 の Service へオーバーフローしていることを示しています。

  3. Ctrl+C を使用して負荷生成ツールを停止してから、Pod を削除します。

    kubectl delete pod loadgen --context gke-west-1
    

60 RPS でテストする

  1. 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'
    
  2. 5 分待ってから、Cloud Ops ダッシュボードを表示します。両方のクラスタが約 30 RPS を受信していることがわかります。すべての Service はグローバルで過剰に使用されているため、トラフィックのスピルオーバーはなく、各 Service は可能な限りすべてのトラフィックを吸収します。

    Service の過剰使用を示すグラフ

  3. Ctrl+C を使用して負荷生成ツールを停止してから、Pod を削除します。

    kubectl delete pod loadgen --context gke-west-1
    

クリーンアップ

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

  1. クラスタを削除します

  2. 他の目的で登録する必要がない場合は、フリートからクラスタの登録を解除します。

  3. multiclusterservicediscovery 機能を無効にします。

    gcloud container fleet multi-cluster-services disable
    
  4. マルチクラスタ Ingress を無効にします。

    gcloud container fleet ingress disable
    
  5. 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 を作成するには、次の手順を使用します。

  1. 共有 VPC を使用したマルチクラスタ Service を設定する手順に沿って操作します。

  2. 構成クラスタにサービスを作成してエクスポートします。

  3. マルチクラスタの内部 Gateway を使用する場合は、プロキシ専用サブネットを作成します。

  4. マルチクラスタの外部または内部 Gateway と HTTPRoute を作成します。

これらの手順を完了したら、トポロジに応じてデプロイを検証できます。

トラブルシューティング

内部 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.

次のステップ