このドキュメントでは、外部マルチクラスタ Gateway をデプロイして、2 つの異なる GKE クラスタで実行されているアプリケーションにインターネット トラフィックを転送する実践的な例について説明します。
マルチクラスタ Gateway は、複数の GKE クラスタにデプロイされたサービスのトラフィックを管理する強力な方法を提供します。Google のグローバル ロード バランシング インフラストラクチャを使用すると、アプリケーションの単一のエントリ ポイントを作成できるため、管理が簡素化され、信頼性が向上します。このチュートリアルでは、サンプル store アプリケーションを使用して、オンライン ショッピング サービスを複数のチームが所有して管理し、共有 GKE クラスタのフリート全体にデプロイしている実際のシナリオをシミュレートします。
始める前に
マルチクラスタ Gateway をデプロイする前に環境を用意する必要があります。続行する前に、マルチクラスタ Gateway 用に環境を準備するの手順を行ってください。
GKE クラスタをデプロイします。
クラスタをフリートに登録します(まだ登録されていない場合)。
マルチクラスタ Service コントローラとマルチクラスタ Gateway コントローラを有効にします。
最後に、お使いの環境でコントローラを使用する前に、GKE Gateway コントローラの制限事項と既知の問題をご確認ください。
マルチクラスタ、マルチリージョン、外部 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 storeCLUSTER_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 storeCLUSTER_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-HASHstore-east-1バックエンド サービス:gkemcg1-store-store-east-1-8080-HASHstore-west-1バックエンド サービス:gkemcg1-store-store-west-1-8080-HASH
- 3 つのヘルスチェック:
storeヘルスチェック:gkemcg1-store-store-8080-HASHstore-east-1ヘルスチェック:gkemcg1-store-store-east-1-8080-HASHstore-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 successGateway が正常にデプロイされたら、
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" }
クリーンアップ
このドキュメントの演習を完了したら、アカウントで不要な請求が発生しないように、以下の手順でリソースを削除します。
他の目的で登録する必要がない場合は、フリートからクラスタの登録を解除します。
multiclusterservicediscovery機能を無効にします。gcloud container fleet multi-cluster-services disableマルチクラスタ Ingress を無効にします。
gcloud container fleet ingress disableAPI を無効にします。
gcloud services disable \ multiclusterservicediscovery.googleapis.com \ multiclusteringress.googleapis.com \ trafficdirector.googleapis.com \ --project=PROJECT_ID
トラブルシューティング
正常なアップストリームがない
症状:
Gateway を作成してもバックエンド サービスにアクセスできない場合(503 レスポンス コード)、次の問題が発生している可能性があります。
no healthy upstream
理由:
このエラー メッセージは、ヘルスチェック プローバーが正常なバックエンド サービスを見つけられないことを示します。バックエンド サービスは正常である可能性がありますが、ヘルスチェックのカスタマイズが必要になる場合もあります。
回避策:
この問題を解決するには、HealthCheckPolicy を使用して、アプリケーションの要件(/health など)に基づいてヘルスチェックをカスタマイズします。
次のステップ
- Gateway コントローラの詳細を確認する。