配置弹性跨区域高可用性

本文档介绍如何使用 Google Kubernetes Engine (GKE) 多集群推理网关和 GKE 自动扩缩功能,为 AI 推理工作负载配置弹性跨区域高可用性。通过此设置,您可以智能地对不同区域中的多个 GKE 集群的工作负载进行负载平衡。

如需详细了解 GKE 多集群推理网关, 请参阅 GKE 多集群推理网关简介

准备工作

  1. 启用 Google Kubernetes Engine API。

    启用 Google Kubernetes Engine API

  2. 如果您计划使用 Google Cloud CLI 执行此任务,请安装并初始化该工具。

  3. 确保您的项目具有足够的 H100 GPU 配额。如需了解详情,请参阅GPU 配额资源 分配

  4. 使用 GKE 1.34.1-gke.1127000 版或更高版本。

  5. 使用 gcloud CLI 480.0.0 版或更高版本。

  6. 确保节点使用的服务帐号具有 roles/monitoring.metricWriterroles/stackdriver.resourceMetadata.writer 权限。

  7. 确保您对项目具有 roles/container.adminroles/iam.serviceAccountAdmin Identity and Access Management (IAM) 角色。

  8. 完成以下 Hugging Face 前提条件:

    1. 创建 Hugging Face 账号。
    2. 在 Hugging Face 上申请并获得 Llama 3.1 模型的访问权限。
    3. 在 Hugging Face 上模型的页面中签署许可协议。
    4. 生成至少具有读取权限的 Hugging Face 访问令牌。

创建集群和节点池

如需在不同区域中创建两个 GKE 集群并配置其节点池,请按以下步骤操作:

  1. 创建第一个集群:

    gcloud container clusters create gke-west --zone \
        CLUSTER_1_ZONE \
        --project=PROJECT_ID \
        --gateway-api=standard \
        --cluster-version=GKE_VERSION \
        --machine-type="MACHINE_TYPE" \
        --disk-type="DISK_TYPE" \
        --enable-managed-prometheus --monitoring=SYSTEM,DCGM \
        --hpa-profile=performance \
        --workload-pool=PROJECT_ID. \
        --async
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
    • GKE_VERSION:要使用的 GKE 版本,例如 1.34.1-gke.1127000
    • MACHINE_TYPE:集群节点的机器类型,例如 c2-standard-16
    • DISK_TYPE:集群节点的磁盘类型,例如 pd-standard
  2. 在第一个集群中创建 H100 节点池:

    gcloud container node-pools create h100 \
        --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 \
        --min-nodes=MIN_NUM_NODES \
        --max-nodes=MAX_NUM_NODES \
        --enable-autoscaling \
        --async
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • NODE_POOL_MACHINE_TYPE:节点池的机器类型,例如 a3-highgpu-2g
    • NUM_NODES:节点池中的节点数,例如 3
    • MIN_NUM_NODES:节点池中自动扩缩的最小节点数,例如 1
    • MAX_NUM_NODES:节点池中自动扩缩的最大节点数,例如 10
  3. 在第一个集群中获取凭据并创建 Hugging Face 令牌 Secret:

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

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • CLUSTER_1_NAME:第一个集群的名称,例如 gke-west
    • CLUSTER_1_ZONE:第一个集群的可用区,例如 europe-west3-c
    • HF_TOKEN:您的 Hugging Face 访问令牌
  4. 在与第一个集群不同的区域中创建第二个集群:

    gcloud container clusters create gke-east --zone CLUSTER_2_ZONE \
        --project=PROJECT_ID \
        --gateway-api=standard \
        --cluster-version=GKE_VERSION \
        --machine-type="MACHINE_TYPE" \
        --disk-type="DISK_TYPE" \
        --enable-managed-prometheus \
        --monitoring=SYSTEM,DCGM \
        --hpa-profile=performance \
        --workload-pool=PROJECT_ID. \
        --async
    

    CLUSTER_2_ZONE 替换为第二个集群的可用区,例如 us-east4-a

  5. 为第二个集群创建 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 \
        --min-nodes=MIN_NUM_NODES \
        --max-nodes=MAX_NUM_NODES \
        --enable-autoscaling \
        --async
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • CLUSTER_2_ZONE:第二个集群的可用区,例如 us-east4-a
    • CLUSTER_2_NAME:第二个集群的名称,例如 gke-east
    • NODE_POOL_MACHINE_TYPE:节点池的机器类型,例如 a3-highgpu-2g
    • NUM_NODES:节点池中的节点数,例如 3
    • MIN_NUM_NODES:节点池中自动扩缩的最小节点数,例如 1
    • MAX_NUM_NODES:节点池中自动扩缩的最大节点数,例如 10
  6. 在第二个集群中获取凭据并为 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
    

    替换以下内容。如需了解其他变量的定义,请参阅前面的步骤:

    • HF_TOKEN:您的 Hugging Face 访问令牌

将集群注册到舰队

  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_2_NAME:第二个集群的名称,例如 gke-east
  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

创建代理专用子网

警告 Google Cloud :允许每个 VPC 网络在每个区域中仅有一个 代理专用子网。如果目标区域已包含设置了 purpose=REGIONAL_MANAGED_PROXY 的代理专用子网,则创建 GLOBAL_MANAGED_PROXY 子网会失败。您必须先删除现有的区域级代理专用子网。删除区域级代理专用子网会影响该区域中使用它的任何基于 Envoy 的区域级负载平衡器,因此请相应地规划更改。

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

    gcloud compute networks subnets create CLUSTER_1_REGION-subnet \
        --purpose=GLOBAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=CLUSTER_1_REGION \
        --network=default \
        --range=SUBNET_RANGE_1 \
        --project=PROJECT_ID
    

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • CLUSTER_1_REGION:第一个集群的区域,例如 europe-west3
    • SUBNET_RANGE_1:第一个集群区域中代理专用子网的子网 IP 范围,例如 10.0.0.0/23
  2. 在第二个集群的区域中创建子网:

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

    替换以下内容:

    • PROJECT_ID:您的项目 ID
    • CLUSTER_2_REGION:第二个集群的区域,例如 us-east4
    • SUBNET_RANGE_2:第二个集群区域中代理专用子网的子网 IP 范围,例如 10.5.0.0/23

安装所需的自定义资源

  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. 在两个集群上安装 InferencePool 和 InferenceObjective 自定义资源:

    kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/v1.0.1/manifests.yaml --context=$CLUSTER1_CONTEXT
    
    kubectl apply -f https://github.com/kubernetes-sigs/gateway-api-inference-extension/releases/download/v1.0.1/manifests.yaml --context=$CLUSTER2_CONTEXT
    

将资源部署到目标集群

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

    kubectl apply -f \
    https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/release-1.0/config/manifests/vllm/gpu-deployment.yaml \
    --context=$CLUSTER1_CONTEXT
    
    kubectl apply -f \
    https://raw.githubusercontent.com/kubernetes-sigs/gateway-api-inference-extension/release-1.0/config/manifests/vllm/gpu-deployment.yaml \
    --context=$CLUSTER2_CONTEXT
    
  2. 将以下清单保存到名为 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
    
  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.0.1 \
      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.0.1 \
      oci://registry.k8s.io/gateway-api-inference-extension/charts/inferencepool
    
  5. 在这两个集群中,将 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
    

部署跨区域推理网关

  1. 将以下清单另存为 mygateway.yaml

    ---
    kind: Gateway
    apiVersion: gateway.networking.k8s.io/v1beta1
    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
        allowedRoutes:
          kinds:
          -   kind: HTTPRoute
          namespaces:
            from: All
    ---
    apiVersion: gateway.networking.k8s.io/v1beta1
    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
    ---
    kind: HealthCheckPolicy
    apiVersion: networking.gke.io/v1
    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 mygateway.yaml --context=CLUSTER1_CONTEXT
    

    替换以下内容:

    • CLUSTER1_CONTEXT:第一个集群的上下文,例如 gke_my-project_europe-west3-c_gke-west

启用自定义指标报告

  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
          exportName: kv-cache
        -   name: vllm:gpu_cache_usage_perc
          exportName: kv-cache-old
    
  2. 对于每个集群,应用指标配置:

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

    如需了解 CLUSTER1_CONTEXTCLUSTER2_CONTEXT 的定义,请参阅 安装所需的自定义资源

配置负载均衡政策

本部分介绍如何配置负载均衡政策。此政策定义了如何根据自定义指标和地区偏好设置在推理池之间分配流量。以下配置将 us-east4 设置为首选区域。只有当 us-east4 区域中的 gke.named_metrics.kv-cache 自定义指标的利用率达到 80% 时,流量才会溢出到其他区域。

  • 对于 vLLM v0.10.2 及更高版本,请使用 gke.named_metrics.kv-cache 指标。
  • 对于早期版本,请使用 gke.named_metrics.kv-cache-old 指标。
  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:
        timeoutSec: 600
        balancingMode: CUSTOM_METRICS
        trafficDuration: LONG
        customMetrics:
        -   name: gke.named_metrics.kv-cache
          maxUtilizationPercent: 80
          dryRun: false
        scopes:
        -   selector:
            gke.io/region: "us-east4"
          backendPreference: PREFERRED
    
  2. 应用新政策:

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

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

配置自动扩缩

为了帮助确保每个集群都能处理不断增加的负载,然后再将流量溢出到其他区域,您需要为模型服务器部署配置 Pod 横向自动扩缩器 (HPA)。

