在代管 Cloud Service Mesh 上設定多叢集網格

本指南說明如何使用 Mesh CA 或憑證授權單位服務,將兩個叢集加入單一 Cloud Service Mesh,並啟用跨叢集負載平衡。您可以輕鬆擴展這項程序,將任意數量的叢集併入網格。

多叢集 Cloud Service Mesh 設定可解決多項企業重要情境,例如規模、位置和隔離。詳情請參閱「多叢集使用案例」。

必要條件

本指南假設您有兩個以上的 Google CloudGKE 叢集,且符合下列規定:

  • 叢集上已安裝 Cloud Service Mesh。您需要 asmcli、istioctl 工具,以及 asmcli 下載至 --output_dir 中指定目錄的範例。
  • 設定 Cloud Service Mesh 前,網格中的叢集必須先建立所有 Pod 之間的連線。此外,如果加入的叢集不在同一個專案中,則必須註冊至相同的機群主機專案,且叢集必須位於相同的網路,並採用共用虛擬私有雲設定。此外,我們建議您使用一個專案來代管 Shared VPC,並使用兩個服務專案來建立叢集。詳情請參閱「使用 Shared VPC 設定叢集」。
  • 如果您使用憑證授權單位服務,所有叢集都必須有各自的從屬 CA 集區,並鏈結至同一個根 CA 集區。否則,所有這些服務都必須使用相同的 CA 集區。

設定專案和叢集變數

  1. 為專案 ID、叢集可用區/區域、叢集名稱和環境建立下列環境變數。

    export PROJECT_1=PROJECT_ID_1
    export LOCATION_1=CLUSTER_LOCATION_1
    export CLUSTER_1=CLUSTER_NAME_1
    export CTX_1="gke_${PROJECT_1}_${LOCATION_1}_${CLUSTER_1}"
    
    export PROJECT_2=PROJECT_ID_2
    export LOCATION_2=CLUSTER_LOCATION_2
    export CLUSTER_2=CLUSTER_NAME_2
    export CTX_2="gke_${PROJECT_2}_${LOCATION_2}_${CLUSTER_2}"
    
  2. 如果是新建立的叢集,請務必使用下列 gcloud 指令擷取每個叢集的憑證,否則相關聯的 context 將無法在本指南的後續步驟中使用。

    指令取決於叢集類型 (區域或可用區):

    區域

    gcloud container clusters get-credentials ${CLUSTER_1} --region ${LOCATION_1}
    gcloud container clusters get-credentials ${CLUSTER_2} --region ${LOCATION_2}
    

    可用區

    gcloud container clusters get-credentials ${CLUSTER_1} --zone ${LOCATION_1}
    gcloud container clusters get-credentials ${CLUSTER_2} --zone ${LOCATION_2}
    

建立防火牆規則

在某些情況下,您需要建立防火牆規則,允許跨叢集流量。舉例來說,如果發生下列情況,您就需要建立防火牆規則:

  • 您為網格中的叢集使用不同的子網路。
  • Pod 開啟的通訊埠不是 443 和 15002。

GKE 會自動為每個節點新增防火牆規則,允許相同子網路內的流量。如果網狀架構包含多個子網路,您必須明確設定防火牆規則,允許跨子網路流量。您必須為每個子網路新增防火牆規則,允許所有傳入流量的來源 IP CIDR 區塊和目標通訊埠。

