在 GKE 上设置多集群网格

本指南介绍如何使用 Mesh CAIstio CA 将两个集群并入单个 Cloud Service Mesh 并启用跨集群负载均衡。您可以轻松扩展此流程,将任意数量的集群整合到网格中。

多集群 Cloud Service Mesh 配置可以满足一些重要企业场景,例如扩缩、位置和隔离。如需了解详情,请参阅多集群用例

前提条件

本指南假定您有两个或多个满足以下要求的 Google Cloud GKE 集群:

设置项目和集群变量

  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来启用该功能,还是要针对公共集群专用集群手动启用该功能。

在公共集群之间配置端点发现

如需在 GKE 集群之间配置端点发现,可以运行 asmcli create-mesh。此命令会执行以下操作:

  • 将所有集群注册到同一舰队。
  • 将网格配置为信任舰队工作负载身份。
  • 创建远程 Secret。

您可以指定每个集群的 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. 将新 Secret 应用到集群中:

    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. 将工作文件夹设置为用于验证跨集群负载均衡是否正常工作的示例。这些示例位于您在 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. 在创建的命名空间中启用 Sidecar 注入。

    推荐:运行以下命令可将默认注入标签应用于命名空间:

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

    我们建议您使用默认注入,但也支持基于修订版本的注入:请按照以下说明操作:

    1. 使用以下命令查找 istiod 的修订版本标签:

      kubectl get deploy -n istio-system -l app=istiod -o \
          jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. 将修订版本标签应用于命名空间。在以下命令中,REVISION_LABEL 是您在上一步中记下的 istiod 修订版本标签的值。

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