使用 Cloud Service Mesh 在 GKE 专用集群上运行分布式服务

本文档介绍了如何使用 Cloud Service Mesh 在 Google Cloud 中的多个 Google Kubernetes Engine (GKE) 集群上运行分布式服务。本文档还介绍了如何使用多集群 Ingress 和 Cloud Service Mesh 公开分布式服务。您可以使用本文档配置非专用 GKE 集群;本文档重点介绍了专门用于专用集群的配置。

本文档适用于对 Kubernetes 有基本了解的平台管理员和服务操作人员。了解一些服务网格是有益的,但这不是必需的。Cloud Service Mesh 基于开源 Istio 技术。如需详细了解服务网格和 Istio,请参阅 istio.io

分布式服务是作为单个逻辑服务运行的 Kubernetes 服务,分布式服务比 Kubernetes 服务更为灵活,因为它们在同一命名空间内的多个 Kubernetes 集群上运行。即使一个或多个 GKE 集群已关闭,只要运行状况良好的集群能够处理所需的负载,分布式服务仍会保持开启。

只有运行它们的集群的 Kubernetes API 服务器知道 Kubernetes Service。如果 Kubernetes 集群关闭(例如在计划性维护期间),则在该集群上运行的所有 Kubernetes 服务也会关闭。运行分布式服务可以简化集群生命周期管理,因为您可以关闭集群以进行维护或升级,而其他集群处理流量。为了创建分布式服务,Cloud Service Mesh 提供的服务网格功能用于将在多个集群上运行的服务链接在一起,以充当单个逻辑服务。

GKE 专用集群可让您将节点和 API 服务器配置为仅在 Virtual Private Cloud (VPC) 网络中可用的专用资源。在 GKE 专用集群中运行分布式服务可为企业提供安全可靠的服务。

架构

本教程使用如下图所示的架构:

在 GKE 专用集群上使用 Cloud Service Mesh 的分布式服务架构

在上图中,架构包含以下集群:

  • 两个集群(gke-central-privgke-west-priv)在两个不同的区域中充当相同的 GKE 专用集群。
  • 单独的集群 (ingress-config) 充当配置多集群 Ingress 的控制层面集群。

在本教程中,您将在两个 GKE 专用集群(gke-central-privgke-west-priv)上部署 Bank of Anthos 示例应用。Bank of Anthos 是一个示例微服务应用,由多个微服务和 SQL 数据库组成,模拟网上银行应用。该应用由客户端可以访问的 Web 前端和多个后端服务(例如余额、分类账和模拟银行的账户服务)组成。

该应用包括两个作为 StatefulSet 安装在 Kubernetes 中的 PostgreSQL 数据库。一个数据库用于事务,另一个数据库用于用户账号。除两个数据库以外的所有服务均作为分布式服务运行。这意味着所有服务的 Pod 都在两个应用集群(位于同一命名空间)中运行,并且 Cloud Service Mesh 已经过配置,使每项服务都显示为单个逻辑服务。

目标

  • 创建三个 GKE 集群。
  • 将两个 GKE 集群配置为专用集群(gke-central-privgke-west-priv)。
  • 配置一个 GKE 集群 (ingress-config) 作为中央配置集群。 此集群充当多集群 Ingress 的配置集群。
  • 配置网络(NAT 网关、Cloud Router 和防火墙规则)以允许来自两个专用 GKE 集群的集群间和出站流量。
  • 配置授权网络以允许 API 服务从 Cloud Shell 访问两个专用 GKE 集群。
  • 在多主模式下为两个专用集群部署和配置多集群 Cloud Service Mesh。多主模式会在两个集群中部署 Cloud Service Mesh 控制平面。
  • 在两个专用集群上部署 Bank of Anthos 应用。除数据库之外的所有服务均部署为分布式服务(两个专用集群上运行的 Pod)。
  • 使用 Cloud Service Mesh 监控服务。
  • 在 Bank of Anthos frontend 服务上配置多集群 Ingress。这允许外部客户端(例如您的网络浏览器)访问一组专用 GKE 集群上运行的分布式服务。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

准备工作

  1. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  2. 确保您的 Google Cloud 项目已启用结算功能

  3. 在 Google Cloud 控制台中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。

    您可以从 Cloud Shell 运行本教程中的所有命令。

  4. 定义本教程中使用的环境变量。这些变量定义了本教程中使用的集群名称、区域、地区、IP 寻址和 Cloud Service Mesh 版本。

    1. 请将 YOUR_PROJECT_ID 替换为您的项目 ID:

      export PROJECT_ID=YOUR_PROJECT_ID
      gcloud config set project ${PROJECT_ID}
      
    2. 设置其余的环境变量:

      export CLUSTER_1=gke-west-priv
      export CLUSTER_2=gke-central-priv
      export CLUSTER_1_ZONE=us-west2-a
      export CLUSTER_1_REGION=us-west2
      export CLUSTER_1_MASTER_IPV4_CIDR=172.16.0.0/28
      export CLUSTER_2_ZONE=us-central1-a
      export CLUSTER_2_REGION=us-central1
      export CLUSTER_2_MASTER_IPV4_CIDR=172.16.1.0/28
      export CLUSTER_INGRESS=gke-ingress
      export CLUSTER_INGRESS_ZONE=us-west1-a
      export CLUSTER_INGRESS_REGION=us-west1
      export CLUSTER_INGRESS_MASTER_IPV4_CIDR=172.16.2.0/28
      export WORKLOAD_POOL=${PROJECT_ID}.svc.id.goog
      export ASM_VERSION=1.10
      export CLOUDSHELL_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
      

