部署内部多集群网关

本文档将通过一个实际示例,引导您部署内部多集群 Gateway,以将 VPC 网络内的流量路由到在两个不同的 GKE 集群中运行的应用。

多集群网关提供了一种强大的方式来管理部署在多个 GKE 集群中的服务的流量。借助 Google 的全球负载均衡基础架构,您可以为应用创建一个统一的入口点,从而简化管理并提高可靠性。

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

此示例展示了如何设置基于路径的路由,以将流量定向到不同的集群。

准备工作

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

  1. 部署 GKE 集群。

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

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

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

跨区域部署内部多集群网关

您可以部署多集群 Gateway,以在多个区域中的 GKE 集群之间提供内部第 7 层负载均衡。这些网关使用 gke-l7-cross-regional-internal-managed-mc GatewayClass。此 GatewayClass 会预配由 Google Cloud 管理的跨区域内部应用负载平衡器,并启用 VPC 网络中的客户端可以访问的内部 VIP。只需使用网关请求这些区域中的地址,即可通过所选区域中的前端公开这些网关。内部 VIP 可以是单个 IP 地址,也可以是多个区域中的 IP 地址,每个区域中有一个 IP 地址在网关中指定。流量会定向到距离最近且健康状况良好并可处理请求的后端 GKE 集群。

前提条件

  1. 通过使用项目 ID 配置 gcloud 环境来设置项目和 shell:

    export PROJECT_ID="YOUR_PROJECT_ID"
    gcloud config set project ${PROJECT_ID}
    
  2. 在不同区域中创建 GKE 集群。

    此示例使用了两个集群,即 us-west1 中的 gke-west-1us-east1 中的 gke-east-1。确保已启用 Gateway API (--gateway-api=standard),并且集群已注册到舰队。

    gcloud container clusters create gke-west-1 \
        --location=us-west1-a \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --project=${PROJECT_ID} \
        --enable-fleet \
        --gateway-api=standard
    
    gcloud container clusters create gke-east-1 \
        --location=us-east1-c \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --project=${PROJECT_ID} \
        --enable-fleet \
        --gateway-api=standard
    

    重命名上下文,以便更轻松地访问:

    gcloud container clusters get-credentials gke-west-1 \
      --location=us-west1-a \
      --project=${PROJECT_ID}
    
    gcloud container clusters get-credentials gke-east-1 \
      --location=us-east1-c \
      --project=${PROJECT_ID}
    kubectl config rename-context gke_${PROJECT_ID}_us-west1-a_gke-west-1 gke-west1
    kubectl config rename-context gke_${PROJECT_ID}_us-east1-c_gke-east-1 gke-east1
    
  3. 启用多集群服务 (MCS) 和多集群 Ingress (MCI/Gateway):

    gcloud container fleet multi-cluster-services enable --project=${PROJECT_ID}
    
    # Set the config membership to one of your clusters (e.g., gke-west-1)
    # This cluster will be the source of truth for multi-cluster Gateway and Route resources.
    gcloud container fleet ingress enable \
        --config-membership=projects/${PROJECT_ID}/locations/us-west1/memberships/gke-west-1 \
        --project=${PROJECT_ID}
    
  4. 配置代理专用子网。 在 GKE 集群所在的每个区域以及负载均衡器将运行的每个区域中,都需要一个代理专用子网。跨区域内部应用负载均衡器要求将此子网的用途设置为 GLOBAL_MANAGED_PROXY

    # Proxy-only subnet for us-west1
    gcloud compute networks subnets create us-west1-proxy-only-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=us-west1 \
        --network=default \
        --range=10.129.0.0/23 # Choose an appropriate unused CIDR range
    
    # Proxy-only subnet for us-east1
    gcloud compute networks subnets create us-east1-proxy-only-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=us-east1 \
        --network=default \
        --range=10.130.0.0/23 # Choose an appropriate unused CIDR range
    

    如果您未使用默认网络,请将 default 替换为您的 VPC 网络名称。确保 CIDR 范围具有唯一性,并且不重叠。

  5. 将演示应用(例如 store)部署到两个集群。gke-networking-recipes 中的示例 store.yaml 文件会创建一个 store 命名空间和一个部署。

    kubectl apply --context gke-west1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    kubectl apply --context gke-east1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store.yaml
    
  6. 通过在每个集群中创建 Kubernetes Service 资源和 ServiceExport 资源,从每个集群导出服务,从而使服务可在整个舰队中被发现。 以下示例从每个集群导出通用 store 服务和特定于区域的服务(store-west-1store-east-1),所有这些服务都位于 store 命名空间内。

    应用于gke-west1

    cat << EOF | kubectl apply --context gke-west1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: store
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: store-west-1 # Specific to this cluster
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store-west-1 # Exporting the region-specific service
      namespace: store
    EOF
    

    应用于gke-east1

    cat << EOF | kubectl apply --context gke-east1 -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: store
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store
      namespace: store
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: store-east-1 # Specific to this cluster
      namespace: store
    spec:
      selector:
        app: store
      ports:
      - port: 8080
        targetPort: 8080
    ---
    kind: ServiceExport
    apiVersion: net.gke.io/v1
    metadata:
      name: store-east-1 # Exporting the region-specific service
      namespace: store
    EOF
    
  7. 检查 ServiceImports: 验证是否在 store 命名空间内的每个集群中都创建了 ServiceImport 资源。创建这些实例可能需要几分钟时间。 bash kubectl get serviceimports --context gke-west1 -n store kubectl get serviceimports --context gke-east1 -n store 您应该会看到 storestore-west-1store-east-1 列出(或根据传播情况显示相关条目)。

配置内部多区域网关

定义引用 gke-l7-cross-regional-internal-managed-mc GatewayClass 的 Gateway 资源。您需要将此清单应用于指定的配置集群,例如 gke-west-1

您可以使用 spec.addresses 字段在特定区域中请求临时 IP 地址,也可以使用预先分配的静态 IP 地址。

  1. 如需使用临时 IP 地址,请将以下 Gateway 清单另存为 cross-regional-gateway.yaml

    # cross-regional-gateway.yaml
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: internal-cross-region-gateway
      namespace: store # Namespace for the Gateway resource
    spec:
      gatewayClassName: gke-l7-cross-regional-internal-managed-mc
      addresses:
      # Addresses across regions. Address value is allowed to be empty or matching
      # the region name.
      - type: networking.gke.io/ephemeral-ipv4-address/us-west1
        value: "us-west1"
      - type: networking.gke.io/ephemeral-ipv4-address/us-east1
        value: "us-east1"
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute # Only allow HTTPRoute to attach
    

    以下列表定义了上述 YAML 文件中的部分字段:

    • metadata.namespace:创建 Gateway 资源的命名空间,例如 store
    • spec.gatewayClassName:GatewayClass 的名称。必须为 gke-l7-cross-regional-internal-managed-mc
    • spec.listeners.allowedRoutes.kinds:可附加的 Route 对象的种类,例如 HTTPRoute
    • spec.addresses:
      • type: networking.gke.io/ephemeral-ipv4-address/REGION:请求临时 IP 地址。
      • value:指定地址的区域,例如 "us-west1""us-east1"
  2. 将清单应用于配置集群,例如 gke-west1

    kubectl apply --context gke-west1 -f cross-regional-gateway.yaml
    

将 HTTPRoute 连接到网关

定义 HTTPRoute 资源以管理流量路由,并将其应用于配置集群。

  1. 将以下 HTTPRoute 清单保存为 store-route.yaml

    # store-route.yaml
    kind: HTTPRoute
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: store-route
      namespace: store
      labels:
        gateway: cross-regional-internal
    spec:
      parentRefs:
      - name: internal-cross-region-gateway
        namespace: store # Namespace where the Gateway is deployed
      hostnames:
      - "store.example.internal" # Hostname clients will use
      rules:
      - matches: # Rule for traffic to /west
        - path:
            type: PathPrefix
            value: /west
        backendRefs:
        - group: net.gke.io # Indicates a multi-cluster ServiceImport
          kind: ServiceImport
          name: store-west-1 # Targets the ServiceImport for the west cluster
          port: 8080
      - matches: # Rule for traffic to /east
        - path:
            type: PathPrefix
            value: /east
        backendRefs:
        - group: net.gke.io
          kind: ServiceImport
          name: store-east-1 # Targets the ServiceImport for the east cluster
          port: 8080
      - backendRefs: # Default rule for other paths (e.g., /)
        - group: net.gke.io
          kind: ServiceImport
          name: store # Targets the generic 'store' ServiceImport (any region)
          port: 8080
    

    以下列表定义了上述 YAML 文件中的部分字段:

    • spec.parentRefs:将此路由附加到 store 命名空间中的 internal-cross-region-gateway
    • spec.hostnames:表示客户端用于访问服务的主机名。
    • spec.rules:定义路由逻辑。此示例使用基于路径的路由:
      • /west 流量路由到 store-west-1 ServiceImport。
      • /east 流量路由到 store-east-1 ServiceImport。
      • 所有其他流量(例如 /)都会流向通用 store ServiceImport。
    • backendRefs:
      • group: net.gke.iokind: ServiceImport 定位多集群服务。
  2. HTTPRoute清单应用于配置集群:

    kubectl apply --context gke-west1 -f store-route.yaml
    

