部署多集群网关以实现基于容量的负载均衡

本文档将引导您完成以下操作:跨不同区域中的两个 GKE 集群部署示例应用,并展示多集群网关如何在流量超出服务容量限制时智能地路由流量。

基于容量的负载均衡是多集群网关的一项功能,可帮助您构建高度可靠且富有弹性的应用。通过定义服务的容量,您可以保护服务免受过载影响,并帮助确保为用户提供一致的体验。当一个集群中的服务达到其容量上限时,负载均衡器会自动将流量重定向到另一个具有可用容量的集群。如需详细了解流量管理,请参阅 GKE 流量管理

在本教程中,您将使用示例 store 应用来模拟以下现实场景:在线购物服务由单独的团队拥有和运营,并且跨共享 GKE 集群舰队进行部署。

准备工作

多集群 Gateway 需要一些环境准备才能部署。在继续操作之前,请按照为多集群 Gateway 准备环境 中的步骤操作:

  1. 部署 GKE 集群。

  2. 向舰队注册集群(如果尚未注册)。

  3. 启用多集群 Service 和多集群 Gateway 控制器。

最后,请先查看 GKE Gateway Controller 的限制和已知问题,然后才可在环境中使用该控制器。

部署基于容量的负载均衡

本部分中的练习通过跨不同区域的两个 GKE 集群部署应用来演示全球负载均衡和 Service 容量概念。生成的流量按各种每秒请求数 (RPS) 级别发送,以显示如何跨集群和区域对流量进行负载均衡。

下图展示了您将部署的拓扑,以及当流量超出 Service 容量时,流量如何在集群和区域之间溢出:

流量从一个集群溢出到另一个集群

准备环境

  1. 按照为多集群 Gateway 准备环境中的说明准备环境。

  2. 确认配置集群上已安装 GatewayClass 资源:

    kubectl get gatewayclasses --context=gke-west-1
    

    输出内容类似如下:

    NAME                                  CONTROLLER                  ACCEPTED   AGE
    gke-l7-global-external-managed        networking.gke.io/gateway   True       16h
    gke-l7-global-external-managed-mc     networking.gke.io/gateway   True       14h
    gke-l7-gxlb                           networking.gke.io/gateway   True       16h
    gke-l7-gxlb-mc                        networking.gke.io/gateway   True       14h
    gke-l7-regional-external-managed      networking.gke.io/gateway   True       16h
    gke-l7-regional-external-managed-mc   networking.gke.io/gateway   True       14h
    gke-l7-rilb                           networking.gke.io/gateway   True       16h
    gke-l7-rilb-mc                        networking.gke.io/gateway   True       14h
    

部署应用

将示例 Web 应用服务器部署到这两个集群:

kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml
kubectl apply --context gke-east-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/master/gateway/docs/store-traffic-deploy.yaml

输出内容类似如下:

namespace/store created
deployment.apps/store created

