在 GKE 中設定多叢集網格

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

多叢集 Cloud Service Mesh 設定可解決多個重要的企業情境,例如規模、位置和隔離。詳情請參閱多叢集應用實例

必要條件

請務必先準備好兩個以上的 GKE 叢集,並符合下列要求,再按照本指南的說明操作: Google Cloud

設定專案和叢集變數

  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 啟用公有或私有叢集之間的端點探索 (搶先版)

如要在車隊中啟用公有或私有叢集的端點探索功能,請在 asm-options configmap 中套用 "multicluster_mode":"connected" 設定。在同一個機群中啟用這項設定的叢集,會自動啟用彼此之間的跨叢集服務探索功能。

所有發布管道的代管 Cloud Service Mesh 安裝作業都適用這個方法。如果您在 Google Cloud console 中建立新的 GKE 叢集時,使用「啟用 Cloud Service Mesh」功能佈建代管 Cloud Service Mesh,這也是設定多叢集端點探索的首選方式。

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

如果是多個專案,您必須手動將 FLEET_PROJECT_ID.svc.id.goog 新增至修訂版本的 meshConfig 中的 trustDomainAliases (如果尚未新增)。

啟用

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

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

如果叢集中asm-options尚未有 configmap ,請建立 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
    

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

設定公開叢集之間的端點探索

如要在 GKE 叢集之間設定端點探索功能,請執行 asmcli create-mesh。此指令會執行下列作業:

  • 將所有叢集註冊至同一個機群。
  • 設定網格,以信任 Fleet Workload Identity。
  • 建立遠端密鑰。

您可以指定每個叢集的 URI,或 kubeconfig 檔案的路徑。

叢集 URI

在下列指令中,將 FLEET_PROJECT_ID 替換為車隊主專案的專案 ID,並將叢集 URI 替換為每個叢集的叢集名稱、區域/地區和專案 ID。這個範例只顯示兩個叢集,但您可以執行指令,在其他叢集上啟用端點探索功能,但須遵守可新增至機群的叢集數量上限

./asmcli create-mesh \
    FLEET_PROJECT_ID \
    ${PROJECT_1}/${LOCATION_1}/${CLUSTER_1} \
    ${PROJECT_2}/${LOCATION_2}/${CLUSTER_2}

kubeconfig 檔案

在下列指令中,將 FLEET_PROJECT_ID 替換為車隊主專案的專案 ID,並將 PATH_TO_KUBECONFIG 替換為每個 kubeconfig 檔案的路徑。這個範例只顯示兩個叢集,但您可以執行指令,在其他叢集上啟用端點探索功能,但須遵守可新增至機群的叢集數量上限

./asmcli create-mesh \
    FLEET_PROJECT_ID \
    PATH_TO_KUBECONFIG_1 \
    PATH_TO_KUBECONFIG_2