验证网关和路由的状态

  1. 检查网关状态:

    kubectl get gateway internal-cross-region-gateway -n store -o yaml --context gke-west1
    

    查找具有以下条件的条件:type:已编程and状态:“True”. You should see IP addresses assigned in the状态。地址field, corresponding to the regions you specified (e.g., one forus-west1and one forus-east1)。

  2. 检查 HTTPRoute 状态:

    kubectl get httproute store-route -n store -o yaml --context gke-west1
    

    status.parents[].conditions 中查找具有 type: Accepted(或 ResolvedRefs)和 status: "True" 的条件。

确认流量

将 IP 地址分配给网关后,您可以测试来自客户端虚拟机的流量,该虚拟机位于您的 VPC 网络中,并且位于某个区域内,或者位于可以连接到网关 IP 地址的区域内。

  1. 检索网关 IP 地址。

    以下命令尝试解析 JSON 输出。您可能需要根据确切的结构调整 jsonpath

    kubectl get gateway cross-region-gateway -n store --context gke-west1 -o=jsonpath="{.status.addresses[*].value}".
    

    此命令的输出应包含 VIP,例如 VIP1_WESTVIP2_EAST

  2. 发送测试请求: 从 VPC 中的客户端虚拟机:

    # Assuming VIP_WEST is an IP in us-west1 and VIP_EAST is an IP in us-east1
    # Traffic to /west should ideally be served by gke-west-1
    curl -H "host: store.example.internal" http://VIP_WEST/west
    curl -H "host: store.example.internal" http://VIP_EAST/west # Still targets store-west-1 due to path
    
    # Traffic to /east should ideally be served by gke-east-1
    curl -H "host: store.example.internal" http://VIP_WEST/east # Still targets store-east-1 due to path
    curl -H "host: store.example.internal" http://VIP_EAST/east
    
    # Traffic to / (default) could be served by either cluster
    curl -H "host: store.example.internal" http://VIP_WEST/
    curl -H "host: store.example.internal" http://VIP_EAST/
    

    响应应包含 store 应用的详细信息,以指明哪个后端 pod 处理了请求,例如 cluster_namezone

使用静态 IP 地址

您可以使用预先分配的静态内部 IP 地址,而不是临时 IP 地址。

  1. 在您要使用的区域中创建静态 IP 地址:

    gcloud compute addresses create cross-region-gw-ip-west --region us-west1 --subnet default --project=${PROJECT_ID}
    gcloud compute addresses create cross-region-gw-ip-east --region us-east1 --subnet default --project=${PROJECT_ID}
    

    如果您未使用默认子网,请将 default 替换为具有您要分配的 IP 地址的子网的名称。这些子网是常规子网,而非代理专用子网。

  2. 通过修改 cross-regional-gateway.yaml 文件中的 spec.addresses 部分来更新网关清单:

    # cross-regional-gateway-static-ip.yaml
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1
    metadata:
      name: internal-cross-region-gateway # Or a new name if deploying alongside
      namespace: store
    spec:
      gatewayClassName: gke-l7-cross-regional-internal-managed-mc
      addresses:
      - type: networking.gke.io/named-address-with-region # Use for named static IP
        value: "regions/us-west1/addresses/cross-region-gw-ip-west"
      - type: networking.gke.io/named-address-with-region
        value: "regions/us-east1/addresses/cross-region-gw-ip-east"
      listeners:
      - name: http
        protocol: HTTP
        port: 80
        allowedRoutes:
          kinds:
          - kind: HTTPRoute
    
  3. 应用更新后的网关清单。

    kubectl apply --context gke-west1 -f cross-regional-gateway.yaml
    

非默认子网的特殊注意事项

使用非默认子网时,请注意以下事项:

  • 同一 VPC 网络:所有用户创建的资源(例如静态 IP 地址、代理专用子网和 GKE 集群)都必须位于同一 VPC 网络中。

  • 地址子网:为网关创建静态 IP 地址时,系统会从指定区域中的常规子网分配这些地址。

  • 集群子网命名:每个区域都必须有一个子网,其名称与 MCG 配置集群所在的子网相同。

    • 例如,如果您的 gke-west-1 配置集群位于 projects/YOUR_PROJECT/regions/us-west1/subnetworks/my-custom-subnet 中,则您要请求地址的区域也必须具有 my-custom-subnet 子网。如果您请求 us-east1us-centra1 区域中的地址,则还必须在这些区域中存在名为 my-custom-subnet 的子网。

清理

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

  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
    

问题排查

内部 Gateway 的代理专用子网不存在

如果您的内部 Gateway 上出现以下事件,则该区域不存在代理专用子网。如需解决此问题,请部署代理专用子网。

generic::invalid_argument: error ensuring load balancer: Insert: Invalid value for field 'resource.target': 'regions/us-west1/targetHttpProxies/gkegw-x5vt-default-internal-http-2jzr7e3xclhj'. A reserved and active subnetwork is required in the same region and VPC as the forwarding rule.

没有健康的上行

具体情况:

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

no healthy upstream

原因:

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

临时解决方法:

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

后续步骤