准备环境

  1. 在 Cloud Shell 中,启用以下 API:

    gcloud services enable \
      --project=${PROJECT_ID} \
      container.googleapis.com \
      mesh.googleapis.com \
      gkehub.googleapis.com
    
  2. 为您的项目启用 Cloud Service Mesh 舰队:

    gcloud container fleet mesh enable --project=${PROJECT_ID}
    

为专用 GKE 集群准备网络

在本部分中,您将为用于运行分布式服务的专用 GKE 集群准备网络。

专用 GKE 集群节点不会分配公共 IP 地址。专用 GKE 集群中的所有节点都会分配一个专用 VPC IP 地址(RFC 1918 地址空间中)。这意味着需要访问外部资源(VPC 网络外部)的 Pod 需要 Cloud NAT 网关。Cloud NAT 网关是地区性 NAT 网关,允许具有内部 IP 地址的 Pod 与互联网进行通信。在本教程中,您将在两个区域中分别配置 Cloud NAT 网关。一个区域内的多个集群可以使用同一个 NAT 网关。

  1. 在 Cloud Shell 中,为两个 NAT 网关创建并预留两个外部 IP 地址:

    gcloud compute addresses create ${CLUSTER_1_REGION}-nat-ip \
      --project=${PROJECT_ID} \
      --region=${CLUSTER_1_REGION}
    
    gcloud compute addresses create ${CLUSTER_2_REGION}-nat-ip \
      --project=${PROJECT_ID} \
      --region=${CLUSTER_2_REGION}
    
  2. 将 IP 地址和 IP 地址的名称存储在变量中:

    export NAT_REGION_1_IP_ADDR=$(gcloud compute addresses describe ${CLUSTER_1_REGION}-nat-ip \
      --project=${PROJECT_ID} \
      --region=${CLUSTER_1_REGION} \
      --format='value(address)')
    
    export NAT_REGION_1_IP_NAME=$(gcloud compute addresses describe ${CLUSTER_1_REGION}-nat-ip \
      --project=${PROJECT_ID} \
      --region=${CLUSTER_1_REGION} \
      --format='value(name)')
    
    export NAT_REGION_2_IP_ADDR=$(gcloud compute addresses describe ${CLUSTER_2_REGION}-nat-ip \
      --project=${PROJECT_ID} \
      --region=${CLUSTER_2_REGION} \
      --format='value(address)')
    
    export NAT_REGION_2_IP_NAME=$(gcloud compute addresses describe ${CLUSTER_2_REGION}-nat-ip \
      --project=${PROJECT_ID} \
      --region=${CLUSTER_2_REGION} \
      --format='value(name)')
    
  3. 在专用 GKE 集群的两个区域中创建 Cloud NAT 网关:

    gcloud compute routers create rtr-${CLUSTER_1_REGION} \
      --network=default \
      --region ${CLUSTER_1_REGION}
    
    gcloud compute routers nats create nat-gw-${CLUSTER_1_REGION} \
      --router=rtr-${CLUSTER_1_REGION} \
      --region ${CLUSTER_1_REGION} \
      --nat-external-ip-pool=${NAT_REGION_1_IP_NAME} \
      --nat-all-subnet-ip-ranges \
      --enable-logging
    
    gcloud compute routers create rtr-${CLUSTER_2_REGION} \
      --network=default \
      --region ${CLUSTER_2_REGION}
    
    gcloud compute routers nats create nat-gw-${CLUSTER_2_REGION} \
      --router=rtr-${CLUSTER_2_REGION} \
      --region ${CLUSTER_2_REGION} \
      --nat-external-ip-pool=${NAT_REGION_2_IP_NAME} \
      --nat-all-subnet-ip-ranges \
      --enable-logging
    
  4. 创建一条防火墙规则,以允许 Pod 到 Pod 的通信和 Pod 到 API 的服务器通信。Pod 到 Pod 的通信可让分布式服务跨 GKE 集群相互通信。Pod 到 API 服务器通信允许 Cloud Service Mesh 控制平面查询 GKE 集群以进行服务发现。

    gcloud compute firewall-rules create all-pods-and-master-ipv4-cidrs \
      --project ${PROJECT_ID} \
      --network default \
      --allow all \
      --direction INGRESS \
      --source-ranges 10.0.0.0/8,${CLUSTER_1_MASTER_IPV4_CIDR},${CLUSTER_2_MASTER_IPV4_CIDR},${CLUSTER_INGRESS_MASTER_IPV4_CIDR}
    

网络现已准备就绪。在本教程中,您将使用整个 10.0.0.0/8 IP 地址范围,其中包括所有 Pod 范围。我们建议您根据条件和要求在生产环境中创建更严格的防火墙规则。

创建专用 GKE 集群

在本部分中,您将创建部署了示例应用的两个专用 GKE 集群。在本教程中,专用 GKE 集群节点具有专用 IP 地址,并且 API 服务器具有一个公共端点。但是,对 API 服务器的访问仅限于使用已获授权的网络。

  1. 在 Cloud Shell 中,创建两个具有授权网络的专用集群。将集群配置为允许从 Pod IP CIDR 范围(针对 Cloud Service Mesh 控制平面)和 Cloud Shell 进行访问,以便您可以从终端访问集群。

    gcloud container clusters create ${CLUSTER_1} \
      --project ${PROJECT_ID} \
      --zone=${CLUSTER_1_ZONE} \
      --machine-type "e2-standard-4" \
      --num-nodes "3" --min-nodes "3" --max-nodes "5" \
      --enable-ip-alias --enable-autoscaling \
      --workload-pool=${WORKLOAD_POOL} \
      --enable-private-nodes \
      --master-ipv4-cidr=${CLUSTER_1_MASTER_IPV4_CIDR} \
      --enable-master-authorized-networks \
      --master-authorized-networks $NAT_REGION_1_IP_ADDR/32,$NAT_REGION_2_IP_ADDR/32,$CLOUDSHELL_IP/32
    
    gcloud container clusters create ${CLUSTER_2} \
      --project ${PROJECT_ID} \
      --zone=${CLUSTER_2_ZONE} \
      --machine-type "e2-standard-4" \
      --num-nodes "3" --min-nodes "3" --max-nodes "5" \
      --enable-ip-alias --enable-autoscaling \
      --workload-pool=${WORKLOAD_POOL} \
      --enable-private-nodes \
      --master-ipv4-cidr=${CLUSTER_2_MASTER_IPV4_CIDR} \
      --enable-master-authorized-networks \
      --master-authorized-networks $NAT_REGION_1_IP_ADDR/32,$NAT_REGION_2_IP_ADDR/32,$CLOUDSHELL_IP/32
    

    已获授权的网络包含 Cloud NAT 网关上的公共 IP 地址。由于专用集群的 API 服务器端点是公共端点,因此专用集群中运行的 Pod 必须使用 Cloud NAT 网关访问公共 API 服务器端点。

    Cloud Shell IP 地址也是授权网络的一部分,可让您从 Cloud Shell 终端访问和管理集群。Cloud Shell 的公开 IP 地址是动态的,因此每次启动 Cloud Shell 时,您都可能获得不同的公共 IP 地址。获得新 IP 地址后,您将失去对集群的访问权限,因为新 IP 地址不属于两个集群的授权网络的一部分。

    如果您无法访问集群,请更新集群已获授权的网络以包含新的 Cloud Shell IP 地址:

    1. 获取更新后的 Cloud Shell 公共 IP 地址:

      export CLOUDSHELL_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
      
    2. 更新两个集群的授权网络:

      gcloud container clusters update ${CLUSTER_1} \
        --zone=${CLUSTER_1_ZONE} \
        --enable-master-authorized-networks \
        --master-authorized-networks $NAT_REGION_1_IP_ADDR/32,$NAT_REGION_2_IP_ADDR/32,$CLOUDSHELL_IP/32
      
      gcloud container clusters update ${CLUSTER_2} \
        --zone=${CLUSTER_2_ZONE} \
        --enable-master-authorized-networks \
        --master-authorized-networks $NAT_REGION_1_IP_ADDR/32,$NAT_REGION_2_IP_ADDR/32,$CLOUDSHELL_IP/32
      
  2. 验证所有集群是否正在运行:

    gcloud container clusters list
    

    输出如下所示:

    NAME              LOCATION       MASTER_VERSION    MASTER_IP      MACHINE_TYPE   NODE_VERSION      NUM_NODES  STATUS
    gke-central-priv  us-central1-a  1.16.15-gke.6000  35.238.99.104  e2-standard-4  1.16.15-gke.6000  3          RUNNING
    gke-west-priv     us-west2-a     1.16.15-gke.6000  34.94.188.180  e2-standard-4  1.16.15-gke.6000  3          RUNNING
    
  3. 连接到这两个集群,以在 kubeconfig 文件中生成相关条目:

    touch ~/asm-kubeconfig && export KUBECONFIG=~/asm-kubeconfig
    gcloud container clusters get-credentials ${CLUSTER_1} --zone ${CLUSTER_1_ZONE}
    gcloud container clusters get-credentials ${CLUSTER_2} --zone ${CLUSTER_2_ZONE}
    

    您可以使用 kubeconfig 文件为每个集群创建用户和上下文,从而向集群进行身份验证。在 kubeconfig 文件中生成条目后,您可以快速切换集群之间的上下文。

  4. 为方便起见,请重命名集群上下文:

    kubectl config rename-context \
    gke_${PROJECT_ID}_${CLUSTER_1_ZONE}_${CLUSTER_1} ${CLUSTER_1}
    
    kubectl config rename-context \
    gke_${PROJECT_ID}_${CLUSTER_2_ZONE}_${CLUSTER_2} ${CLUSTER_2}
    
  5. 确认这两个集群上下文已正确重命名和配置:

    kubectl config get-contexts --output="name"
    

    输出如下所示:

    gke-central-priv
    gke-west-priv
    
  6. 向队列注册集群:

    gcloud container fleet memberships register ${CLUSTER_1} --gke-cluster=${CLUSTER_1_ZONE}/${CLUSTER_1} --enable-workload-identity
    gcloud container fleet memberships register ${CLUSTER_2} --gke-cluster=${CLUSTER_2_ZONE}/${CLUSTER_2} --enable-workload-identity
    

现在,您已创建并重命名专用 GKE 集群。

安装 Cloud Service Mesh

在本部分中,您将在两个 GKE 集群上安装 Cloud Service Mesh,并配置集群以实现跨集群服务发现。

  1. 在 Cloud Shell 中,使用 fleet API 在这两个集群上安装 Cloud Service Mesh:

    gcloud container fleet mesh update --management automatic --memberships ${CLUSTER_1},${CLUSTER_2}
    
  2. 在集群上启用代管式 Cloud Service Mesh 后,请为要安装的网格设置监视:

    watch -g "gcloud container fleet mesh describe | grep 'code: REVISION_READY'"
    
  3. 为这两个集群安装 Cloud Service Mesh 入站流量网关:

    kubectl --context=${CLUSTER_1} create namespace asm-ingress
    kubectl --context=${CLUSTER_1} label namespace asm-ingress istio-injection=enabled --overwrite
    kubectl --context=${CLUSTER_2} create namespace asm-ingress
    kubectl --context=${CLUSTER_2} label namespace asm-ingress istio-injection=enabled --overwrite
    
    cat <<'EOF' > asm-ingress.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    spec:
      type: LoadBalancer
      selector:
        asm: ingressgateway
      ports:
      - port: 80
        name: http
      - port: 443
        name: https
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    spec:
      selector:
        matchLabels:
          asm: ingressgateway
      template:
        metadata:
          annotations:
            # This is required to tell Anthos Service Mesh to inject the gateway with the
            # required configuration.
            inject.istio.io/templates: gateway
          labels:
            asm: ingressgateway
        spec:
          containers:
          - name: istio-proxy
            image: auto # The image will automatically update each time the pod starts.
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: asm-ingressgateway-sds
      namespace: asm-ingress
    rules:
    - apiGroups: [""]
      resources: ["secrets"]
      verbs: ["get", "watch", "list"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: asm-ingressgateway-sds
      namespace: asm-ingress
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: asm-ingressgateway-sds
    subjects:
    - kind: ServiceAccount
      name: default
    EOF
    
    kubectl --context=${CLUSTER_1} apply -f asm-ingress.yaml
    kubectl --context=${CLUSTER_2} apply -f asm-ingress.yaml
    
  4. 验证是否已部署 Cloud Service Mesh 入站流量网关:

    kubectl --context=${CLUSTER_1} get pod,service -n asm-ingress
    kubectl --context=${CLUSTER_2} get pod,service -n asm-ingress
    

    这两个集群的输出如下所示:

    NAME                                        READY   STATUS    RESTARTS   AGE
    pod/asm-ingressgateway-5894744dbd-zxlgc   1/1     Running   0          84s
    
    NAME                           TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)                      AGE
    service/asm-ingressgateway   LoadBalancer   10.16.2.131   34.102.100.138   80:30432/TCP,443:30537/TCP   92s
    

    为这两个集群安装 Cloud Service Mesh 控制平面和入站流量网关后,系统将使用舰队 API 启用跨集群服务发现。跨集群服务发现可让两个集群从远程集群中发现服务端点。分布式服务在同一命名空间中的多个集群上运行。

    为了让这两个 Cloud Service Mesh 控制平面能够发现分布式服务的所有端点,Cloud Service Mesh 必须有权访问运行分布式服务的所有集群。此示例使用两个集群,因此两个集群都必须能够查询远程集群的服务端点。通过舰队 API 启用代管式 Cloud Service Mesh,系统会自动配置端点发现。

集群和 Cloud Service Mesh 现已配置完毕。