部署 Service、Gateway 和 HTTPRoute

  1. 将以下 Service 清单同时应用于 gke-west-1gke-east-1 集群:

    cat << EOF | kubectl apply --context gke-west-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: traffic-test
      annotations:
        networking.gke.io/max-rate-per-endpoint: "10"
    spec:
      ports:
      - port: 8080
        targetPort: 8080
        name: http
      selector:
        app: store
      type: ClusterIP
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: traffic-test
    EOF
    
    cat << EOF | kubectl apply --context gke-east-1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: traffic-test
      annotations:
        networking.gke.io/max-rate-per-endpoint: "10"
    spec:
      ports:
      - port: 8080
        targetPort: 8080
        name: http
      selector:
        app: store
      type: ClusterIP
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: traffic-test
    EOF
    

    此 Service 的 max-rate-per-endpoint 设置为每秒 10 个请求。每个集群有 2 个副本,每个 Service 的每个集群有 20 RPS 容量。

    如需详细了解如何为 Service 选择 Service 容量级别,请参阅确定 Service 的容量

  2. 将以下 Gateway 清单应用于配置集群(在此示例中为 gke-west-1):

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store
      namespace: traffic-test
    spec:
      gatewayClassName: gke-l7-global-external-managed-mc
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    EOF
    

    该清单描述了一个外部全球多集群 Gateway,用于部署具有可公开访问的 IP 地址的外部应用负载均衡器。

  3. 将以下 HTTPRoute 清单应用于配置集群(在此示例中为 gke-west-1):

    cat << EOF | kubectl apply --context gke-west-1 -f -
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store
      namespace: traffic-test
      labels:
        gateway: store
    spec:
      parentRefs:
      - kind: Gateway
        namespace: traffic-test
        name: store
      rules:
      - backendRefs:
        - name: store
          group: net.gke.io
          kind: ServiceImport
          port: 8080
    EOF
    

    该清单描述了一个 HTTPRoute,用于使用将所有流量定向到 store ServiceImport 的路由规则配置 Gateway。store ServiceImport 会将跨两个集群的 store Service Pod 分组,并允许负载均衡器将它们作为单个 Service 进行寻址。

    几分钟后,您可以检查 Gateway 的事件,以了解其是否已完成部署:

    kubectl describe gateway store -n traffic-test --context gke-west-1
    

    输出内容类似如下:

    ...
    Status:
      Addresses:
        Type:   IPAddress
        Value:  34.102.159.147
      Conditions:
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               The OSS Gateway API has deprecated this condition, do not depend on it.
        Observed Generation:   1
        Reason:                Scheduled
        Status:                True
        Type:                  Scheduled
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:
        Observed Generation:   1
        Reason:                Accepted
        Status:                True
        Type:                  Accepted
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:
        Observed Generation:   1
        Reason:                Programmed
        Status:                True
        Type:                  Programmed
        Last Transition Time:  2023-10-12T21:40:59Z
        Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
        Observed Generation:   1
        Reason:                Ready
        Status:                True
        Type:                  Ready
      Listeners:
        Attached Routes:  1
        Conditions:
          Last Transition Time:  2023-10-12T21:40:59Z
          Message:
          Observed Generation:   1
          Reason:                Programmed
          Status:                True
          Type:                  Programmed
          Last Transition Time:  2023-10-12T21:40:59Z
          Message:               The OSS Gateway API has altered the "Ready" condition semantics and reservedit for future use.  GKE Gateway will stop emitting it in a future update, use "Programmed" instead.
          Observed Generation:   1
          Reason:                Ready
          Status:                True
          Type:                  Ready
        Name:                    http
        Supported Kinds:
          Group:  gateway.networking.k8s.io
          Kind:   HTTPRoute
    Events:
      Type    Reason  Age                  From                   Message
      ----    ------  ----                 ----                   -------
      Normal  ADD     12m                  mc-gateway-controller  traffic-test/store
      Normal  SYNC    6m43s                mc-gateway-controller  traffic-test/store
      Normal  UPDATE  5m40s (x4 over 12m)  mc-gateway-controller  traffic-test/store
      Normal  SYNC    118s (x6 over 10m)   mc-gateway-controller  SYNC on traffic-test/store was a success
    

    此输出显示 Gateway 已成功部署。在部署 Gateway 后,流量可能需要几分钟时间才能开始通过。记下此输出中的 IP 地址,因为在接下来的步骤中会用到。

确认流量

使用 curl 命令测试 Gateway IP 地址,以确认流量正在传递给应用:

curl GATEWAY_IP_ADDRESS

输出内容类似如下:

{
  "cluster_name": "gke-west-1",
  "host_header": "34.117.182.69",
  "pod_name": "store-54785664b5-mxstv",
  "pod_name_emoji": "👳🏿",
  "project_id": "project",
  "timestamp": "2021-11-01T14:06:38",
  "zone": "us-west1-a"
}

此输出会显示 Pod 元数据,表明处理请求所在的区域。

使用负载测试验证流量

如需验证负载均衡器是否正常运行,您可以在 gke-west-1 集群中部署流量生成器。流量生成器在不同的负载级别生成流量,以演示负载均衡器的容量和溢出功能。以下步骤演示了三个级别的负载:

  • 10 RPS,低于 gke-west-1 中 store Service 的容量。
  • 30 RPS,超过 gke-west-1 store Service 的容量,导致流量溢出到 gke-east-1
  • 60 RPS,超出这两个集群中的 Service 的容量。

配置信息中心

  1. 获取 Gateway 的底层 URLmap 的名称:

    kubectl get gateway store -n traffic-test --context=gke-west-1 -o=jsonpath="{.metadata.annotations.networking\.gke\.io/url-maps}"
    

    输出类似于以下内容:

    /projects/PROJECT_NUMBER/global/urlMaps/gkemcg1-traffic-test-store-armvfyupay1t
    
  2. 在 Google Cloud 控制台中,前往 Metrics Explorer 页面。

    转到 Metrics Explorer

  3. 选择指标下,点击代码:MQL

  4. 输入以下查询,以观察跨两个集群的 store Service 的流量指标:

    fetch https_lb_rule
    | metric 'loadbalancing.googleapis.com/https/backend_request_count'
    | filter (resource.url_map_name == 'GATEWAY_URL_MAP')
    | align rate(1m)
    | every 1m
    | group_by [resource.backend_scope],
        [value_backend_request_count_aggregate:
            aggregate(value.backend_request_count)]
    

    GATEWAY_URL_MAP 替换为上一步中的 URLmap 名称。

  5. 点击运行查询。在下一部分中部署负载生成器后,请至少等待 5 分钟,使指标显示在图表中。