設定私人叢集之間的端點探索

  1. 設定遠端 Secret,允許 API 伺服器存取叢集,存取其他叢集的 Cloud Service Mesh 控制層。指令取決於您的 Cloud Service Mesh 類型 (叢集內或代管):

    A. 如果是叢內 Cloud Service Mesh,您必須設定私人 IP,而非公開 IP,因為公開 IP 無法存取:

    PRIV_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \
     --zone "${LOCATION_1}" --format "value(privateClusterConfig.privateEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PRIV_IP} > ${CTX_1}.secret
    
    PRIV_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \
     --zone "${LOCATION_2}" --format "value(privateClusterConfig.privateEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PRIV_IP} > ${CTX_2}.secret
    

    B. 如果是代管 Cloud Service Mesh:

    PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \
     --zone "${LOCATION_1}" --format "value(privateClusterConfig.publicEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PUBLIC_IP} > ${CTX_1}.secret
    
    PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \
     --zone "${LOCATION_2}" --format "value(privateClusterConfig.publicEndpoint)"`
    
    ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PUBLIC_IP} > ${CTX_2}.secret
    
  2. 將新密鑰套用至叢集:

    kubectl apply -f ${CTX_1}.secret --context=${CTX_2}
    
    kubectl apply -f ${CTX_2}.secret --context=${CTX_1}
    

為私人叢集設定授權網路

只有在網狀網路符合下列所有條件時,才適用本節內容:

部署多個私人叢集時,每個叢集中的 Cloud Service Mesh 控制層都需要呼叫遠端叢集的 GKE 控制層。如要允許流量,您需要在呼叫叢集的 Pod 位址範圍中,新增遠端叢集的授權網路。

  1. 取得每個叢集的 Pod IP CIDR 區塊:

    POD_IP_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
      --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
    
    POD_IP_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
      --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
    
  2. 將 Kubernetes 叢集 Pod IP CIDR 區塊新增至遠端叢集:

    EXISTING_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"`
    gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
    --enable-master-authorized-networks \
    --master-authorized-networks ${POD_IP_CIDR_2},${EXISTING_CIDR_1//;/,}
    
    EXISTING_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"`
    gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
    --enable-master-authorized-networks \
    --master-authorized-networks ${POD_IP_CIDR_1},${EXISTING_CIDR_2//;/,}
    

    詳情請參閱「建立具有授權網路的叢集」。

  3. 確認授權網路已更新:

    gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
    
    gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
    

啟用控制層全域存取權

只有在網狀網路符合下列所有條件時,才適用本節內容:

  • 您使用的是私人叢集。
  • 您為網格中的叢集使用不同區域。

您必須啟用控制層全域存取權,才能允許每個叢集中的 Cloud Service Mesh 控制層呼叫遠端叢集的 GKE 控制層。

  1. 啟用控制層全域存取權:

    gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \
     --enable-master-global-access
    
    gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \
     --enable-master-global-access
    
  2. 確認已啟用控制層全域存取權:

    gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1}
    
    gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2}
    

    輸出內容中的 privateClusterConfig 部分會顯示 masterGlobalAccessConfig 的狀態。

驗證多叢集連線

本節說明如何將範例 HelloWorldSleep 服務部署至多叢集環境,以驗證跨叢集負載平衡是否正常運作。

設定範例目錄的變數

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

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

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

啟用 Sidecar 注入功能

  1. 找出修訂版本標籤值,後續步驟會用到。這個步驟取決於 Cloud Service Mesh 類型 (代管或叢集內)。

    受管理

    使用下列指令找出修訂版本標籤,您會在後續步驟中使用該標籤。

    kubectl get controlplanerevision -n istio-system

    輸出看起來類似以下內容:

     NAME                RECONCILED   STALLED   AGE
     asm-managed-rapid   True         False     89d
     

    在輸出內容的「NAME」欄下方,記下修訂版本標籤的值。在本範例中,這個值為 asm-managed-rapid。在下一節的步驟中,使用修訂版本值。

    叢集內

    使用下列指令找出修訂版本標籤,您會在後續步驟中使用該標籤。

    kubectl -n istio-system get pods -l app=istiod --show-labels

    輸出看起來類似以下內容:

     NAME                                READY   STATUS    RESTARTS   AGE   LABELS
     istiod-asm-173-3-5788d57586-bljj4   1/1     Running   0          23h   app=istiod,istio.io/rev=asm-173-3,istio=istiod,pod-template-hash=5788d57586
     istiod-asm-173-3-5788d57586-vsklm   1/1     Running   1          23h   app=istiod,istio.io/rev=asm-173-3,istio=istiod,pod-template-hash=5788d57586
     

    在輸出內容的 LABELS 欄下方,記下 istiod 修訂版本標籤的值,該值位於 istio.io/rev= 前置字串後方。在本範例中,這個值為 asm-173-3。在下一節的步驟中,請使用修訂版本值。

安裝 HelloWorld 服務

  1. 在每個叢集中建立範例命名空間和服務定義。在下列指令中,將 REVISION 替換為您在上一步記下的istiod修訂版本標籤。

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

    其中 REVISION 是您先前記下的 istiod 修訂版本標籤。

    輸出內容會如下所示:

    label "istio-injection" not found.
    namespace/sample labeled
    

    您可以放心忽略 label "istio-injection" not found.

  2. 在兩個叢集中建立 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_1v2CLUSTER_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 v1v2 是否正在執行。確認輸出內容與顯示的內容類似:

    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!

清除 HelloWorld 服務

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

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