部署 Bank of Anthos 应用

  1. 在 Cloud Shell 中,克隆 Anthos Bank 代码库:

    git clone https://github.com/GoogleCloudPlatform/bank-of-anthos.git ${HOME}/bank-of-anthos
    
  2. 在这两个集群中创建和标记 bank-of-anthos 命名空间。此标签允许自动在已加标签的命名空间内的每个 Pod 中注入 Sidecar Envoy 代理。

    # cluster_1
    kubectl create --context=${CLUSTER_1} namespace bank-of-anthos
    kubectl label --context=${CLUSTER_1} namespace bank-of-anthos istio-injection=enabled
    
    # cluster_2
    kubectl create --context=${CLUSTER_2} namespace bank-of-anthos
    kubectl label --context=${CLUSTER_2} namespace bank-of-anthos istio-injection=enabled
    
  3. 将 Anthos 银行应用部署到 bank-of-anthos 命名空间中的两个集群。

    # The following secret is used for user account creation and authentication
    kubectl --context=$CLUSTER_1 -n bank-of-anthos apply -f ${HOME}/bank-of-anthos/extras/jwt/jwt-secret.yaml
    kubectl --context=$CLUSTER_2 -n bank-of-anthos apply -f ${HOME}/bank-of-anthos/extras/jwt/jwt-secret.yaml
    
    # Deploy all manifests to both clusters
    kubectl --context=$CLUSTER_1 -n bank-of-anthos apply -f ${HOME}/bank-of-anthos/kubernetes-manifests
    kubectl --context=$CLUSTER_2 -n bank-of-anthos apply -f ${HOME}/bank-of-anthos/kubernetes-manifests
    

    Kubernetes 服务需要同时位于两个集群中,才能实现服务发现。当其中一个集群中的服务尝试发出请求时,它首先对主机名执行 DNS 查找以获取 IP 地址。在 GKE 中,集群中运行的 kube-dns 服务器处理此查找,因此需要已配置的 Service 定义。

  4. 从一个集群中删除 StatefulSets,以便这两个 PostgreSQL 数据库仅存在于其中一个集群中:

    # Delete the two DB statefulSets from Cluster2
    kubectl --context=$CLUSTER_2 -n bank-of-anthos delete statefulset accounts-db
    kubectl --context=$CLUSTER_2 -n bank-of-anthos delete statefulset ledger-db
    
  5. 确保所有 Pod 都在两个集群中运行:

    1. cluster_1 获取 Pod:

      kubectl --context=${CLUSTER_1} -n bank-of-anthos get pod
      

      输出如下所示:

      NAME                                  READY   STATUS    RESTARTS   AGE
      accounts-db-0                         2/2     Running   0          9m54s
      balancereader-c5d664b4c-xmkrr         2/2     Running   0          9m54s
      contacts-7fd8c5fb6-wg9xn              2/2     Running   1          9m53s
      frontend-7b7fb9b665-m7cw7             2/2     Running   1          9m53s
      ledger-db-0                           2/2     Running   0          9m53s
      ledgerwriter-7b5b6db66f-xhbp4         2/2     Running   0          9m53s
      loadgenerator-7fb54d57f8-g5lz5        2/2     Running   0          9m52s
      transactionhistory-7fdb998c5f-vqh5w   2/2     Running   1          9m52s
      userservice-76996974f5-4wlpf          2/2     Running   1          9m52s
      
    2. cluster_2 获取 Pod:

      kubectl --context=${CLUSTER_2} -n bank-of-anthos get pod
      

      输出如下所示:

      NAME                                  READY   STATUS    RESTARTS   AGE
      balancereader-c5d664b4c-bn2pl         2/2     Running   0          9m54s
      contacts-7fd8c5fb6-kv8cp              2/2     Running   0          9m53s
      frontend-7b7fb9b665-bdpp4             2/2     Running   0          9m53s
      ledgerwriter-7b5b6db66f-297c2         2/2     Running   0          9m52s
      loadgenerator-7fb54d57f8-tj44v        2/2     Running   0          9m52s
      transactionhistory-7fdb998c5f-xvmtn   2/2     Running   0          9m52s
      userservice-76996974f5-mg7t6          2/2     Running   0          9m51s
      
  6. 将 Cloud Service Mesh 配置部署到这两个集群。这将在 asm-ingress 命名空间中为 frontend 服务创建 Gateway,并在 bank-of-anthos 命名空间中为 frontend 服务创建 VirtualService,以便您允许入站流量传入 frontend 服务。

    Gateways 通常由平台管理员或网络管理员团队拥有。因此,Gateway 资源在由平台管理员负责管理的 Ingress Gateway 命名空间中创建,可通过他们自己的 VirtualService 条目在其他命名空间中使用。这是一种“共享网关”模型。

    cat <<'EOF' > asm-vs-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: asm-ingressgateway
      namespace: asm-ingress
    spec:
      selector:
        asm: ingressgateway
      servers:
        - port:
            number: 80
            name: http
            protocol: HTTP
          hosts:
            - "*"
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: frontend
      namespace: bank-of-anthos
    spec:
      hosts:
      - "*"
      gateways:
      - asm-ingress/asm-ingressgateway
      http:
      - route:
        - destination:
            host: frontend
            port:
              number: 80
    EOF
    
    kubectl --context=$CLUSTER_1 apply -f asm-vs-gateway.yaml
    
    kubectl --context=$CLUSTER_2 apply -f asm-vs-gateway.yaml
    

