从边缘到多集群网格:通过 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. Make sure 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`
    

创建 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"
    

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

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

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

后续步骤

贡献者

作者:

其他贡献者: