部署多叢集閘道,進行加權流量拆分

本文將引導您在兩個 GKE 叢集之間,對範例store 應用程式進行藍綠部署。藍綠部署是將應用程式遷移至新 GKE 叢集的有效策略,可將風險降至最低。將流量從目前的叢集 (藍色) 逐步轉移至新的叢集 (綠色),即可在正式環境中驗證新環境,然後再全面轉換。

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

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

事前準備

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

  1. 部署 GKE 叢集。

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

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

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

藍綠部署,透過閘道進行多叢集轉送

gke-l7-global-external-managed-*gke-l7-regional-external-managed-*gke-l7-rilb-* GatewayClass 具有許多進階流量轉送功能,包括流量拆分、標頭比對、標頭操控、流量鏡像等。在本範例中,您將示範如何使用權重式流量分割,明確控管兩個 GKE 叢集之間的流量比例。

本範例將逐步說明服務擁有者在遷移或擴展應用程式至新 GKE 叢集時,會採取哪些實際步驟。藍綠部署的目標是透過多個驗證步驟降低風險,確認新叢集運作正常。這個範例將逐步說明部署作業的四個階段:

  1. 100% - 以標頭為準的金絲雀部署使用 HTTP 標頭路由,只將測試或合成流量傳送至新叢集。
  2. 100% - 鏡像流量: 將使用者流量鏡像至 Canary 叢集。這項測試會將 100% 的使用者流量複製到 Canary 叢集,藉此測試叢集的容量。
  3. 90%-10%將流量分配給新叢集 10%,讓新叢集逐漸處理實際流量。
  4. 0%-100%完全轉換至新叢集,如果發現任何錯誤,可以選擇切換回舊叢集。

在兩個 GKE 叢集之間進行藍綠流量分割

這個範例與前一個範例類似,差別在於這個範例部署的是內部多叢集閘道。這會部署內部應用程式負載平衡器,只能從 VPC 內部以私有方式存取。您將使用先前步驟中部署的叢集和相同應用程式,但會透過不同的 Gateway 部署。

必要條件

下列範例以「部署外部多叢集閘道」中的部分步驟為基礎。請先完成下列步驟,再繼續進行這個範例:

  1. 為多叢集閘道準備環境

  2. 部署示範應用程式

    本範例使用您已設定的 gke-west-1gke-west-2 叢集。這些叢集位於相同區域,因為 gke-l7-rilb-mc GatewayClass 是區域性的,且僅支援相同區域中的叢集後端。

  3. 在每個叢集上部署所需的 Service 和 ServiceExports。如果您已從上一個範例部署服務和 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
    

設定僅限 Proxy 的子網路

如果您尚未設定,請為部署內部閘道的每個區域設定僅限 Proxy 的子網路。這個子網路用於為負載平衡器 Proxy 提供內部 IP 位址,且必須將 --purpose 設為 REGIONAL_MANAGED_PROXY

您必須先建立僅限 Proxy 的子網路,才能建立管理內部應用程式負載平衡器的閘道。您在使用內部應用程式負載平衡器的虛擬私有雲 (VPC) 網路的每個區域,都必須有僅限 Proxy 的子網路。

gcloud compute networks subnets create 指令會建立僅限 Proxy 的子網路。

gcloud compute networks subnets create SUBNET_NAME \
    --purpose=REGIONAL_MANAGED_PROXY \
    --role=ACTIVE \
    --region=REGION \
    --network=VPC_NETWORK_NAME \
    --range=CIDR_RANGE

更改下列內容:

  • SUBNET_NAME:僅限 Proxy 子網路的名稱。
  • REGION:僅限 Proxy 子網路的區域。
  • VPC_NETWORK_NAME:包含子網路的虛擬私有雲網路名稱。
  • CIDR_RANGE:子網路的主要 IP 位址範圍。 您必須使用不超過 /26 的子網路遮罩,使該地區的 Proxy 至少有 64 個 IP 位址可用。建議使用的子網路遮罩為 /23

部署閘道

下列閘道是從 gke-l7-rilb-mc GatewayClass 建立,屬於地區內部閘道,只能以相同地區的 GKE 叢集為目標。

  1. 將下列 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
    
  2. 確認閘道已成功啟動。您可以使用下列指令,只篩選這個閘道的事件:

    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
    

以標頭為準的初期測試

服務擁有者可透過以標頭為準的 Canary 測試,比對非真人使用者產生的合成測試流量。這樣一來,您就能輕鬆驗證應用程式的基本網路功能是否正常運作,不必直接向使用者公開。

  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:
      # 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 會設定下列轉送行為:

    • 傳送至 store.example.internal 沒有 env: canary HTTP 標頭的內部要求,會路由至 gke-west-1 叢集上的 store Pod
    • store.example.internal with 的內部要求會連同 env: canary HTTP 標頭,一併路由至 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 中使用私人用戶端,確認要求是否正確傳送。您必須在可私下存取閘道 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"
    }
    

流量鏡像

這個階段會將流量傳送至預期叢集,但也會將流量鏡像至 Canary 叢集。

使用鏡像功能有助於判斷流量負載對應用程式效能的影響,且不會以任何方式影響對用戶的回應。並非所有類型的推出作業都必須進行這項測試,但如果推出的大幅變更可能會影響效能或負載,這項測試就很有用。

  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:
      # 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 位址是負載平衡器的內部 IP 位址,這些位址會與 Pod 通訊。

    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 -
    

流量拆分

流量分割是推出新程式碼或安全部署至新環境最常見的方法之一。服務擁有者會明確設定要傳送至 Canary 後端的流量百分比,通常是整體流量的一小部分,這樣才能在可接受的實際使用者要求風險下,判斷推出作業是否成功。

服務擁有者可以透過流量分割,檢查應用程式的健康狀態和回應。如果所有信號都正常,即可繼續進行全面轉換。

  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
    
  2. 使用私人用戶端,持續向 internal- http Gateway 傳送 curl 要求。

    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",
    ...
    

流量切換

藍綠遷移的最後階段是完全切換至新叢集,並移除舊叢集。如果服務擁有者實際上是將第二個叢集加入現有叢集,則最後一個步驟會有所不同,因為最終步驟會將流量導向兩個叢集。在這種情況下,建議使用單一 store ServiceImport,其中包含 gke-west-1gke-west-2 叢集的 Pod。負載平衡器可根據鄰近程度、健康狀態和容量,判斷流量應導向何處,以供主動-主動應用程式使用。

  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:
          # 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. 使用私人用戶端,持續向 internal- http Gateway 傳送 curl 要求。

    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",
    ...
    

最後這個步驟會完成應用程式的完整藍綠遷移作業,從一個 GKE 叢集遷移至另一個 GKE 叢集。

清除所用資源

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

  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)。

後續步驟