部署外部多叢集閘道

本文將透過實例,引導您部署外部多叢集閘道,將網際網路流量轉送至在兩個不同 GKE 叢集中執行的應用程式。

多叢集閘道提供強大的方式,可管理部署在多個 GKE 叢集中的服務流量。使用 Google 的全球負載平衡基礎架構,即可為應用程式建立單一進入點,簡化管理作業並提升可靠性。

在本教學課程中,您將使用範例 store 應用程式模擬實際情況,也就是線上購物服務由不同團隊擁有及營運,並部署在共用 GKE 叢集的機群中。

這個範例說明如何設定路徑導向轉送,將流量導向不同叢集。

事前準備

部署多叢集閘道前,需要先完成一些環境準備作業。繼續操作前,請按照「準備多叢集閘道環境」一文的步驟操作:

  1. 部署 GKE 叢集。

  2. 將叢集註冊至機群 (如果尚未註冊)。

  3. 啟用多叢集服務和多叢集閘道控制器。

最後,請先查看 GKE Gateway 控制器的限制和已知問題,再於環境中使用控制器。

多叢集、多區域、外部閘道

在本教學課程中,您將建立外部多叢集閘道,為在兩個 GKE 叢集中執行的應用程式提供外部流量。

store.example.com 部署在兩個 GKE 叢集,並透過多叢集閘道公開至網際網路

您將在後續步驟執行下列操作:

  1. 將範例store應用程式部署至 gke-west-1gke-east-1 叢集。
  2. 在每個叢集上設定要匯出至機群的服務 (多叢集服務)。
  3. 將外部多叢集閘道和 HTTPRoute 部署至設定叢集 (gke-west-1)。

部署應用程式和 Gateway 資源後,您可以使用路徑式路由,控管兩個 GKE 叢集之間的流量:

  • /west 的要求會轉送到 gke-west-1 叢集中的 store Pod。
  • /east 的要求會轉送到 gke-east-1 叢集中的 store Pod。
  • 系統會根據叢集的健康狀態、容量和與要求用戶端的距離,將其他路徑的要求轉送至任一叢集。

部署示範應用程式

  1. 在「準備多叢集閘道環境」中部署的所有三個叢集中,建立 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
    

    本頁面的所有範例都使用這個步驟中部署的應用程式。請務必先在所有三個叢集部署應用程式,再嘗試執行其餘步驟。本範例只使用叢集 gke-west-1gke-east-1gke-west-2 則用於其他範例。

多叢集 Service

Service 可讓用戶端存取 Pod。由於 GKE Gateway 控制器使用容器原生負載平衡,因此不會使用 ClusterIP 或 Kubernetes 負載平衡來連線至 Pod。流量會直接從負載平衡器傳送至 Pod IP 位址。不過,服務仍扮演重要角色,是 Pod 分組的邏輯 ID。

多叢集服務 (MCS) 是跨叢集服務的 API 標準,GKE 控制器則提供跨 GKE 叢集的服務探索功能。多叢集 Gateway 控制器會使用 MCS API 資源,將 Pod 分組到可跨多個叢集定址或橫跨多個叢集的 Service 中。

多叢集服務 API 定義下列自訂資源:

  • ServiceExports 會對應至 Kubernetes 服務,並將該服務的端點匯出至已向機群註冊的所有叢集。如果 Service 具有對應的 ServiceExport,表示多叢集閘道可以處理該 Service。
  • 多叢集服務控制器會自動產生 ServiceImports。ServiceExport 和 ServiceImport 會成對出現。如果機群中存在 ServiceExport,系統就會建立對應的 ServiceImport,讓對應至 ServiceExport 的 Service 可從各個叢集存取。

匯出服務的運作方式如下:商店服務存在於 gke-west-1 中,可選取該叢集中的一組 Pod。系統會在叢集中建立 ServiceExport,讓機群中的其他叢集可以存取 gke-west-1 中的 Pod。ServiceExport 會對應並公開與 ServiceExport 資源名稱和命名空間相同的服務。

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 服務的本機表示法。這會啟用 client Pod (位於 gke-east-1 中),以便使用 ClusterIP 或無標題服務連線至 gke-west-1 中的 store Pod。以這種方式使用時,多叢集服務會在叢集之間提供東西向負載平衡,無須內部 LoadBalancer 服務。如要使用多叢集服務進行叢集間的負載平衡,請參閱設定多叢集服務

多叢集服務會匯出叢集中的服務,讓叢集之間能夠通訊

多叢集閘道也會使用 ServiceImports,但不是用於叢集間的負載平衡。而是使用 ServiceImports 做為服務的邏輯 ID,該服務存在於其他叢集,或跨越多個叢集。下列 HTTPRoute 參照的是 ServiceImport,而非 Service 資源。參照 ServiceImport 時,這表示服務會將流量轉送至一或多個叢集中執行的後端 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 如何將 store.example.com 流量轉送至 gke-west-1gke-east-1 上的 store Pod。負載平衡器會將這些後端視為一個集區。如果其中一個叢集的 Pod 狀況不佳、無法連線或沒有流量容量,流量負載會平衡至另一個叢集中的其餘 Pod。您可以使用 store 服務和 ServiceExport 新增或移除叢集。這項功能會透明地新增或移除後端 Pod,無須進行任何明確的路由設定變更。

