设置多集群 GKE Inference Gateway

本文档介绍了如何设置多集群 Google Kubernetes Engine (GKE) 推理网关,以在多个 GKE 集群(可以跨不同区域)之间智能地对 AI/机器学习推理工作负载进行负载均衡。此设置使用 Gateway API、多集群 Ingress 和 InferencePool、InferenceObjective 等自定义资源来提高模型服务部署的可伸缩性,帮助确保高可用性,并优化资源利用率。

为了理解本文档,请熟悉以下内容:

本文档适用于以下角色:

  • 希望使用 GKE 的容器编排功能处理 AI/机器学习工作负载的机器学习 (ML) 工程师、平台管理员和运维人员,以及数据和 AI 专家。
  • 与 GKE 网络交互的云架构师或网络专家。

如需详细了解我们在Google Cloud 内容中提及的常见角色和示例任务,请参阅常见的 GKE Enterprise 用户角色和任务

准备工作

在开始之前,请确保您已执行以下任务:

  • 启用 Google Kubernetes Engine API。
  • 启用 Google Kubernetes Engine API
  • 如果您要使用 Google Cloud CLI 执行此任务,请安装初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请通过运行 gcloud components update 命令来获取最新版本。较早版本的 gcloud CLI 可能不支持运行本文档中的命令。
  • 启用 Compute Engine API、Kubernetes Engine API、Model Armor 和 Network Services API。

    前往启用对 API 的访问,然后按照说明操作。

  • 启用 Autoscaling API。

    前往自动扩缩 API,然后按照说明操作。

  • 启用 GKE Hub API。

    前往 GKE Hub API,然后按照说明操作。

  • Hugging Face 前提条件:

    • 如果您还没有 Hugging Face 账号,请创建一个。
    • 在 Hugging Face 上申请并获得对 Llama 3.1 模型的访问权限。
    • 在 Hugging Face 上相应模型的页面上签署许可同意协议。
    • 生成至少具有 Read 权限的 Hugging Face 访问令牌。

要求

  • 确保您的项目具有足够的 H100 GPU 配额。如需了解详情,请参阅规划 GPU 配额分配配额
  • 使用 GKE 1.34.1-gke.1127000 版或更高版本。
  • 使用 gcloud CLI 480.0.0 版或更高版本。
  • 您的节点服务账号必须有权向 Autoscaling API 写入指标
  • 您必须拥有项目的以下 IAM 角色:roles/container.adminroles/iam.serviceAccountAdmin

多端口和 NEG 限制

在多集群设置中部署多端口 InferencePool 资源时,请考虑 Google Cloud 后端服务 NEG 限制。每个地区中的每个端口都会创建一个专用 NEG。例如,如果某个区域级集群包含三个可用区,并且某个 InferencePool 配置了 8 个端口,则该集群将使用 24 个 NEG。由于一个后端服务最多只能包含 50 个 NEG,因此您最多只能从两个集群聚合此特定 InferencePool,否则就会达到上限。

设置多集群推理网关

如需设置多集群 GKE 推理网关,请按以下步骤操作:

创建集群和节点池

如需托管 AI/ML 推理工作负载并实现跨区域负载均衡,请在不同区域中创建两个 GKE 集群,每个集群都包含一个 H100 GPU 节点池。

  1. 创建第一个集群:

    gcloud container clusters create CLUSTER_1_NAME \
        --region LOCATION \
        --project=PROJECT_ID \
        --gateway-api=standard \
        --release-channel "rapid" \
        --cluster-version=GKE_VERSION \
        --machine-type="MACHINE_TYPE" \
        --disk-type="DISK_TYPE" \
        --enable-managed-prometheus --monitoring=SYSTEM,DCGM \
        --hpa-profile=performance \
        --async # Allows the command to return immediately
    

    替换以下内容:

    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • LOCATION:第一个集群的区域,例如 europe-west3
    • PROJECT_ID:您的项目 ID。
    • GKE_VERSION:要使用的 GKE 版本,例如 1.34.1-gke.1127000
    • MACHINE_TYPE:集群节点的机器类型,例如 c2-standard-16
    • DISK_TYPE:集群节点的磁盘类型,例如 pd-standard
  2. 为第一个集群创建 H100 节点池:

    gcloud container node-pools create NODE_POOL_NAME \
        --accelerator "type=nvidia-h100-80gb,count=2,gpu-driver-version=latest" \
        --project=PROJECT_ID \
        --location=CLUSTER_1_ZONE \
        --node-locations=CLUSTER_1_ZONE \
        --cluster=CLUSTER_1_NAME \
        --machine-type=NODE_POOL_MACHINE_TYPE \
        --num-nodes=NUM_NODES \
        --spot \
        --async # Allows the command to return immediately
    

    替换以下内容:

    • NODE_POOL_NAME:节点池的名称,例如 h100
    • PROJECT_ID:您的项目 ID。
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • NODE_POOL_MACHINE_TYPE:节点池的机器类型,例如 a3-highgpu-2g
    • NUM_NODES:节点池中的节点数,例如 3
  3. 获取凭据:

    gcloud container clusters get-credentials CLUSTER_1_NAME \
        --location CLUSTER_1_ZONE \
        --project=PROJECT_ID
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID。
    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
  4. 在第一个集群上,为 Hugging Face 令牌创建一个 Secret:

    kubectl create secret generic hf-token \
        --from-literal=token=HF_TOKEN
    

    HF_TOKEN 替换为您的 Hugging Face 访问令牌。

  5. 在与第一个集群不同的区域中创建第二个集群:

    gcloud container clusters create gke-east --region LOCATION \
        --project=PROJECT_ID \
        --gateway-api=standard \
        --release-channel "rapid" \
        --cluster-version=GKE_VERSION \
        --machine-type="MACHINE_TYPE" \
        --disk-type="DISK_TYPE" \
        --enable-managed-prometheus \
        --monitoring=SYSTEM,DCGM \
        --hpa-profile=performance \
        --async # Allows the command to return immediately while the
    cluster is created in the background.
    

    替换以下内容:

    • LOCATION:第二个集群的区域。 此区域必须与第一个集群的区域不同。例如 us-east4
    • PROJECT_ID:您的项目 ID。
    • GKE_VERSION:要使用的 GKE 版本,例如 1.34.1-gke.1127000
    • MACHINE_TYPE:集群节点的机器类型,例如 c2-standard-16
    • DISK_TYPE:集群节点的磁盘类型,例如 pd-standard
  6. 为第二个集群创建 H100 节点池:

    gcloud container node-pools create h100 \
        --accelerator "type=nvidia-h100-80gb,count=2,gpu-driver-version=latest" \
        --project=PROJECT_ID \
        --location=CLUSTER_2_ZONE \
        --node-locations=CLUSTER_2_ZONE \
        --cluster=CLUSTER_2_NAME \
        --machine-type=NODE_POOL_MACHINE_TYPE \
        --num-nodes=NUM_NODES \
        --spot \
        --async # Allows the command to return immediately
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID。
    • CLUSTER_2_ZONE:第二个集群的可用区,例如 us-east4-a
    • CLUSTER_2_NAME:第二个集群的名称,例如 gke-east
    • NODE_POOL_MACHINE_TYPE:节点池的机器类型,例如 a3-highgpu-2g
    • NUM_NODES:节点池中的节点数,例如 3
  7. 对于第二个集群,获取凭据并为 Hugging Face 令牌创建 Secret:

    gcloud container clusters get-credentials CLUSTER_2_NAME \
        --location CLUSTER_2_ZONE \
        --project=PROJECT_ID
    
    kubectl create secret generic hf-token --from-literal=token=HF_TOKEN
    

    替换以下内容:

    • CLUSTER_2_NAME:第二个集群的名称,例如 gke-east
    • CLUSTER_2_ZONE:第二个集群的可用区,例如 us-east4-a
    • PROJECT_ID:您的项目 ID。
    • HF_TOKEN:您的 Hugging Face 访问令牌。

将集群注册到舰队

如需启用多集群功能(例如多集群 GKE 推理网关),请将集群注册到舰队。

  1. 将这两个集群都注册到项目的舰队:

    gcloud container fleet memberships register CLUSTER_1_NAME \
        --gke-cluster CLUSTER_1_ZONE/CLUSTER_1_NAME \
        --location=global \
        --project=PROJECT_ID
    
    gcloud container fleet memberships register CLUSTER_2_NAME \
        --gke-cluster CLUSTER_2_ZONE/CLUSTER_2_NAME \
        --location=global \
        --project=PROJECT_ID
    

    替换以下内容:

    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
    • PROJECT_ID:您的项目 ID。
    • CLUSTER_2_NAME:第二个集群的名称,例如 gke-east
    • CLUSTER_2_ZONE:第二个集群的可用区,例如 us-east4-a
  2. 如需允许单个网关管理多个集群之间的流量,请启用多集群 Ingress 功能并指定配置集群:

    gcloud container fleet ingress enable \
        --config-membership=projects/PROJECT_ID/locations/global/memberships/CLUSTER_1_NAME
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID。
    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west

创建代理专用子网

对于内部网关,请在每个区域中创建一个代理专用子网。内部网关的 Envoy 代理使用这些专用子网来处理 VPC 网络内的流量。

  1. 在第一个集群的区域中创建子网:

    gcloud compute networks subnets create CLUSTER_1_REGION-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=CLUSTER_1_REGION \
        --network=default \
        --range=10.0.0.0/23 \
        --project=PROJECT_ID
    
  2. 在第二个集群的区域中创建子网:

    gcloud compute networks subnets create CLUSTER_2_REGION-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=CLUSTER_2_REGION \
        --network=default \
        --range=10.5.0.0/23 \
        --project=PROJECT_ID
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID。
    • CLUSTER_1_REGION:第一个集群所在的区域,例如 europe-west3
    • CLUSTER_2_REGION:第二个集群所在的区域,例如 us-east4

安装所需的 CRD

多集群 GKE 推理网关使用 InferencePool 和 InferenceObjective 等自定义资源。GKE Gateway API 控制器用于管理 InferencePool 自定义资源定义 (CRD)。不过,您必须在集群上手动安装处于 Alpha 版的 InferenceObjective CRD。

  1. 为集群定义上下文变量:

    CLUSTER1_CONTEXT="gke_PROJECT_ID_CLUSTER_1_ZONE_CLUSTER_1_NAME"
    CLUSTER2_CONTEXT="gke_PROJECT_ID_CLUSTER_2_ZONE_CLUSTER_2_NAME"
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID。
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • CLUSTER_2_ZONE:第二个集群的可用区,例如 us-east4-a
    • CLUSTER_2_NAME:第二个集群的名称,例如 gke-east
  2. 在两个集群上安装 InferenceObjective CRD:

    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/crd/bases/inference.networking.x-k8s.io_inferenceobjectives.yaml --context=$CLUSTER1_CONTEXT
    
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/crd/bases/inference.networking.x-k8s.io_inferenceobjectives.yaml --context=$CLUSTER2_CONTEXT
    

将资源部署到目标集群

为了在每个集群上提供 AI/ML 推理工作负载,请部署所需的资源,例如模型服务器和 InferenceObjective 自定义资源。

  1. 将模型服务器部署到这两个集群:

    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/manifests/vllm/gpu-deployment.yaml --context=CLUSTER1_CONTEXT
    
    kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/v1.1.0/config/manifests/vllm/gpu-deployment.yaml --context=CLUSTER2_CONTEXT
    

    替换以下内容:

    • CLUSTER1_CONTEXT:第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west
    • CLUSTER2_CONTEXT:第二个集群的上下文,例如 gke_my-project_us-east4-a_gke-east
  2. 将 InferenceObjective 资源部署到这两个集群。将以下示例清单保存到名为 inference-objective.yaml 的文件中:

    apiVersion: inference.networking.x-k8s.io/v1alpha2
    kind: InferenceObjective
    metadata:
      name: food-review
    spec:
      priority: 10
      poolRef:
        name: llama3-8b-instruct
        group: "inference.networking.k8s.io"
    
  3. 将清单应用于两个集群:

    kubectl apply -f inference-objective.yaml --context=CLUSTER1_CONTEXT
    kubectl apply -f inference-objective.yaml --context=CLUSTER2_CONTEXT
    

    替换以下内容:

    • CLUSTER1_CONTEXT:第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west
    • CLUSTER2_CONTEXT:第二个集群的上下文,例如 gke_my-project_us-east4-a_gke-east
  4. 使用 Helm 将 InferencePool 资源部署到这两个集群:

      helm install vllm-llama3-8b-instruct \
      --kube-context CLUSTER1_CONTEXT \
      --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
      --set provider.name=gke \
      --set inferenceExtension.monitoring.gke.enabled=true \
      --version v1.1.0 \
      oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
    
    helm install vllm-llama3-8b-instruct \
      --kube-context CLUSTER2_CONTEXT \
      --set inferencePool.modelServers.matchLabels.app=vllm-llama3-8b-instruct \
      --set provider.name=gke \
      --set inferenceExtension.monitoring.gke.enabled=true \
      --version v1.1.0 \
      oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
    

    替换以下内容:

    • CLUSTER1_CONTEXT:第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west
    • CLUSTER2_CONTEXT:第二个集群的上下文,例如 gke_my-project_us-east4-a_gke-east
  5. 在两个集群上将 InferencePool 资源标记为已导出。此注解可让配置集群导入 InferencePool,这是多集群路由的必要步骤。

    kubectl annotate inferencepool vllm-llama3-8b-instruct networking.gke.io/export="True" \
        --context=CLUSTER1_CONTEXT
    
    kubectl annotate inferencepool vllm-llama3-8b-instruct networking.gke.io/export="True" \
        --context=CLUSTER2_CONTEXT
    

    替换以下内容:

    • CLUSTER1_CONTEXT:第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west
    • CLUSTER2_CONTEXT:第二个集群的上下文,例如 gke_my-project_us-east4-a_gke-east

将资源部署到配置集群

如需定义如何在所有已注册集群中的 InferencePool 资源之间路由和负载均衡流量,请部署 GatewayHTTPRouteHealthCheckPolicy 资源。您将这些资源部署到指定的配置集群(本文档中为 gke-west)。

  1. 创建一个名为 mcig.yaml 的文件,其中包含以下内容:

    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: cross-region-gateway
      namespace: default
    spec:
      gatewayClassName: gke-l7-cross-regional-internal-managed-mc
      addresses:
      - type: networking.gke.io/ephemeral-ipv4-address/europe-west3
        value: "europe-west3"
      - type: networking.gke.io/ephemeral-ipv4-address/us-east4
        value: "us-east4"
      listeners:
      - name: http
        protocol: HTTP
        port: 80
    ---
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: vllm-llama3-8b-instruct-default
    spec:
      parentRefs:
      - name: cross-region-gateway
        kind: Gateway
      rules:
      - backendRefs:
        - group: networking.gke.io
          kind: GCPInferencePoolImport
          name: vllm-llama3-8b-instruct
    ---
    apiVersion: networking.gke.io/v1
    kind: HealthCheckPolicy
    metadata:
      name: health-check-policy
      namespace: default
    spec:
      targetRef:
        group: "networking.gke.io"
        kind: GCPInferencePoolImport
        name: vllm-llama3-8b-instruct
      default:
        config:
          type: HTTP
          httpHealthCheck:
            requestPath: /health
            port: 8000
    
  2. 应用清单:

    kubectl apply -f mcig.yaml --context=CLUSTER1_CONTEXT
    

    CLUSTER1_CONTEXT 替换为第一个集群(配置集群)的上下文,例如 gke_my-project_europe-west3-c_gke-west

启用自定义指标报告

为了启用自定义指标报告并改进跨区域负载均衡,您需要从所有集群导出 KV 缓存使用情况指标。负载均衡器使用此导出的 KV 缓存使用情况数据作为自定义负载信号。使用此自定义负载信号可根据每个集群的实际工作负载做出更智能的负载均衡决策。

  1. 创建一个名为 metrics.yaml 的文件,其中包含以下内容:

    apiVersion: autoscaling.gke.io/v1beta1
    kind: AutoscalingMetric
    metadata:
      name: gpu-cache
      namespace: default
    spec:
      selector:
        matchLabels:
          app: vllm-llama3-8b-instruct
      endpoints:
      - port: 8000
        path: /metrics
        metrics:
        - name: vllm:kv_cache_usage_perc # For vLLM versions v0.10.2 and newer
          exportName: kv-cache
        - name: vllm:gpu_cache_usage_perc # For vLLM versions v0.6.2 and newer
          exportName: kv-cache-old
    
  2. 将指标配置应用于这两个集群:

    kubectl apply -f metrics.yaml --context=CLUSTER1_CONTEXT
    kubectl apply -f metrics.yaml --context=CLUSTER2_CONTEXT
    

    替换以下内容:

    • CLUSTER1_CONTEXT:第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west
    • CLUSTER2_CONTEXT:第二个集群的上下文,例如 gke_my-project_us-east4-a_gke-east

配置负载均衡政策

如需优化 AI/ML 推理请求在 GKE 集群中的分配方式,请配置负载均衡政策。选择合适的均衡模式有助于确保高效利用资源、防止单个集群过载,并提高推理服务的整体性能和响应速度。

配置超时

如果您的请求预计会持续很长时间,请为负载均衡器配置更长的超时时间。在 GCPBackendPolicy 中,将 timeoutSec 字段设置为至少为估计的 P99 请求延迟时间的两倍。

例如,以下清单将负载均衡器超时时间设置为 100 秒。

apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
  name: my-backend-policy
spec:
  targetRef:
    group: "networking.gke.io"
    kind: GCPInferencePoolImport
    name: vllm-llama3-8b-instruct
  default:
    timeoutSec: 100
    balancingMode: CUSTOM_METRICS
    trafficDuration: LONG
    customMetrics:
      - name: gke.named_metrics.kv-cache
        dryRun: false
        maxUtilizationPercent: 60

如需了解详情,请参阅多集群网关限制

由于自定义指标正在处理的请求数这两种负载均衡模式互斥,因此请在 GCPBackendPolicy 中仅配置其中一种模式。

为部署选择负载均衡模式。

自定义指标

为了实现最佳负载均衡,请从 60% 的目标利用率开始。如需实现此目标,请在 GCPBackendPolicycustomMetrics 配置中设置 maxUtilizationPercent: 60

  1. 创建一个名为 backend-policy.yaml 的文件,其中包含以下内容,以根据 kv-cache 自定义指标启用负载均衡:

    apiVersion: networking.gke.io/v1
    kind: GCPBackendPolicy
    metadata:
      name: my-backend-policy
    spec:
      targetRef:
        group: "networking.gke.io"
        kind: GCPInferencePoolImport
        name: vllm-llama3-8b-instruct
      default:
        balancingMode: CUSTOM_METRICS
        trafficDuration: LONG
        customMetrics:
          - name: gke.named_metrics.kv-cache
            dryRun: false
            maxUtilizationPercent: 60
    
  2. 应用新政策:

    kubectl apply -f backend-policy.yaml --context=CLUSTER1_CONTEXT
    

    CLUSTER1_CONTEXT 替换为第一个集群的上下文,例如 gke_my-project-europe-west3-c-gke-west

正在处理的请求

如需使用动态均衡模式,请估计每个后端可以处理的动态请求数量,并明确配置容量值。

  1. 创建一个名为 backend-policy.yaml 的文件,其中包含以下内容,以实现基于正在处理的请求数量的负载均衡:

    kind: GCPBackendPolicy
    apiVersion: networking.gke.io/v1
    metadata:
      name: my-backend-policy
    spec:
      targetRef:
        group: "networking.gke.io"
        kind: GCPInferencePoolImport
        name: vllm-llama3-8b-instruct
      default:
        balancingMode: IN_FLIGHT
        trafficDuration: LONG
        maxInFlightRequestsPerEndpoint: 1000
        dryRun: false
    
  2. 应用新政策:

    kubectl apply -f backend-policy.yaml --context=CLUSTER1_CONTEXT
    

    CLUSTER1_CONTEXT 替换为第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west

验证 Deployment

如需验证内部负载均衡器,您必须从 VPC 网络内部发送请求,因为内部负载均衡器使用专用 IP 地址。在其中一个集群内运行临时 Pod,以从您的 VPC 网络发送请求并验证内部负载均衡器:

  1. 在新 shell 中,获取网关 IP 地址:

    GW_IP=$(kubectl get gateway/cross-region-gateway -n default --context=$CLUSTER1_CONTEXT -o jsonpath='{.status.addresses[0].value}')
    
  2. 从集群内的临时 Pod 发送测试请求:

    kubectl run -it --rm --image=curlimages/curl curly --context=$CLUSTER1_CONTEXT -- \
      curl -i -X POST ${GW_IP}:80/v1/completions -H 'Content-Type: application/json' -d '{
      "model": "food-review-1",
      "prompt": "What is the best pizza in the world?",
      "max_tokens": 100,
      "temperature": 0
      }'
    

后续步骤