主要配置原则

  • 使用相同的自定义指标:应将 HPA 配置为根据您在GCPBackendPolicy中使用的相同自定义指标(例如 vllm:kv_cache_usage_perc)进行伸缩,以用于多集群推理网关。这种方法有助于确保负载均衡和伸缩决策都由推理服务器的同一信号驱动。您选择的指标的值必须介于 0 和 1 之间,以表示利用率。如果指标值大于 1,则负载均衡器会将其解读为 100% 利用率,这可能会导致意外的路由行为。

  • 设置较低的 HPA 目标:HPA 配置中指标的目标值必须设置为低于 maxUtilizationPercent 设置,该设置定义在 GCPBackendPolicy 中。通过将 HPA 的目标利用率设置为较低的值(例如,HPA 在平均利用率为 50% 时进行扩缩),您可以让集群在达到负载均衡器的阈值(例如,利用率为 80%)之前添加更多副本。这种方法有助于最大限度地提高首选区域内的容量。较低的利用率目标还有助于防止过早的流量溢出,因为当当前区域真正接近其限制时,它会保留弹性跨区域高可用性。

  1. 为您的用户授予创建所需授权角色的权限:

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin --user "$(gcloud config get-value account)" --context=CLUSTER1_CONTEXT
    
    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin --user "$(gcloud config get-value account)" --context=CLUSTER2_CONTEXT
    

    CLUSTER1_CONTEXTCLUSTER2_CONTEXT 变量在 安装所需的自定义资源 部分中定义。

  2. 对于每个集群,应用清单:

    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml --context=CLUSTER1_CONTEXT
    
    kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-stackdriver/master/custom-metrics-stackdriver-adapter/deploy/production/adapter_new_resource_model.yaml --context=CLUSTER2_CONTEXT
    
  3. 允许 custom-metrics-stackdriver-adapter 服务帐号读取 Cloud Monitoring 指标:

    PROJECT_NUMBER=$(gcloud projects describe PROJECT_ID --format="value(projectNumber)")
    
    gcloud projects add-iam-policy-binding projects/PROJECT_ID \
      --role roles/monitoring.viewer \
      --member=principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID./subject/ns/custom-metrics/sa/custom-metrics-stackdriver-adapter
    

    PROJECT_ID 替换为您的项目 ID

  4. 将以下清单保存到名为 pod-monitoring.yaml 的文件中:

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: inference-server-podmon
    spec:
      selector:
        matchLabels:
          app: vllm-llama3-8b-instruct
      endpoints:
      -   port: 8000
        path: /metrics
        interval: 5s
    
  5. 将清单应用于两个集群:

    kubectl apply -f pod-monitoring.yaml --context=CLUSTER1_CONTEXT
    kubectl apply -f pod-monitoring.yaml --context=CLUSTER2_CONTEXT
    
  6. 将以下清单保存到名为 hpa.yaml 的文件中:

    apiVersion: autoscaling/v2
    kind: HorizontalPodAutoscaler
    metadata:
      name: inference-server-hpa
    spec:
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: vllm-llama3-8b-instruct
      minReplicas: 1
      maxReplicas: 10
      metrics:
      -   type: Pods
        pods:
          metric:
            name: prometheus.googleapis.com|vllm:gpu_cache_usage_perc|gauge
          target:
            type: AverageValue
            averageValue: "0.5"
    
  7. 将清单应用于两个集群:

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

验证 Deployment

  1. 获取网关 IP 地址:

    export GW_IP=$(kubectl get gateway/cross-region-gateway -n default --context=CLUSTER1_CONTEXT -o jsonpath='{.status.addresses[0].value}')
    
    echo ${GW_IP}
    

    CLUSTER1_CONTEXT 变量在 安装所需的自定义资源部分中定义。

  2. 在临时 Pod 中启动交互式 sh 会话:

    kubectl run -it --rm --image=curlimages/curl curly --context=CLUSTER1_CONTEXT -- /bin/sh
    

    CLUSTER1_CONTEXT 变量在 安装所需的自定义资源部分中定义。

  3. curly Pod 中,发送测试请求:

    curl -i -X POST <var>GW_IP</var>: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
    }'
    

    GW_IP 替换为上一步中的网关 IP 地址。

对网关执行负载测试

  1. 使用同一 VPC 网络中的负载生成器,对网关 IP 地址应用持续负载。

  2. 首先使用您预计首选区域 (us-east4) 可以处理的适中负载。

  3. 逐渐提高负载测试的请求速率或并发数。

  4. 在负载测试运行时,在 Google Cloud 控制台中或 使用 kubectl 监控系统:

    • Pod 伸缩 (HPA):检查两个集群中 vllm-llama3-8b-instruct Deployment 中的 Pod 数量。
    • 节点伸缩(集群自动伸缩器):监控两个集群中 h100节点池中的节点数。
    • 自定义指标:在 Monitoring 中监控两个集群中模型服务器部署的 vllm:kv_cache_usage_perc 指标(适用于 vllm v0.10.2 及更高版本)或 vllm:gpu_cache_usage_perc 指标(适用于 vllm 版本低于 v.0.10.2)。
    • 负载平衡器指标:检查 Monitoring 中与 cross-region-gateway 关联的负载平衡器 的指标。

随着负载的增加,us-east4 中的利用率也会增加。当 gke-east 集群中的 HPA 横向扩容,并且平均利用率接近 GCPBackendPolicy 中定义的 maxUtilization 值 (80%) 时,负载平衡器会开始将请求路由到 europe-west3 中的 gke-west 集群。

后续步骤