下列操作說明可讓專案中的所有叢集或僅 $CLUSTER_1 和 $CLUSTER_2 之間進行通訊。

  1. 收集叢集網路的相關資訊。

    所有專案叢集

    如果叢集位於同一專案中,可以使用下列指令,允許專案中的所有叢集進行通訊。如果專案中有不想公開的叢集,請使用「特定叢集」分頁中的指令。

    function join_by { local IFS="$1"; shift; echo "$*"; }
    ALL_CLUSTER_CIDRS=$(gcloud container clusters list --project $PROJECT_1 --format='value(clusterIpv4Cidr)' | sort | uniq)
    ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
    ALL_CLUSTER_NETTAGS=$(gcloud compute instances list --project $PROJECT_1 --format='value(tags.items.[0])' | sort | uniq)
    ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
    

    特定叢集

    下列指令允許 $CLUSTER_1 和 $CLUSTER_2 之間的通訊,且不會公開專案中的其他叢集。

    function join_by { local IFS="$1"; shift; echo "$*"; }
    ALL_CLUSTER_CIDRS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P container clusters list --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(clusterIpv4Cidr)'; done | sort | uniq)
    ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
    ALL_CLUSTER_NETTAGS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P compute instances list  --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(tags.items.[0])' ; done | sort | uniq)
    ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
    
  2. 建立防火牆規則。

    GKE

    gcloud compute firewall-rules create istio-multicluster-pods \
        --allow=tcp,udp,icmp,esp,ah,sctp \
        --direction=INGRESS \
        --priority=900 \
        --source-ranges="${ALL_CLUSTER_CIDRS}" \
        --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet \
        --network=YOUR_NETWORK
    

    Autopilot

    TAGS=""
    for CLUSTER in ${CLUSTER_1} ${CLUSTER_2}
    do
        TAGS+=$(gcloud compute firewall-rules list --filter="Name:$CLUSTER*" --format="value(targetTags)" | uniq) && TAGS+=","
    done
    TAGS=${TAGS::-1}
    echo "Network tags for pod ranges are $TAGS"
    
    gcloud compute firewall-rules create asm-multicluster-pods \
        --allow=tcp,udp,icmp,esp,ah,sctp \
        --network=gke-cluster-vpc \
        --direction=INGRESS \
        --priority=900 --network=VPC_NAME \
        --source-ranges="${ALL_CLUSTER_CIDRS}" \
        --target-tags=$TAGS
    

設定端點探索

使用宣告式 API 啟用公有或私有叢集之間的端點探索

使用機群 API 啟用代管 Cloud Service Mesh 時,系統會為這個叢集啟用端點探索功能。如果您使用其他工具佈建代管 Cloud Service Mesh,可以套用 "multicluster_mode":"connected" asm-options configmap 中的設定,手動啟用機群中公有或私有叢集的端點探索功能。在同一個機群中啟用這項設定的叢集,會自動啟用彼此之間的跨叢集服務探索功能。

如果您採用「受管理 (TD) 控制層實作」,這是設定多叢集端點探索的唯一方式。如果您採用「受管理 (Istiod)」實作,建議您使用這種方式設定。

繼續操作前,請先建立防火牆規則。

啟用

如果叢集中asm-options已存在 configmap,請為叢集啟用端點探索功能:

      kubectl patch configmap/asm-options -n istio-system --type merge -p '{"data":{"multicluster_mode":"connected"}}'

如果叢集中asm-options尚未有 configmap,請建立該項目並啟用叢集的端點探索功能:

      kubectl --context ${CTX_1} create configmap asm-options -n istio-system --from-file <(echo '{"data":{"multicluster_mode":"connected"}}')

停用

停用叢集的端點探索功能:

      kubectl patch configmap/asm-options -n istio-system --type merge -p '{"data":{"multicluster_mode":"manual"}}'

如果取消註冊機群叢集時未停用端點探索功能,叢集內可能會保留密鑰。您必須手動清理所有剩餘的密鑰。

  1. 執行下列指令,找出需要清除的密鑰:

    kubectl get secrets -n istio-system -l istio.io/owned-by=mesh.googleapis.com,istio/multiCluster=true
    
  2. 刪除每個密鑰:

    kubectl delete secret SECRET_NAME
    

    對每個剩餘的密鑰重複這個步驟。

驗證多叢集連線

本節說明如何將範例 HelloWorld 和 Sleep 服務部署至多叢集環境,確認跨叢集負載平衡功能正常運作。

設定範例目錄的變數

  1. 前往 asmcli 的下載位置,然後執行下列指令來設定 ASM_VERSION

    export ASM_VERSION="$(./asmcli --version)"
    
  2. 將工作資料夾設為您用來驗證跨叢集負載平衡是否正常運作的範例。範例位於 asmcli install 指令中指定的 --output_dir 目錄的子目錄中。在下列指令中,將 OUTPUT_DIR 變更為您在 --output_dir 中指定的目錄。

    export SAMPLES_DIR=OUTPUT_DIR/istio-${ASM_VERSION%+*}
    