MCS 資源

出口服務

此時,應用程式會在兩個叢集上執行。接著,您要將服務和 ServiceExport 部署至每個叢集,藉此公開及匯出應用程式。

  1. 將下列資訊清單套用至 gke-west-1 叢集,建立 storestore-west-1 服務和 ServiceExports:

    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 叢集,建立 storestore-east-1 服務和 ServiceExports:

    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_NAME 替換為 gke-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-1store-east-1 Service 則只在各自的叢集中包含 store Pod。這些重疊的服務可用於指定多個叢集中的 Pod,或單一叢集中的部分 Pod。

  4. 幾分鐘後,請確認多叢集服務控制器是否已在叢集聯盟的所有叢集中,自動建立隨附的 ServiceImports

    kubectl get serviceimports --context CLUSTER_NAME --namespace store
    

    CLUSTER_NAME 替換為 gke-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
    

    這表示機群中兩個叢集都能存取這三項服務。不過,每個車隊只有一個有效的設定叢集,因此您只能在 gke-west-1 中部署參照這些 ServiceImport 的 Gateway 和 HTTPRoute。當設定叢集中的 HTTPRoute 將這些 ServiceImport 參照為後端時,無論這些服務是從哪個叢集匯出,Gateway 都能將流量轉送至這些服務。

部署 Gateway 和 HTTPRoute

應用程式部署完成後,您可以使用 gke-l7-global-external-managed-mc GatewayClass 設定 Gateway。這個閘道會建立外部應用程式負載平衡器,並設定將流量分配至目標叢集。

  1. 將下列 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
    • 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/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 會設定下列轉送行為:

    • /west 的要求會路由到 gke-west-1 叢集中的 store Pod,因為 store-west-1 ServiceExport 選取的 Pod 只存在於 gke-west-1 叢集。
    • /east 的要求會路由到 gke-east-1 叢集中的 store Pod,因為 store-east-1 ServiceExport 選取的 Pod 只存在於 gke-east-1 叢集。
    • 系統會根據健康狀態、容量和與要求用戶端的距離,將其他路徑的要求轉送至任一叢集中的 store Pod。
    • GATEWAY_IP:80 發出的要求會導致預設頁面顯示以下訊息:fault filter abort

    HTTPRoute 可透過重疊的服務,將流量導向不同的叢集子集

    請注意,如果特定叢集上的所有 Pod 都不正常 (或不存在),則傳送至 store 服務的流量只會傳送至實際有 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。

這是兩個叢集之間的閘道和多叢集服務資源模型

請注意,這是邏輯資源模型,並非流量流程的描述。流量路徑會直接從負載平衡器傳送至後端 Pod,與哪個叢集是設定叢集沒有直接關係。

驗證部署作業

您現在可以向多叢集閘道發出要求,並在兩個 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/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
    
  2. 閘道部署完成後,請從 external-http 閘道擷取外部 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. 將流量傳送至網域的根路徑。這會將流量負載平衡至跨叢集 storegke-west-1 的 ServiceImport。gke-east-1負載平衡器會將流量傳送到離您最近的區域,因此您可能不會看到其他區域的回應。

    curl -H "host: store.example.com" http://VIP
    

    輸出內容會確認要求是由叢集中的 Pod 提供服務:gke-east-1

    {
      "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 路徑。這會將流量轉送至 store-west-1 ServiceImport,該 ServiceImport 只會在 gke-west-1 叢集上執行 Pod。叢集專用的 ServiceImport (例如 store-west-1) 可讓應用程式擁有者明確將流量傳送至特定叢集,而不是讓負載平衡器做出決定。

    curl -H "host: store.example.com" http://VIP/west
    

    輸出內容會確認要求是由叢集中的 Pod 提供服務:gke-west-1

    {
      "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
    

    輸出內容會確認要求是由叢集中的 Pod 提供服務:gke-east-1

    {
      "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"
    }
    

清除所用資源

完成本文件的練習後,請按照下列步驟移除資源,以免您的帳戶產生不必要的費用:

  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
    

疑難排解

沒有健康的上游

症狀:

建立閘道但無法存取後端服務時 (503 回應碼),可能會發生下列問題:

no healthy upstream

原因:

這則錯誤訊息表示健康狀態檢查探測器找不到健康狀態良好的後端服務。後端服務可能運作正常,但您可能需要自訂健康狀態檢查。

解決方法:

如要解決這個問題,請使用 HealthCheckPolicy,根據應用程式的需求自訂健康狀態檢查 (例如 /health)。

後續步驟