您现在已在两个专用 GKE 集群上部署了 Bank of Anthos 应用。除数据库之外,所有服务都作为分布式服务运行。

检查分布式服务

在本部分中,您将使用 istioctl 工具检查任意代理的 proxy-config。这样,您就可以看到边车代理查看每项服务的两个 Pod,每个集群中运行一个 Pod。

  1. 在 Cloud Shell 中,检查 cluster_1frontend Pod 的 proxy-config 端点列表:

    export FRONTEND1=$(kubectl get pod -n bank-of-anthos -l app=frontend \
      --context=${CLUSTER_1} -o jsonpath='{.items[0].metadata.name}')
    istioctl proxy-config endpoints \
    --context $CLUSTER_1 -n bank-of-anthos $FRONTEND1 | grep bank-of-anthos
    

    输出如下所示:

    10.12.0.6:5432                   HEALTHY     OK                outbound|5432||accounts-db.bank-of-anthos.svc.cluster.local
    10.12.0.7:8080                   HEALTHY     OK                outbound|8080||balancereader.bank-of-anthos.svc.cluster.local
    10.12.0.8:8080                   HEALTHY     OK                outbound|8080||transactionhistory.bank-of-anthos.svc.cluster.local
    10.12.0.9:8080                   HEALTHY     OK                outbound|8080||userservice.bank-of-anthos.svc.cluster.local
    10.12.1.10:8080                  HEALTHY     OK                outbound|8080||ledgerwriter.bank-of-anthos.svc.cluster.local
    10.12.1.9:8080                   HEALTHY     OK                outbound|8080||contacts.bank-of-anthos.svc.cluster.local
    10.12.2.11:5432                  HEALTHY     OK                outbound|5432||ledger-db.bank-of-anthos.svc.cluster.local
    10.12.2.13:8080                  HEALTHY     OK                outbound|80||frontend.bank-of-anthos.svc.cluster.local
    10.76.1.10:8080                  HEALTHY     OK                outbound|8080||transactionhistory.bank-of-anthos.svc.cluster.local
    10.76.1.8:8080                   HEALTHY     OK                outbound|8080||balancereader.bank-of-anthos.svc.cluster.local
    10.76.1.9:8080                   HEALTHY     OK                outbound|80||frontend.bank-of-anthos.svc.cluster.local
    10.76.2.10:8080                  HEALTHY     OK                outbound|8080||userservice.bank-of-anthos.svc.cluster.local
    10.76.2.8:8080                   HEALTHY     OK                outbound|8080||contacts.bank-of-anthos.svc.cluster.local
    10.76.2.9:8080                   HEALTHY     OK                outbound|8080||ledgerwriter.bank-of-anthos.svc.cluster.local
    

    在上面的输出中,每个分布式服务都有两个端点 IP 地址。这些是 pod IP 地址,每个集群对应一个 IP 地址。

访问 Bank of Anthos

如需访问 Bank of Anthos 应用,您可以使用任一集群中的 asm-ingressgateway Service 公共 IP 地址。

  1. 从这两个集群中获取 asm-ingressgateway IP 地址:

    kubectl --context ${CLUSTER_1} \
    --namespace asm-ingress get svc asm-ingressgateway -o jsonpath='{.status.loadBalancer}' | grep "ingress"
    
    kubectl --context ${CLUSTER_2} \
    --namespace asm-ingress get svc asm-ingressgateway -o jsonpath='{.status.loadBalancer}' | grep "ingress"
    
    

    输出如下所示。

    {"ingress":[{"ip":"35.236.4.18"}]}
    {"ingress":[{"ip":"34.68.94.81"}]}
    

    复制其中一个 IP 地址以在下一步中使用。

  2. 在网络浏览器中打开新标签页,然后转到上述输出中的任一 IP 地址。系统应该显示 Bank of Anthos 前端,以便您登录、将资金存入您的账号,并将资金转移到其他账号。应用应能够完全正常运行。

直观呈现分布式服务

您可以在 Cloud Service Mesh 中直观呈现分布式服务。

  1. 如需查看您的服务,请转到 Google Cloud 控制台中的 Anthos > Service Mesh 页面。

    前往“服务网格”

    您可以在视图或拓扑视图中查看服务。默认视图是表格视图,以表格格式运行的所有分布式服务。要更改视图,请点击要显示的视图。

  2. 视图中,点击 frontend distributed service。点击某个服务时,您会看到该服务的详细信息以及关联的服务。

    在服务详情视图中,您可以点击显示时间轴,以创建 SLO 并查看服务的历史时间轴。

  3. 如需查看黄金信号,请点击侧边栏中的指标

  4. 每秒请求次数图表中,点击细分依据,然后选择位置

    结果会显示这两个地区内两个集群的每秒请求数。分布式服务运行状况良好,两个端点都在处理流量。

  5. 如需查看服务网格的拓扑,请在侧边栏上点击 Anthos Service Mesh,然后点击拓扑视图

  6. 如需查看其他数据,请将鼠标指针悬停在 frontend 服务上。这会显示每秒从前端到其他服务的请求数。

  7. 如需查看更多详细信息,请点击 frontend 服务上的展开。系统会显示服务和工作负载。您可以进一步将工作负载扩展到两个 Deployment,将 Deployment 扩展到 ReplicaSet,并将 ReplicaSet 扩展到 Pod。展开所有元素时,您可以看到分布式 frontend 服务,该服务实际上是一个 Service 和两个 Pod。

配置多集群 Ingress

在本部分中,您将创建一个多集群 Ingress,它会将流量发送到在两个集群中运行的 Bank of GKE Enterprise frontend 服务。您可以使用 Cloud Load Balancing 创建一个将两个集群中的 asm-ingressgateway Service 用作后端的负载均衡器。ingress-config 集群用于编排多集群 Ingress 配置。

如需创建负载均衡器,请使用 MultiClusterIngress 以及一个或多个 MultiClusterServicesMultiClusterIngressMultiClusterService 对象是多集群模拟,类似于在单个集群上下文中使用的现有 Kubernetes Ingress 和 Service 资源。

  1. 启用所需的 GKE Enterprise、GKE Fleet 和 Multi Cluster Ingress API:

    gcloud services enable \
      anthos.googleapis.com \
      multiclusterservicediscovery.googleapis.com \
      multiclusteringress.googleapis.com
    
  2. 创建 ingress-config 集群。您可以使用任何集群,但我们建议您为此目的创建单独的集群。

    gcloud container clusters create ${CLUSTER_INGRESS} \
      --zone ${CLUSTER_INGRESS_ZONE} \
      --num-nodes=1 \
      --enable-ip-alias \
      --workload-pool=${WORKLOAD_POOL}
    
  3. 为方便起见,请获取集群凭据并重命名上下文:

    gcloud container clusters get-credentials ${CLUSTER_INGRESS} \
      --zone ${CLUSTER_INGRESS_ZONE} --project ${PROJECT_ID}
    
    kubectl config rename-context \
      gke_${PROJECT_ID}_${CLUSTER_INGRESS_ZONE}_${CLUSTER_INGRESS} ${CLUSTER_INGRESS}
    
  4. 如需使用多集群 Ingress,请将所有参与的集群注册到 GKE Enterprise 舰队,包括配置集群:

  5. 注册配置集群:

    gcloud container fleet memberships register ${CLUSTER_INGRESS} \
      --project=${PROJECT_ID} \
      --gke-cluster=${CLUSTER_INGRESS_ZONE}/${CLUSTER_INGRESS} \
      --enable-workload-identity
    
  6. 验证所有集群均已向 GKE Enterprise 舰队注册:

    gcloud container fleet memberships list
    

    输出如下所示:

    NAME            EXTERNAL_ID
    gke-west        7fe5b7ce-50d0-4e64-a9af-55d37b3dd3fa
    gke-central     6f1f6bb2-a3f6-4e9c-be52-6907d9d258cd
    gke-ingress     3574ee0f-b7e6-11ea-9787-42010a8a019c
    
  7. ingress-config 集群上启用多集群 Ingress 功能。这将在集群上创建 MulticlusterServiceMulticlusterIngress CustomResourceDefinition (CRD)。

    gcloud container fleet ingress enable \
      --config-membership=projects/${PROJECT_ID}/locations/global/memberships/${CLUSTER_INGRESS}
    
  8. 验证 ingress-config 集群上是否已启用多集群 Ingress:

    gcloud container fleet ingress describe
    

    输出如下所示:

    membershipStates:
      projects/986443280307/locations/global/memberships/gke-central-priv:
        state:
          code: OK
          updateTime: '2022-09-29T13:57:02.972748202Z'
      projects/986443280307/locations/global/memberships/gke-ingress:
        state:
          code: OK
          updateTime: '2022-09-29T13:57:02.972744692Z'
      projects/986443280307/locations/global/memberships/gke-west-priv:
        state:
          code: OK
          updateTime: '2022-09-29T13:57:02.972746497Z'
    
  9. 验证两个 CRD 已部署在 ingress-config 集群中:

    kubectl --context=${CLUSTER_INGRESS} get crd | grep multicluster
    

    输出如下所示。

    multiclusteringresses.networking.gke.io     2020-10-29T17:32:50Z
    multiclusterservices.networking.gke.io      2020-10-29T17:32:50Z
    
  10. ingress-config 集群中创建 asm-ingress 命名空间:

    kubectl --context ${CLUSTER_INGRESS} create namespace asm-ingress
    
  11. 创建 MultiClusterIngress 资源:

    cat <<EOF > ${HOME}/mci.yaml
    apiVersion: networking.gke.io/v1beta1
    kind: MultiClusterIngress
    metadata:
      name: asm-ingressgateway-multicluster-ingress
    spec:
      template:
        spec:
          backend:
           serviceName: asm-ingressgateway-multicluster-svc
           servicePort: 80
    EOF
    
  12. 创建 MultiClusterService 资源:

    cat <<'EOF' > $HOME/mcs.yaml
    apiVersion: networking.gke.io/v1beta1
    kind: MultiClusterService
    metadata:
      name: asm-ingressgateway-multicluster-svc
      annotations:
        beta.cloud.google.com/backend-config: '{"ports": {"80":"gke-ingress-config"}}'
    spec:
      template:
        spec:
          selector:
            asm: ingressgateway
          ports:
          - name: frontend
            protocol: TCP
            port: 80 # servicePort defined in Multi Cluster Ingress
      clusters:
      - link: "us-west2-a/gke-west-priv"
      - link: "us-central1-a/gke-central-priv"
    EOF
    
  13. 为健康检查创建 BackendConfig 资源:

    cat <<EOF > $HOME/backendconfig.yaml
    apiVersion: cloud.google.com/v1beta1
    kind: BackendConfig
    metadata:
      name: gke-ingress-config
    spec:
      healthCheck:
        type: HTTP
        port: 15021
        requestPath: /healthz/ready
    EOF
    
  14. 应用 BackendConfigMultiClusterServiceMultiClusterIngress 清单:

    kubectl --context ${CLUSTER_INGRESS} -n asm-ingress apply -f ${HOME}/backendconfig.yaml
    kubectl --context ${CLUSTER_INGRESS} -n asm-ingress apply -f ${HOME}/mci.yaml
    kubectl --context ${CLUSTER_INGRESS} -n asm-ingress apply -f ${HOME}/mcs.yaml
    
  15. 您在 Ingress 集群中部署的 MultiClusterService 将在集群 1 和集群 2 中创建“无头” Service。验证是否已创建“无头”Services

    kubectl --context=${CLUSTER_1} -n asm-ingress \
      get services | grep multicluster-svc
    kubectl --context=${CLUSTER_2} -n asm-ingress \
      get services | grep multicluster-svc
    

    输出类似于以下内容:

    mci-frontend-multi-cluster-service-svc-f7rcyqry22iq8nmw   ClusterIP      None          <none>          80/TCP         77s
    mci-frontend-multi-cluster-service-svc-f7rcyqry22iq8nmw   ClusterIP      None          <none>          80/TCP         78s
    
  16. 运行以下命令并等待一段时间,直到您获得 Cloud Load Balancing IP 地址:

    watch kubectl --context ${CLUSTER_INGRESS} -n asm-ingress get multiclusteringress \
      -o jsonpath="{.items[].status.VIP}"
    

    输出如下所示:

    35.35.23.11
    

    如需退出监视命令,请按 Ctrl+C

  17. 在网络浏览器中导航到 Cloud Load Balancing IP 地址,以访问 Anthos Bank 前端:

    kubectl --context ${CLUSTER_INGRESS} \
      -n asm-ingress get multiclusteringress \
      -o jsonpath="{.items[].status.VIP}"
    

    如果您收到 404 错误(或 502 错误),请等待几分钟,然后在网络浏览器中刷新页面。

清理

为避免系统向您的账号收取费用,请删除项目或删除集群。

删除项目

为避免支付费用,最简单的方法是删除您为本教程创建的项目。

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除集群

  1. 在 Cloud Shell 中,取消注册并删除 bluegreen 集群:

    gcloud container fleet memberships unregister ${CLUSTER_1} \
      --project=${PROJECT} \
      --gke-uri=${CLUSTER_1_URI}
    gcloud container clusters delete ${CLUSTER_1} \
      --zone ${CLUSTER_1_ZONE} \
      --quiet
    
    gcloud container fleet memberships unregister ${CLUSTER_2} \
      --project=${PROJECT} \
      --gke-uri=${CLUSTER_2_URI}
    gcloud container clusters delete ${CLUSTER_2} \
      --zone ${CLUSTER_2_ZONE} \
      --quiet
    
  2. 从 ingress-config 集群中删除 MuticlusterIngress 资源:

    kubectl --context ${CLUSTER_INGRESS} -n istio-system delete -f $HOME/mci.yaml
    

    此命令会从项目中删除 Cloud Load Balancing 资源。

  3. 取消注册并删除 ingress-config 集群:

    gcloud container fleet memberships unregister ${CLUSTER_INGRESS} \
      --project=${PROJECT} \
      --gke-uri=${CLUSTER_INGRESS_URI}
    gcloud container clusters delete ${CLUSTER_INGRESS} \
      --zone ${CLUSTER_INGRESS_ZONE} \
      --quiet
    
  4. 验证是否已删除所有集群:

    gcloud container clusters list
    

    输出如下所示:

    <null>
  5. 重置 kubeconfig 文件:

    unset KUBECONFIG
    

后续步骤