啟用加入邊車容器功能

  1. 在每個叢集中建立範例命名空間。

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl create --context=${CTX} namespace sample
    done
    
  2. 啟用命名空間以進行插入作業。實際步驟取決於控制層實作方式。

    代管 (TD)

    1. 將預設插入標籤套用至命名空間:
    for CTX in ${CTX_1} ${CTX_2}
    do
       kubectl label --context=${CTX} namespace sample \
          istio.io/rev- istio-injection=enabled --overwrite
    done
    

    受管理 (Istiod)

    建議:執行下列指令,將預設插入標籤套用至命名空間:

     for CTX in ${CTX_1} ${CTX_2}
     do
        kubectl label --context=${CTX} namespace sample \
           istio.io/rev- istio-injection=enabled --overwrite
     done
    

    如果您是使用受管理 Istiod 控制層的現有使用者: 建議使用預設注入功能,但系統也支援以修訂版本為準的注入功能。請按照下列指示操作:

    1. 執行下列指令,找出可用的發布管道:

      kubectl -n istio-system get controlplanerevision
      

      輸出結果會與下列內容相似:

      NAME                AGE
      asm-managed-rapid   6d7h
      

      在輸出內容中,「NAME」欄下方的值是與 Cloud Service Mesh 版本可用發布管道對應的修訂版本標籤。

    2. 將修訂版本標籤套用至命名空間:

      for CTX in ${CTX_1} ${CTX_2}
      do
        kubectl label --context=${CTX} namespace sample \
           istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      done
      

安裝 HelloWorld 服務

  • 在兩個叢集中建立 HelloWorld 服務:

    kubectl create --context=${CTX_1} \
        -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    
    kubectl create --context=${CTX_2} \
        -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
        -l service=helloworld -n sample
    

將 HelloWorld v1 和 v2 部署至每個叢集

  1. 將 HelloWorld v1 部署至 CLUSTER_1,並將 v2 部署至 CLUSTER_2,這有助於稍後驗證跨叢集負載平衡:

    kubectl create --context=${CTX_1} \
      -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v1 -n sample
    kubectl create --context=${CTX_2} \
      -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \
      -l version=v2 -n sample
  2. 使用下列指令確認 HelloWorld v1 和 v2 是否正在執行。確認輸出內容與下圖類似:

    kubectl get pod --context=${CTX_1} -n sample
    NAME                            READY     STATUS    RESTARTS   AGE
    helloworld-v1-86f77cd7bd-cpxhv  2/2       Running   0          40s
    kubectl get pod --context=${CTX_2} -n sample
    NAME                            READY     STATUS    RESTARTS   AGE
    helloworld-v2-758dd55874-6x4t8  2/2       Running   0          40s

部署 Sleep 服務

  1. 將 Sleep 服務部署至兩個叢集。這個 Pod 會產生人工網路流量,以供示範:

    for CTX in ${CTX_1} ${CTX_2}
    do
        kubectl apply --context=${CTX} \
            -f ${SAMPLES_DIR}/samples/sleep/sleep.yaml -n sample
    done
    
  2. 等待每個叢集啟動 Sleep 服務。確認輸出內容與下圖類似:

    kubectl get pod --context=${CTX_1} -n sample -l app=sleep
    NAME                             READY   STATUS    RESTARTS   AGE
    sleep-754684654f-n6bzf           2/2     Running   0          5s
    kubectl get pod --context=${CTX_2} -n sample -l app=sleep
    NAME                             READY   STATUS    RESTARTS   AGE
    sleep-754684654f-dzl9j           2/2     Running   0          5s

驗證跨叢集負載平衡

多次呼叫 HelloWorld 服務,並檢查輸出內容,確認 v1 和 v2 交替回覆:

  1. 呼叫 HelloWorld 服務:

    kubectl exec --context="${CTX_1}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_1}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
    

    輸出結果會與下列內容相似:

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...
  2. 再次呼叫 HelloWorld 服務:

    kubectl exec --context="${CTX_2}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_2}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
    

    輸出結果會與下列內容相似:

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...

恭喜!您已驗證負載平衡的多叢集 Cloud Service Mesh!

將流量保留在叢集內

在某些情況下,預設的跨叢集負載平衡行為並不理想。如要將流量保留在「叢集本機」(也就是說,從 cluster-a 傳送的流量只會抵達 cluster-a 中的目的地),請使用 MeshConfig.serviceSettings 將主機名稱或萬用字元標示為 clusterLocal。

舉例來說,您可以為個別服務、特定命名空間中的所有服務,或網格中的所有服務強制執行叢集本機流量,如下所示:

每項服務

serviceSettings:
- settings:
    clusterLocal: true
  hosts:
  - "mysvc.myns.svc.cluster.local"

每個命名空間

serviceSettings:
- settings:
    clusterLocal: true
  hosts:
  - "*.myns.svc.cluster.local"

全球

serviceSettings:
- settings:
    clusterLocal: true
  hosts:
  - "*"

您也可以設定全域叢集本機規則並新增明確例外狀況 (可為特定或萬用字元),進一步調整服務存取權。在下列範例中,除了 myns 命名空間中的服務外,叢集中的所有服務都會保留叢集本機:

serviceSettings:
- settings:
    clusterLocal: true
  hosts:
  - "*"
- settings:
    clusterLocal: false
  hosts:
  - "*.myns.svc.cluster.local"

啟用本機叢集服務

  1. 檢查叢集中的 MeshConfig config map

    kubectl get configmap -n istio-system
    

    您應該會看到設定對應表,名稱為 istio-asm-managed、istio-asm-managed-rapid 或 istio-asm-managed-stable。

    如果您已從 ISTIOD 實作遷移至 TRAFFIC_DIRECTOR 實作,可能會看到多個設定對應。在這種情況下,您可以執行下列指令來判斷管道:

    kubectl get controlplanerevision -n istio-system
    

    您要選取的通道是已完成對帳的控制平面修訂版本通道。

  2. 更新 ConfigMap

    cat <<EOF > config.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: CONFIGMAP_NAME
      namespace: istio-system
    data:
      mesh: |-
        serviceSettings:
        - settings:
            clusterLocal: true
          hosts:
          - "*"
    EOF
    

    將 CONFIGMAP_NAME 替換為您在步驟 1 中找到的 ConfigMap 名稱,然後更新 ConfigMap。

    kubectl apply --context=${CTX_1} -f config.yaml
    
  3. 使用下列指令,確認本機叢集功能運作正常。使用 CTX_1 呼叫 HelloWorld 的輸出內容類似於:

    kubectl exec --context="${CTX_1}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_1}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
    

    輸出內容應會顯示「Only v1 is response」:

    Hello version: v1, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...
    

    如果使用 CTX_2 撥打 HelloWorld:

    kubectl exec --context="${CTX_2}" -n sample -c sleep \
        "$(kubectl get pod --context="${CTX_2}" -n sample -l \
        app=sleep -o jsonpath='{.items[0].metadata.name}')" \
        -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
    

    輸出內容應會顯示 v1 和 v2 交替回覆的內容。

    Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8
    Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv
    ...
    

清理 HelloWorld 服務

完成負載平衡驗證後,請從叢集中移除 HelloWorld 和 Sleep 服務。

kubectl delete ns sample --context ${CTX_1}
kubectl delete ns sample --context ${CTX_2}

設定地區負載平衡

透過 Cloud Service Mesh,您可以運用主要路由行為,控管流量在不同區域和可用區的分配方式:

  • SPRAY_TO_WORLD (預設):流量會以循環方式分配給全球所有健康狀態良好的端點,無論用戶端位於何處。
  • WATERFALL_BY_ZONE:流量會轉送至最近的健康區域。如果本機區域不正常,流量會轉送至次近的正常區域。

本指南說明如何使用 Istio DestinationRule 和 MeshConfig 資源設定這些行為。

啟用刊登序列路由

如要觸發 WATERFALL_BY_ZONE,使用者必須在 DestinationRule (或全域的 MeshConfig) 中設定 DestinationRule.TrafficPolicy.outlierDetection AND 下列其中一項:

  • localityLbSetting.enabled為true
  • localityLbSetting.failover.from
  • localityLbSetting.failover.to

使用 DestinationRule 啟用刊登序列

以下說明如何使用 DestinationRule,為特定服務成功啟用 Waterfall 路由:

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: backend-waterfall-rule
spec:
  host: my-backend.default.svc.cluster.local
  trafficPolicy:
    outlierDetection: {}
    loadBalancer:
      localityLbSetting:
        enabled: true # or failover: []

使用 MeshConfig + DestinationRule 啟用刊登序列

或者,您也可以使用 MeshConfig (例如在 istio-asm-managed ConfigMap 中),為叢集中的整個網格全域啟用區域負載平衡。將 localityLbSetting 套用至整個網格時,個別 DestinationRule 只需要空白的 outlierDetection 區塊,即可觸發 Waterfall 路由。

下列 YAML 檔案提供此設定的範例 MeshConfig 和 DestinationRule:

MeshConfig (全球):

apiVersion: v1
kind: ConfigMap
metadata:
  name: istio-asm-managed
  namespace: istio-system
data:
  mesh: |-
    localityLbSetting:
      enabled: true # or failover: []

DestinationRule (服務專屬):

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: backend-waterfall-rule
spec:
  host: my-backend.default.svc.cluster.local
  trafficPolicy:
    outlierDetection: {}

為每個工作負載版本套用地域設定

由於 DestinationRule 不支援子集專屬和通訊埠專屬的地區設定,因此您可以透過多個 Kubernetes 服務定義提供各項服務的轉送設定,以達成混合地區行為。