使用 10 RPS 进行测试

  1. 将 Pod 部署到 gke-west-1 集群:

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 10'
    

    GATEWAY_IP_ADDRESS 替换为上一步中的 Gateway IP 地址。

    输出类似于以下内容,表明流量生成器正在发送流量:

    If you don't see a command prompt, try pressing enter.
    

    负载生成器会持续向 Gateway 发送 10 RPS。即使流量来自 Google Cloud 区域内部,负载均衡器也会将其视为来自美国西海岸的客户端流量。为了模拟真实的客户端多样性,负载生成器会将每个 HTTP 请求作为新的 TCP 连接发送,这意味着流量会更均匀地分配到后端 Pod 中。

    生成器最多需要 5 分钟来为信息中心生成流量。

  2. 查看 Metrics Explorer 信息中心。系统将显示两行,指示为了均衡负载而向每个集群分配的流量:

    显示流量负载均衡到集群的图表

    您应该会看到 us-west1-a 收到了大约 10 RPS 的流量,而 us-east1-b 没有收到任何流量。由于流量生成器在 us-west1 中运行,因此所有流量都会发送到 gke-west-1 集群中的 Service。

  3. 使用 Ctrl+C 停止负载生成器,然后删除 Pod:

    kubectl delete pod loadgen --context gke-west-1
    

使用 30 RPS 进行测试

  1. 再次部署负载生成器,但将其配置为发送 30 RPS:

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 30'
    

    生成器最多需要 5 分钟来为信息中心生成流量。

  2. 查看 Cloud 运维信息中心。

    此图显示流量溢出到 gke-east-1

    您应该会看到大约 20 RPS 被发送到 us-west1-a,10 RPS 被发送到 us-east1-b。这表示 gke-west-1 中的 Service 得到充分利用,并将 10 RPS 的流量溢出到 gke-east-1 中的 Service。

  3. 使用 Ctrl+C 停止负载生成器,然后删除 Pod:

    kubectl delete pod loadgen --context gke-west-1
    

使用 60 RPS 进行测试

  1. 将负载生成器配置为发送 60 RPS 并部署:

    kubectl run --context gke-west-1 -i --tty --rm loadgen  \
        --image=cyrilbkr/httperf  \
        --restart=Never  \
        -- /bin/sh -c 'httperf  \
        --server=GATEWAY_IP_ADDRESS  \
        --hog --uri="/zone" --port 80  --wsess=100000,1,1 --rate 60'
    
  2. 等待 5 分钟并查看您的 Cloud 运维信息中心。现在,它应该显示两个集群都收到大约 30 RPS。由于所有 Service 在全球范围内都被过度利用,因此不会发生流量溢出,而且 Service 会吸收其可以吸收的所有流量。

    显示 Service 利用率过高的图表

  3. 使用 Ctrl+C 停止负载生成器,然后删除 Pod:

    kubectl delete pod loadgen --context gke-west-1
    

清理

完成本文档中的练习后,请按照以下步骤移除资源,防止您的账号产生不必要的费用:

  1. 删除集群

  2. 如果不需要为其他目的注册集群,请从舰队中取消注册集群

  3. 停用 multiclusterservicediscovery 功能:

    gcloud container fleet multi-cluster-services disable
    
  4. 停用 Multi Cluster Ingress:

    gcloud container fleet ingress disable
    
  5. 停用 API:

    gcloud services disable \
        multiclusterservicediscovery.googleapis.com \
        multiclusteringress.googleapis.com \
        trafficdirector.googleapis.com \
        --project=PROJECT_ID
    

问题排查

没有健康的上行

具体情况:

创建网关但无法访问后端服务(503 响应代码)时,可能会出现以下问题:

no healthy upstream

原因:

此错误消息表示健康检查探测器找不到健康状况良好的后端服务。您的后端服务可能处于健康状况良好状态,但您可能需要自定义健康检查。

临时解决方法:

如需解决此问题,请使用 HealthCheckPolicy 根据应用的要求自定义监控状况检查(例如 /health)。

后续步骤