从边缘到多集群网格:通过 GKE 网关和 Cloud Service Mesh 部署分布全球的应用

Last reviewed 2024-06-30 UTC

本文档介绍如何完成以下任务:

本部署指南适用于平台管理员,以及运行 Cloud Service Mesh 的高级从业人员。这些说明也适用于 Istio on GKE。

架构

下图展示了服务网格的默认入站流量拓扑,即公开单个集群上的入站流量网关代理的外部 TCP/UDP 负载均衡器:

外部负载均衡器通过入站流量网关代理将外部客户端路由到网格。

本部署指南使用 Google Kubernetes Engine (GKE) 网关资源。具体来说,它使用多集群网关配置多区域负载均衡,作为分布在两个区域中的多个 Autopilot 集群的前端。

客户端、负载均衡器和网格中的 TLS 加密。

上图展示了数据如何流经 Cloud Ingress 和网格 Ingress。如需了解详情,请参阅相关参考架构文档中的架构图说明。

目标

  • 将 Google Cloud 上的两个 GKE Autopilot 集群部署到同一舰队
  • 将基于 Istio 的 Cloud Service Mesh 部署到同一舰队。
  • 使用 GKE 网关配置负载均衡器,以终止公共 HTTPS 流量。
  • 将公共 HTTPS 流量定向到跨多个集群和区域部署的 Cloud Service Mesh 托管的应用。
  • whereami 示例应用部署到这两个 Autopilot 集群。

费用优化

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

如需根据您的预计使用量来估算费用,请使用价格计算器

新 Google Cloud 用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  2. Verify that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    您将通过 Cloud Shell 运行此部署中的所有终端命令。

  4. 设置默认 Google Cloud 项目:

    export PROJECT=YOUR_PROJECT
    export PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)")
    gcloud config set project PROJECT_ID
    

    PROJECT_ID 替换为您要用于此部署的项目的 ID。

  5. 创建工作目录:

    mkdir -p ${HOME}/edge-to-mesh-multi-region
    cd ${HOME}/edge-to-mesh-multi-region
    export WORKDIR=`pwd`
    
  6. 创建 GKE 集群

    在本部分中,您将创建 GKE 集群来托管应用和配套基础架构,这些稍后将在本部署指南中创建。

    1. 在 Cloud Shell 中,创建一个新的 kubeconfig 文件。此步骤确保不会导致与现有(默认)kubeconfig 文件冲突。

      touch edge2mesh_mr_kubeconfig
      export KUBECONFIG=${WORKDIR}/edge2mesh_mr_kubeconfig
      
    2. 定义在创建 GKE 集群及其中的资源时要使用的环境变量。您可以根据需要修改默认区域选择。

      export CLUSTER_1_NAME=edge-to-mesh-01
      export CLUSTER_2_NAME=edge-to-mesh-02
      export CLUSTER_1_REGION=us-central1
      export CLUSTER_2_REGION=us-east4
      export PUBLIC_ENDPOINT=frontend.endpoints.PROJECT_ID.cloud.goog
      
    3. 启用本指南中需要使用的 Google Cloud API:

      gcloud services enable \
        container.googleapis.com \
        mesh.googleapis.com \
        gkehub.googleapis.com \
        multiclusterservicediscovery.googleapis.com \
        multiclusteringress.googleapis.com \
        trafficdirector.googleapis.com \
        certificatemanager.googleapis.com
      
    4. CLUSTER_1_REGION 中创建一个具有专用节点的 GKE Autopilot 集群。使用 --async 标志,这样便无需等待第一个集群预配完毕和注册到舰队:

      gcloud container clusters create-auto --async \
      ${CLUSTER_1_NAME} --region ${CLUSTER_1_REGION} \
      --release-channel rapid --labels mesh_id=proj-${PROJECT_NUMBER} \
      --enable-private-nodes --enable-fleet
      
    5. CLUSTER_2_REGION 中创建并注册第二个 Autopilot 集群:

      gcloud container clusters create-auto \
      ${CLUSTER_2_NAME} --region ${CLUSTER_2_REGION} \
      --release-channel rapid --labels mesh_id=proj-${PROJECT_NUMBER} \
      --enable-private-nodes --enable-fleet
      
    6. 确保这两个集群都在正常运行。最多可能需要 20 分钟,这两个集群才能都处于正常运行状态:

      gcloud container clusters list
      

      输出类似于以下内容:

      NAME             LOCATION     MASTER_VERSION  MASTER_IP       MACHINE_TYPE  NODE_VERSION    NUM_NODES  STATUS
      edge-to-mesh-01  us-central1  1.27.5-gke.200  34.27.171.241   e2-small      1.27.5-gke.200             RUNNING
      edge-to-mesh-02  us-east4     1.27.5-gke.200  35.236.204.156  e2-small      1.27.5-gke.200             RUNNING
      
    7. 收集 CLUSTER_1_NAME 的凭据。由于您是异步创建 CLUSTER_1_NAME,因此可以在该集群预配期间运行其他命令。

      gcloud container clusters get-credentials ${CLUSTER_1_NAME} \
          --region ${CLUSTER_1_REGION}
      
    8. 为明确起见,用集群的名称重命名 Kubernetes 上下文:

      kubectl config rename-context gke_PROJECT_ID_${CLUSTER_1_REGION}_${CLUSTER_1_NAME} ${CLUSTER_1_NAME}
      kubectl config rename-context gke_PROJECT_ID_${CLUSTER_2_REGION}_${CLUSTER_2_NAME} ${CLUSTER_2_NAME}
      

    安装服务网格

    在本部分中,您将配置使用 Fleet API 的托管式 Cloud Service Mesh。如果使用 Fleet API 启用 Cloud Service Mesh,便可以使用声明式方法来预配服务网格。

    1. 在 Cloud Shell 中,为舰队启用 Cloud Service Mesh:

      gcloud container fleet mesh enable
      
    2. 启用控制平面和数据平面自动管理功能:

      gcloud container fleet mesh update \
        --management automatic \
        --memberships ${CLUSTER_1_NAME},${CLUSTER_2_NAME}
      
    3. 等待大约 20 分钟。然后,确认控制平面状态是否为 ACTIVE

      gcloud container fleet mesh describe
      

      输出类似于以下内容:

      createTime: '2023-11-30T19:23:21.713028916Z'
      membershipSpecs:
        projects/603904278888/locations/us-central1/memberships/edge-to-mesh-01:
          mesh:
            management: MANAGEMENT_AUTOMATIC
        projects/603904278888/locations/us-east4/memberships/edge-to-mesh-02:
          mesh:
            management: MANAGEMENT_AUTOMATIC
      membershipStates:
        projects/603904278888/locations/us-central1/memberships/edge-to-mesh-01:
          servicemesh:
            controlPlaneManagement:
              details:
              - code: REVISION_READY
                details: 'Ready: asm-managed-rapid'
              implementation: ISTIOD
              state: ACTIVE
            dataPlaneManagement:
              details:
              - code: OK
                details: Service is running.
              state: ACTIVE
          state:
           code: OK
            description: |-
              Revision ready for use: asm-managed-rapid.
              All Canonical Services have been reconciled successfully.
            updateTime: '2024-06-27T09:00:21.333579005Z'
        projects/603904278888/locations/us-east4/memberships/edge-to-mesh-02:
          servicemesh:
            controlPlaneManagement:
              details:
              - code: REVISION_READY
                details: 'Ready: asm-managed-rapid'
              implementation: ISTIOD
              state: ACTIVE
            dataPlaneManagement:
              details:
              - code: OK
                details: Service is running.
              state: ACTIVE
          state:
            code: OK
            description: |-
              Revision ready for use: asm-managed-rapid.
              All Canonical Services have been reconciled successfully.
            updateTime: '2024-06-27T09:00:24.674852751Z'
      name: projects/e2m-private-test-01/locations/global/features/servicemesh
      resourceState:
        state: ACTIVE
      spec: {}
      updateTime: '2024-06-04T17:16:28.730429993Z'
      

    部署外部应用负载均衡器并创建入站流量网关

    在本部分中,您将通过 GKE Gateway Controller 部署外部应用负载均衡器,并为两个集群创建入站流量网关。gatewaygatewayClass 资源会自动预配负载均衡器和后端健康检查。为了在负载均衡器上实现 TLS 终结,您需要创建 Certificate Manager 资源并将其关联到负载均衡器。此外,您还可以使用 Endpoints 自动为应用预配公共 DNS 名称。

    在两个集群上安装入站流量网关

    为保证安全性,我们建议在与网格控制平面不同的命名空间中部署入站流量网关。

    1. 在 Cloud Shell 中,为每个集群创建一个专用的 asm-ingress 命名空间:

      kubectl --context=${CLUSTER_1_NAME} create namespace asm-ingress
      kubectl --context=${CLUSTER_2_NAME} create namespace asm-ingress
      
    2. 将命名空间标签添加到 asm-ingress 命名空间:

      kubectl --context=${CLUSTER_1_NAME} label namespace asm-ingress istio-injection=enabled
      kubectl --context=${CLUSTER_2_NAME} label namespace asm-ingress istio-injection=enabled
      

      输出类似于以下内容:

      namespace/asm-ingress labeled
      

      使用 istio-injection=enabledasm-ingress 命名空间添加标签会指示 Cloud Service Mesh 在部署 pod 时自动注入 Envoy 边车代理。

    3. 生成自签名证书以备将来使用:

      openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
       -subj "/CN=frontend.endpoints.PROJECT_ID.cloud.goog/O=Edge2Mesh Inc" \
       -keyout ${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \
       -out ${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
      

      该证书可在负载均衡器和服务网格入站流量网关之间提供额外一层加密。此外,它还支持基于 HTTP/2 的协议(如 gRPC)。如需了解如何将自签名证书关联到入站流量网关,请参阅后文的创建外部 IP 地址、DNS 记录和 TLS 证书资源部分。

      如需详细了解入站流量网关证书的要求,请参阅从负载均衡器到后端的加密

    4. 在每个集群上创建一个 Kubernetes Secret 来存储自签名证书:

      kubectl --context ${CLUSTER_1_NAME} -n asm-ingress create secret tls \
       edge2mesh-credential \
       --key=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \
       --cert=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
      kubectl --context ${CLUSTER_2_NAME} -n asm-ingress create secret tls \
       edge2mesh-credential \
       --key=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.key \
       --cert=${WORKDIR}/frontend.endpoints.PROJECT_ID.cloud.goog.crt
      
    5. 如需与外部应用负载均衡器相集成,请创建 kustomize 变体来配置入站流量网关资源

      mkdir -p ${WORKDIR}/asm-ig/base
      
      cat <<EOF > ${WORKDIR}/asm-ig/base/kustomization.yaml
      resources:
        - github.com/GoogleCloudPlatform/anthos-service-mesh-samples/docs/ingress-gateway-asm-manifests/base
      EOF
      
      mkdir ${WORKDIR}/asm-ig/variant
      
      cat <<EOF > ${WORKDIR}/asm-ig/variant/role.yaml
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
        name: asm-ingressgateway
        namespace: asm-ingress
      rules:
      - apiGroups: [""]
        resources: ["secrets"]
        verbs: ["get", "watch", "list"]
      EOF
      
      cat <<EOF > ${WORKDIR}/asm-ig/variant/rolebinding.yaml
      apiVersion: rbac.authorization.k8s.io/v1
      kind: RoleBinding
      metadata:
        name: asm-ingressgateway
        namespace: asm-ingress
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: Role
        name: asm-ingressgateway
      subjects:
        - kind: ServiceAccount
          name: asm-ingressgateway
      EOF
      
      cat <<EOF > ${WORKDIR}/asm-ig/variant/service-proto-type.yaml
      apiVersion: v1
      kind: Service
      metadata:
        name: asm-ingressgateway
        namespace: asm-ingress
      spec:
        ports:
        - name: status-port
          port: 15021
          protocol: TCP
          targetPort: 15021
        - name: http
          port: 80
          targetPort: 8080
          appProtocol: HTTP
        - name: https
          port: 443
          targetPort: 8443
          appProtocol: HTTP2
        type: ClusterIP
      EOF
      
      cat <<EOF > ${WORKDIR}/asm-ig/variant/gateway.yaml
      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: asm-ingressgateway
        namespace: asm-ingress
      spec:
       servers:
        - port:
            number: 443
            name: https
            protocol: HTTPS
          hosts:
          - "*" # IMPORTANT: Must use wildcard here when using SSL, as SNI isn't passed from GFE
          tls:
            mode: SIMPLE
            credentialName: edge2mesh-credential
      EOF
      
      cat <<EOF > ${WORKDIR}/asm-ig/variant/kustomization.yaml
      namespace: asm-ingress
      resources:
      - ../base
      - role.yaml
      - rolebinding.yaml
      patches:
      - path: service-proto-type.yaml
        target:
          kind: Service
      - path: gateway.yaml
        target:
          kind: Gateway
      EOF
      
    6. 将入站流量网关配置应用于这两个集群:

      kubectl --context ${CLUSTER_1_NAME} apply -k ${WORKDIR}/asm-ig/variant
      kubectl --context ${CLUSTER_2_NAME} apply -k ${WORKDIR}/asm-ig/variant
      

    使用多集群服务将入站流量网关 pod 公开给负载均衡器

    在本部分中,您将通过 ServiceExport 自定义资源导出入站流量网关 pod。出于以下原因,您必须通过 ServiceExport 自定义资源导出入站流量网关 pod:

    1. 在 Cloud Shell 中,为舰队启用多集群服务 (MCS):

      gcloud container fleet multi-cluster-services enable
      
    2. 向 MCS 授予对项目或舰队的必要 IAM 权限:

      gcloud projects add-iam-policy-binding PROJECT_ID \
       --member "serviceAccount:PROJECT_ID.svc.id.goog[gke-mcs/gke-mcs-importer]" \
       --role "roles/compute.networkViewer"
      
    3. 创建 ServiceExport YAML 文件:

      cat <<EOF > ${WORKDIR}/svc_export.yaml
      kind: ServiceExport
      apiVersion: net.gke.io/v1
      metadata:
        name: asm-ingressgateway
        namespace: asm-ingress
      EOF
      
    4. ServiceExport YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/svc_export.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/svc_export.yaml
      

      如果您收到以下错误消息,请稍等数分钟,让 MCS 自定义资源定义 (CRD) 完成安装。然后,重新运行命令,将 ServiceExport YAML 文件应用于这两个集群。

      error: resource mapping not found for name: "asm-ingressgateway" namespace: "asm-ingress" from "svc_export.yaml": no matches for kind "ServiceExport" in version "net.gke.io/v1"
      ensure CRDs are installed first
      

    创建外部 IP 地址、DNS 记录和 TLS 证书资源

    在本部分中,您将创建相关的网络资源,这些资源将为您稍后在本部署中创建的负载均衡资源提供支持。

    1. 在 Cloud Shell 中,预留静态外部 IP 地址:

      gcloud compute addresses create mcg-ip --global
      

      GKE 网关资源会使用静态 IP 地址。即使重新创建外部负载均衡器,该静态 IP 地址也会保持不变。

    2. 获取该静态 IP 地址并将其存储在环境变量中:

      export MCG_IP=$(gcloud compute addresses describe mcg-ip --global --format "value(address)")
      echo ${MCG_IP}
      

      如需针对您的网关 IP 地址创建易于使用的稳定映射,您必须具有公共 DNS 记录。

      您可以使用所需的任何 DNS 提供商和自动化架构。本部署使用 Endpoints 而不是创建托管式 DNS 区域。Endpoints 为外部 IP 地址提供免费的 Google 管理的 DNS 记录。

    3. 运行以下命令以创建名为 dns-spec.yaml 的 YAML 文件:

      cat <<EOF > ${WORKDIR}/dns-spec.yaml
      swagger: "2.0"
      info:
        description: "Cloud Endpoints DNS"
        title: "Cloud Endpoints DNS"
        version: "1.0.0"
      paths: {}
      host: "frontend.endpoints.PROJECT_ID.cloud.goog"
      x-google-endpoints:
      - name: "frontend.endpoints.PROJECT_ID.cloud.goog"
        target: "${MCG_IP}"
      EOF
      

      dns-spec.yaml 文件以 frontend.endpoints.PROJECT_ID.cloud.goog 的形式定义公共 DNS 记录,其中 PROJECT_ID 是您的唯一项目标识符。

    4. 部署 dns-spec.yaml 文件以创建 DNS 条目。此过程需要几分钟才能完成。

      gcloud endpoints services deploy ${WORKDIR}/dns-spec.yaml
      
    5. 使用 Certificate Manager 为您在上一步中创建的 DNS 条目名称创建证书

      gcloud certificate-manager certificates create mcg-cert \
          --domains="frontend.endpoints.PROJECT_ID.cloud.goog"
      

      Google 管理的 TLS 证书用于终止负载均衡器上的入站客户端请求。

    6. 创建证书映射

      gcloud certificate-manager maps create mcg-cert-map
      

      负载均衡器通过您在下一步中创建的证书映射条目来引用证书。

    7. 为您在本部分前面创建的证书创建证书映射条目

      gcloud certificate-manager maps entries create mcg-cert-map-entry \
          --map="mcg-cert-map" \
          --certificates="mcg-cert" \
          --hostname="frontend.endpoints.PROJECT_ID.cloud.goog"
      

    创建后端服务政策和负载均衡器资源

    在本部分中,您将完成以下任务:

    • 创建 Cloud Armor 安全政策及相关规则。
    • 创建一个政策,让负载均衡器通过您在前面创建的 ServiceExport YAML 文件检查入站流量网关 pod 的响应能力。
    • 使用 GKE Gateway API 创建负载均衡器资源。
    • 使用 GatewayClass 自定义资源设置特定的负载均衡器类型。
    • 为舰队启用多集群负载均衡,并将其中一个集群指定为舰队的配置集群
    1. 在 Cloud Shell 中,创建 Cloud Armor 安全政策:

      gcloud compute security-policies create edge-fw-policy \
          --description "Block XSS attacks"
      
    2. 为安全政策创建规则:

      gcloud compute security-policies rules create 1000 \
          --security-policy edge-fw-policy \
          --expression "evaluatePreconfiguredExpr('xss-stable')" \
          --action "deny-403" \
          --description "XSS attack filtering"
      
    3. 为安全政策创建 YAML 文件,并通过相应的 ServiceImport YAML 文件引用 ServiceExport YAML 文件:

      cat <<EOF > ${WORKDIR}/cloud-armor-backendpolicy.yaml
      apiVersion: networking.gke.io/v1
      kind: GCPBackendPolicy
      metadata:
        name: cloud-armor-backendpolicy
        namespace: asm-ingress
      spec:
        default:
          securityPolicy: edge-fw-policy
        targetRef:
          group: net.gke.io
          kind: ServiceImport
          name: asm-ingressgateway
      EOF
      
    4. 将 Cloud Armor 政策应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
      
    5. 创建自定义 YAML 文件,以允许负载均衡器对这两个集群中入站流量网关 pod 的 Envoy 健康端点(路径 /healthz/ready 上的端口 15021)执行健康检查:

      cat <<EOF > ${WORKDIR}/ingress-gateway-healthcheck.yaml
      apiVersion: networking.gke.io/v1
      kind: HealthCheckPolicy
      metadata:
        name: ingress-gateway-healthcheck
        namespace: asm-ingress
      spec:
        default:
          config:
            httpHealthCheck:
              port: 15021
              portSpecification: USE_FIXED_PORT
              requestPath: /healthz/ready
            type: HTTP
        targetRef:
          group: net.gke.io
          kind: ServiceImport
          name: asm-ingressgateway
      EOF
      
    6. 将您在上一步中创建的自定义 YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
      
    7. 为舰队启用多集群负载均衡,并将 CLUSTER_1_NAME 指定为配置集群:

      gcloud container fleet ingress enable \
        --config-membership=${CLUSTER_1_NAME} \
        --location=${CLUSTER_1_REGION}
      
    8. 向舰队中的网关控制器授予 IAM 权限:

      gcloud projects add-iam-policy-binding PROJECT_ID \
          --member "serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-multiclusteringress.iam.gserviceaccount.com" \
          --role "roles/container.admin"
      
    9. 通过引用 gke-l7-global-external-managed-mc gatewayClass 和您在前面创建的静态 IP 地址的网关自定义资源创建负载均衡器 YAML 文件:

      cat <<EOF > ${WORKDIR}/frontend-gateway.yaml
      kind: Gateway
      apiVersion: gateway.networking.k8s.io/v1
      metadata:
        name: external-http
        namespace: asm-ingress
        annotations:
          networking.gke.io/certmap: mcg-cert-map
      spec:
        gatewayClassName: gke-l7-global-external-managed-mc
        listeners:
        - name: http # list the port only so we can redirect any incoming http requests to https
          protocol: HTTP
          port: 80
        - name: https
          protocol: HTTPS
          port: 443
          allowedRoutes:
            kinds:
            - kind: HTTPRoute
        addresses:
        - type: NamedAddress
          value: mcg-ip
      EOF
      
    10. frontend-gateway YAML 文件应用于这两个集群。只有 CLUSTER_1_NAME 具有权威性,除非您将其他配置集群指定为具有权威性:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-gateway.yaml
      
    11. 创建一个名为 default-httproute.yamlHTTPRoute YAML 文件,以指示网关资源向入站流量网关发送请求:

      cat << EOF > ${WORKDIR}/default-httproute.yaml
      apiVersion: gateway.networking.k8s.io/v1
      kind: HTTPRoute
      metadata:
        name: default-httproute
        namespace: asm-ingress
      spec:
        parentRefs:
        - name: external-http
          namespace: asm-ingress
          sectionName: https
        rules:
        - backendRefs:
          - group: net.gke.io
            kind: ServiceImport
            name: asm-ingressgateway
            port: 443
      EOF
      
    12. 将您在上一步中创建的 HTTPRoute YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute.yaml
      
    13. 如需执行从 HTTP 到 HTTP(S) 的重定向,请额外创建一个名为 default-httproute-redirect.yamlHTTPRoute YAML 文件:

      cat << EOF > ${WORKDIR}/default-httproute-redirect.yaml
      kind: HTTPRoute
      apiVersion: gateway.networking.k8s.io/v1
      metadata:
        name: http-to-https-redirect-httproute
        namespace: asm-ingress
      spec:
        parentRefs:
        - name: external-http
          namespace: asm-ingress
          sectionName: http
        rules:
        - filters:
          - type: RequestRedirect
            requestRedirect:
              scheme: https
              statusCode: 301
      EOF
      
    14. 将重定向 HTTPRoute YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/default-httproute-redirect.yaml
      
    15. 查看网关资源,以检查负载均衡器部署的进度:

      kubectl --context=${CLUSTER_1_NAME} describe gateway external-http -n asm-ingress
      

      输出会显示您在此部分输入的信息。

    部署 whereami 示例应用

    本指南使用 whereami 作为示例应用,直接提供有关哪些集群正在响应请求的反馈。以下部分将在这两个集群中设置两个单独的 whereami 部署:frontend 部署和 backend 部署。

    frontend 部署是接收请求的第一个工作负载。然后,它会调用 backend 部署。

    此模型用于演示多服务应用架构。frontendbackend 服务均会部署到这两个集群。

    1. 在 Cloud Shell 中,在这两个集群中为 whereami frontend 和 whereami backend 创建命名空间,并启用命名空间注入:

      kubectl --context=${CLUSTER_1_NAME} create ns frontend
      kubectl --context=${CLUSTER_1_NAME} label namespace frontend istio-injection=enabled
      kubectl --context=${CLUSTER_1_NAME} create ns backend
      kubectl --context=${CLUSTER_1_NAME} label namespace backend istio-injection=enabled
      kubectl --context=${CLUSTER_2_NAME} create ns frontend
      kubectl --context=${CLUSTER_2_NAME} label namespace frontend istio-injection=enabled
      kubectl --context=${CLUSTER_2_NAME} create ns backend
      kubectl --context=${CLUSTER_2_NAME} label namespace backend istio-injection=enabled
      
    2. 为 whereami backend 创建 kustomize 变体:

      mkdir -p ${WORKDIR}/whereami-backend/base
      
      cat <<EOF > ${WORKDIR}/whereami-backend/base/kustomization.yaml
      resources:
        - github.com/GoogleCloudPlatform/kubernetes-engine-samples/quickstarts/whereami/k8s
      EOF
      
      mkdir ${WORKDIR}/whereami-backend/variant
      
      cat <<EOF > ${WORKDIR}/whereami-backend/variant/cm-flag.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: whereami
      data:
        BACKEND_ENABLED: "False" # assuming you don't want a chain of backend calls
        METADATA:        "backend"
      EOF
      
      cat <<EOF > ${WORKDIR}/whereami-backend/variant/service-type.yaml
      apiVersion: "v1"
      kind: "Service"
      metadata:
        name: "whereami"
      spec:
        type: ClusterIP
      EOF
      
      cat <<EOF > ${WORKDIR}/whereami-backend/variant/kustomization.yaml
      nameSuffix: "-backend"
      namespace: backend
      commonLabels:
        app: whereami-backend
      resources:
      - ../base
      patches:
      - path: cm-flag.yaml
        target:
          kind: ConfigMap
      - path: service-type.yaml
        target:
          kind: Service
      EOF
      
    3. 将 whereami backend 变体应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-backend/variant
      kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-backend/variant
      
    4. 为 whereami frontend 创建 kustomize 变体:

      mkdir -p ${WORKDIR}/whereami-frontend/base
      
      cat <<EOF > ${WORKDIR}/whereami-frontend/base/kustomization.yaml
      resources:
        - github.com/GoogleCloudPlatform/kubernetes-engine-samples/quickstarts/whereami/k8s
      EOF
      
      mkdir whereami-frontend/variant
      
      cat <<EOF > ${WORKDIR}/whereami-frontend/variant/cm-flag.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: whereami
      data:
        BACKEND_ENABLED: "True"
        BACKEND_SERVICE: "http://whereami-backend.backend.svc.cluster.local"
      EOF
      
      cat <<EOF > ${WORKDIR}/whereami-frontend/variant/service-type.yaml
      apiVersion: "v1"
      kind: "Service"
      metadata:
        name: "whereami"
      spec:
        type: ClusterIP
      EOF
      
      cat <<EOF > ${WORKDIR}/whereami-frontend/variant/kustomization.yaml
      nameSuffix: "-frontend"
      namespace: frontend
      commonLabels:
        app: whereami-frontend
      resources:
      - ../base
      patches:
      - path: cm-flag.yaml
        target:
          kind: ConfigMap
      - path: service-type.yaml
        target:
          kind: Service
      EOF
      
    5. 将 whereami frontend 变体应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
      kubectl --context=${CLUSTER_2_NAME} apply -k ${WORKDIR}/whereami-frontend/variant
      
    6. 创建 VirtualService YAML 文件以将请求路由到 whereami frontend

      cat << EOF > ${WORKDIR}/frontend-vs.yaml
      apiVersion: networking.istio.io/v1beta1
      kind: VirtualService
      metadata:
        name: whereami-vs
        namespace: frontend
      spec:
        gateways:
        - asm-ingress/asm-ingressgateway
        hosts:
        - 'frontend.endpoints.PROJECT_ID.cloud.goog'
        http:
        - route:
          - destination:
              host: whereami-frontend
              port:
                number: 80
      EOF
      
    7. frontend-vs YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-vs.yaml
      
    8. 现在,您已将 frontend-vs.yaml 部署到这两个集群,可以尝试调用集群的公共端点:

      curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq
      

      输出类似于以下内容:

      {
        "backend_result": {
          "cluster_name": "edge-to-mesh-02",
          "gce_instance_id": "8396338201253702608",
          "gce_service_account": "e2m-mcg-01.svc.id.goog",
          "host_header": "whereami-backend.backend.svc.cluster.local",
          "metadata": "backend",
          "node_name": "gk3-edge-to-mesh-02-pool-2-675f6abf-645h",
          "pod_ip": "10.124.0.199",
          "pod_name": "whereami-backend-7cbdfd788-8mmnq",
          "pod_name_emoji": "📸",
          "pod_namespace": "backend",
          "pod_service_account": "whereami-backend",
          "project_id": "e2m-mcg-01",
          "timestamp": "2023-12-01T03:46:24",
          "zone": "us-east4-b"
        },
        "cluster_name": "edge-to-mesh-01",
        "gce_instance_id": "1047264075324910451",
        "gce_service_account": "e2m-mcg-01.svc.id.goog",
        "host_header": "frontend.endpoints.e2m-mcg-01.cloud.goog",
        "metadata": "frontend",
        "node_name": "gk3-edge-to-mesh-01-pool-2-d687e3c0-5kf2",
        "pod_ip": "10.54.1.71",
        "pod_name": "whereami-frontend-69c4c867cb-dgg8t",
        "pod_name_emoji": "🪴",
        "pod_namespace": "frontend",
        "pod_service_account": "whereami-frontend",
        "project_id": "e2m-mcg-01",
        "timestamp": "2023-12-01T03:46:24",
        "zone": "us-central1-c"
      }
      

    如果您多次运行 curl 命令,会看到响应(来自 frontendbackend)来自不同的区域。负载均衡器在其响应中提供地理位置路由。这意味着负载均衡器会将请求从客户端路由到最近的活跃集群,但请求仍然随机着陆。如果请求偶尔从一个区域发送到另一个区域,会增加延迟时间和费用。

    在下一部分中,您将在服务网格中实现局部负载均衡,以使请求保持在本地。

    为 whereami 启用并测试局部负载均衡

    在本部分中,您将在服务网格中实现局部负载均衡,以使请求保持在本地。您还将执行一些测试,了解 whereami 如何处理各种故障场景。

    当您向 whereami frontend 服务发出请求时,负载均衡器会将请求发送到相对于客户端延迟时间最短的集群。这意味着网格中的入站流量网关 pod 会跨两个集群负载均衡发送到 whereami frontend pod 的请求。本部分将通过在网格内启用局部负载均衡来解决此问题。

    1. 在 Cloud Shell 中,创建 DestinationRule YAML 文件,以启用局部负载均衡,在区域内故障切换到 frontend 服务:

      cat << EOF > ${WORKDIR}/frontend-dr.yaml
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: frontend
        namespace: frontend
      spec:
        host: whereami-frontend.frontend.svc.cluster.local
        trafficPolicy:
          connectionPool:
            http:
              maxRequestsPerConnection: 0
          loadBalancer:
            simple: LEAST_REQUEST
            localityLbSetting:
              enabled: true
          outlierDetection:
            consecutive5xxErrors: 1
            interval: 1s
            baseEjectionTime: 1m
      EOF
      

      上述代码示例只为 frontend 服务启用了本地路由。您还需要一个用来处理后端的配置。

    2. frontend-dr YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/frontend-dr.yaml
      
    3. 创建 DestinationRule YAML 文件,以启用局部负载均衡,在区域内故障切换到 backend 服务:

      cat << EOF > ${WORKDIR}/backend-dr.yaml
      apiVersion: networking.istio.io/v1beta1
      kind: DestinationRule
      metadata:
        name: backend
        namespace: backend
      spec:
        host: whereami-backend.backend.svc.cluster.local
        trafficPolicy:
          connectionPool:
            http:
              maxRequestsPerConnection: 0
          loadBalancer:
            simple: LEAST_REQUEST
            localityLbSetting:
              enabled: true
          outlierDetection:
            consecutive5xxErrors: 1
            interval: 1s
            baseEjectionTime: 1m
      EOF
      
    4. backend-dr YAML 文件应用于这两个集群:

      kubectl --context=${CLUSTER_1_NAME} apply -f ${WORKDIR}/backend-dr.yaml
      kubectl --context=${CLUSTER_2_NAME} apply -f ${WORKDIR}/backend-dr.yaml
      

      将两组 DestinationRule YAML 文件应用于这两个集群后,请求将保持在请求所路由到的集群本地。

      为了测试 frontend 服务的故障切换,减少主集群中入站流量网关的副本数量。

      从多区域负载均衡器的角度来看,此操作是为了模拟集群故障。而这会导致该集群的负载均衡器健康检查失败。此示例使用 CLUSTER_1_REGION 中的集群。您应该只会在 CLUSTER_2_REGION 中看到来自集群的响应。

    5. 将主集群中入站流量网关的副本数量减少到零,并调用公共端点以验证请求是否已故障切换到另一个集群:

      kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=0 deployment/asm-ingressgateway
      

      输出应类似于以下内容:

      $ curl -s https://frontend.endpoints.PROJECT_ID.cloud.goog | jq
      {
        "backend_result": {
          "cluster_name": "edge-to-mesh-02",
          "gce_instance_id": "2717459599837162415",
          "gce_service_account": "e2m-mcg-01.svc.id.goog",
          "host_header": "whereami-backend.backend.svc.cluster.local",
          "metadata": "backend",
          "node_name": "gk3-edge-to-mesh-02-pool-2-675f6abf-dxs2",
          "pod_ip": "10.124.1.7",
          "pod_name": "whereami-backend-7cbdfd788-mp8zv",
          "pod_name_emoji": "🏌🏽‍♀",
          "pod_namespace": "backend",
          "pod_service_account": "whereami-backend",
          "project_id": "e2m-mcg-01",
          "timestamp": "2023-12-01T05:41:18",
          "zone": "us-east4-b"
        },
        "cluster_name": "edge-to-mesh-02",
        "gce_instance_id": "6983018919754001204",
        "gce_service_account": "e2m-mcg-01.svc.id.goog",
        "host_header": "frontend.endpoints.e2m-mcg-01.cloud.goog",
        "metadata": "frontend",
        "node_name": "gk3-edge-to-mesh-02-pool-3-d42ddfbf-qmkn",
        "pod_ip": "10.124.1.142",
        "pod_name": "whereami-frontend-69c4c867cb-xf8db",
        "pod_name_emoji": "🏴",
        "pod_namespace": "frontend",
        "pod_service_account": "whereami-frontend",
        "project_id": "e2m-mcg-01",
        "timestamp": "2023-12-01T05:41:18",
        "zone": "us-east4-b"
      }
      
    6. 如需恢复典型的流量路由方式,请将入站流量网关副本数量恢复为集群中的原始值:

      kubectl --context=${CLUSTER_1_NAME} -n asm-ingress scale --replicas=3 deployment/asm-ingressgateway
      
    7. 通过将主要区域中的副本数量减少到 0,模拟 backend 服务故障:

      kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=0 deployment/whereami-backend
      

      验证 frontend 服务的响应是否来自 us-central1 主要区域(通过负载均衡器传输),backend 服务的响应是否来自 us-east4 次要区域。

      输出还应包含来自主要区域 (us-central1) 的 frontend 服务响应,以及来自次要区域 (us-east4) 的 backend 服务响应。

    8. 将后端服务副本数量恢复为原始值,以恢复典型的流量路由方式:

      kubectl --context=${CLUSTER_1_NAME} -n backend scale --replicas=3 deployment/whereami-backend
      

    现在,您有一个全球 HTTP(S) 负载均衡器,作为服务网格托管的多区域应用的前端。

    清理

    为避免因本部署中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留该项目但删除各个资源。

    删除项目

    1. In the Google Cloud console, go to the Manage resources page.

      Go to Manage resources

    2. In the project list, select the project that you want to delete, and then click Delete.
    3. In the dialog, type the project ID, and then click Shut down to delete the project.

    逐个删除资源

    如果您想保留本部署中使用的 Google Cloud 项目,请逐个删除资源:

    1. 在 Cloud Shell 中,删除 HTTPRoute 资源:

      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/default-httproute-redirect.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/default-httproute-redirect.yaml
      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/default-httproute.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/default-httproute.yaml
      
    2. 删除 GKE 网关资源:

      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/frontend-gateway.yaml
      
    3. 删除政策:

      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/ingress-gateway-healthcheck.yaml
      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/cloud-armor-backendpolicy.yaml
      
    4. 删除服务导出内容:

      kubectl --context=${CLUSTER_1_NAME} delete -f ${WORKDIR}/svc_export.yaml
      kubectl --context=${CLUSTER_2_NAME} delete -f ${WORKDIR}/svc_export.yaml
      
    5. 删除 Cloud Armor 资源:

      gcloud --project=PROJECT_ID compute security-policies rules delete 1000 --security-policy edge-fw-policy --quiet
      gcloud --project=PROJECT_ID compute security-policies delete edge-fw-policy --quiet
      
    6. 删除 Certificate Manager 资源:

      gcloud --project=PROJECT_ID certificate-manager maps entries delete mcg-cert-map-entry --map="mcg-cert-map" --quiet
      gcloud --project=PROJECT_ID certificate-manager maps delete mcg-cert-map --quiet
      gcloud --project=PROJECT_ID certificate-manager certificates delete mcg-cert --quiet
      
    7. 删除 Endpoints DNS 条目:

      gcloud --project=PROJECT_ID endpoints services delete "frontend.endpoints.PROJECT_ID.cloud.goog" --quiet
      
    8. 删除静态 IP 地址:

      gcloud --project=PROJECT_ID compute addresses delete mcg-ip --global --quiet
      
    9. 删除 GKE Autopilot 集群。此步骤需要几分钟时间来完成。

      gcloud --project=PROJECT_ID container clusters delete ${CLUSTER_1_NAME} --region ${CLUSTER_1_REGION} --quiet
      gcloud --project=PROJECT_ID container clusters delete ${CLUSTER_2_NAME} --region ${CLUSTER_2_REGION} --quiet
      

    后续步骤

    贡献者

    作者:

    其他贡献者: