本文档介绍了如何通过配置 Kubernetes Gateway API 和 GKE Inference Gateway,管理 Google Kubernetes Engine (GKE) 上多个 Ray Serve 集群的推理请求。通过此配置,您可以集中管理多个团队的流量,在不同区域分配工作负载以提高容量,并根据请求正文内容实现模型感知路由。
使用 GKE Inference Gateway 和 Ray Serve 的优势
使用 GKE Inference Gateway 和 Ray Serve 具有以下优势:
- 路径路由:为每个 RayService 配置路径前缀,然后使用一个网关路由到多个 Ray 服务。
- 如需详细了解如何设置路径前缀规则,请参阅 Gateway API 文档。
- 模型感知路由:根据 请求正文选择要路由到的 RayService,例如,通过从 OpenAI-API JSON 请求中提取请求的模型。
- 治理:要求使用 API 密钥才能使用您的服务,或者使用 Apigee 进行身份验证和 API 管理,以强制执行 用户 配额。
- 多区域:使用多集群网关将流量拆分到具有 RayService 的多个 GKE 集群,以实现更高的可用性或容量。
- 关注点分离:使用可由 不同团队管理、遵循不同发布流程并在不同拓扑上运行的单独 RayService。
- 安全性:使用网关充当 SSL 终止符,以帮助保护您的用户 流量在互联网上的安全。如需了解详情,请参阅 网关安全性。
如需配置路由,您需要部署网关、HTTPRoute 和 RayService。通常,KubeRay 会为每个目标 Ray 集群创建一个 Kubernetes 服务。 Ray Serve 会在集群内分散请求负载,而无需创建 InferencePool 或端点选择器。
在 GKE 上为 Ray Serve 实现模型感知路由
模型感知路由由基于正文的路由扩展程序启用。基于正文的路由可让您仅根据用户请求中命名的模型将流量定向到不同的 RayService,从而让您拥有一个可以为多个 Ray 集群中托管的多个模型提供服务的端点。您的用户可以简化访问,而您的应用开发者可以控制每个 Ray 端点的配置。
如需配置模型感知路由,请部署以下关键组件:
- 一个基于正文的路由器扩展程序,用于从 JSON 载荷中提取模型名称。 此路由器扩展程序使用 Helm 部署。
- 一个 GKE 网关(L7 区域级内部应用负载平衡器),用于处理传入流量。
- HTTPRoute 规则,用于使用路由器扩展程序填充的标头将流量定向到正确的 Ray 服务。
- 多个 Ray Serve 集群,用于管理孤立模型的生命周期和自动扩缩。
准备工作
在开始之前,请确保您已执行以下任务:
- 启用 Google Kubernetes Engine API。 启用 Google Kubernetes Engine API
- 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请通过运行
gcloud components update命令来获取最新版本。较早版本的 gcloud CLI 可能不支持运行本文档中的命令。
- 确保您已安装 Helm。
- 如果您还没有 Hugging Face 账号,请创建一个。
- 确保您拥有 Hugging Face 令牌。
准备环境
设置环境变量:
export CLUSTER=$(whoami)-ray-bbr
export PROJECT_ID=$(gcloud config get-value project)
export LOCATION=us-central1-b
export REGION=us-central1
export HUGGING_FACE_TOKEN=YOUR_HUGGING_FACE_TOKEN
将 YOUR_HUGGING_FACE_TOKEN 替换为您的 Hugging Face 访问令牌。
准备基础架构
在本部分中,您将设置一个启用 Ray 和网关的 GKE 集群,其中包含 L4 GPU。
创建一个启用了 Ray Operator 和 Gateway API 的集群:
gcloud container clusters create ${CLUSTER} \ --project ${PROJECT_ID} \ --location ${LOCATION} \ --cluster-version 1.35 \ --gateway-api standard \ --addons HttpLoadBalancing,RayOperator \ --enable-ray-cluster-logging \ --enable-ray-cluster-monitoring \ --machine-type e2-standard-4为模型工作负载创建 GPU 节点池:
gcloud container node-pools create gpu-pool \ --cluster=${CLUSTER} \ --location=${LOCATION} \ --accelerator="type=nvidia-l4,count=1,gpu-driver-version=latest" \ --machine-type=g2-standard-8 \ --num-nodes=4为区域级内部应用负载平衡器创建一个代理专用子网,这是基于正文的路由所必需的:
gcloud compute networks subnets create bbr-proxy-only-subnet \ --purpose=REGIONAL_MANAGED_PROXY \ --role=ACTIVE \ --region=${REGION} \ --network=default \ --range=192.168.10.0/24部署 Hugging Face Secret:
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HUGGING_FACE_TOKEN}
部署基于正文的路由器以实现模型感知路由
基于正文的路由器扩展程序会拦截请求、解析 JSON 正文,并将模型字段提取到 X-Gateway-Model-Name 标头中。
创建一个名为
helm-values.yaml的文件,其中包含以下内容:bbr: plugins: - type: "body-field-to-header" name: "openai-model-extractor" json: field_name: "model" header_name: "X-Gateway-Model-Name"使用 Helm 安装基于正文的路由器:
helm install body-based-router \ oci://registry.k8s.io/gateway-api-inference-extension/charts/body-based-routing \ --version v1.4.0 \ --set provider.name=gke \ --set inferenceGateway.name=ray-multi-model-gateway \ --values helm-values.yaml
部署 RayService
如需部署模型,您必须应用 RayService 清单。每个清单定义一个运行特定 LLM 的 Ray 集群。
创建一个名为
gemma-2b-it.yaml的文件,其中包含以下内容:apiVersion: ray.io/v1 kind: RayService metadata: name: gemma-2b-it spec: serveConfigV2: | applications: - name: llm_app route_prefix: "/" import_path: ray.serve.llm:build_openai_app args: llm_configs: - model_loading_config: model_id: gemma-2b-it model_source: google/gemma-2b-it accelerator_type: L4 log_engine_metrics: true deployment_config: autoscaling_config: min_replicas: 2 max_replicas: 2 health_check_period_s: 600 health_check_timeout_s: 300 rayClusterConfig: headGroupSpec: rayStartParams: dashboard-host: "0.0.0.0" num-cpus: "0" template: spec: containers: - name: ray-head image: rayproject/ray-llm:2.54.0-py311-cu128 resources: limits: memory: "8Gi" ephemeral-storage: "32Gi" requests: cpu: "2" memory: "8Gi" ephemeral-storage: "32Gi" ports: - containerPort: 6379 name: gcs-server - containerPort: 8265 name: dashboard - containerPort: 10001 name: client - containerPort: 8000 name: serve env: - name: RAY_SERVE_THROUGHPUT_OPTIMIZED value: "1" - name: RAY_SERVE_ENABLE_HA_PROXY value: "1" - name: HUGGING_FACE_HUB_TOKEN valueFrom: secretKeyRef: name: hf-secret key: hf_api_token rayVersion: 2.54.0 workerGroupSpecs: - replicas: 2 minReplicas: 2 maxReplicas: 2 groupName: gpu-group rayStartParams: {} template: spec: containers: - name: llm image: rayproject/ray-llm:2.54.0-py311-cu128 env: - name: RAY_SERVE_THROUGHPUT_OPTIMIZED value: "1" - name: RAY_SERVE_ENABLE_HA_PROXY value: "1" - name: HUGGING_FACE_HUB_TOKEN valueFrom: secretKeyRef: name: hf-secret key: hf_api_token resources: limits: nvidia.com/gpu: "1" ephemeral-storage: "24Gi" requests: cpu: "6" memory: "24Gi" nvidia.com/gpu: "1" ephemeral-storage: "24Gi" nodeSelector: cloud.google.com/gke-accelerator: nvidia-l4创建一个名为
qwen2.5-3b.yaml的文件,其中包含以下内容:apiVersion: ray.io/v1 kind: RayService metadata: name: qwen-25-3b spec: serveConfigV2: | applications: - name: llm_app route_prefix: "/" import_path: ray.serve.llm:build_openai_app args: llm_configs: - model_loading_config: model_id: qwen-2.5-3b model_source: Qwen/Qwen2.5-3B accelerator_type: L4 log_engine_metrics: true deployment_config: autoscaling_config: min_replicas: 2 max_replicas: 2 health_check_period_s: 600 health_check_timeout_s: 300 rayClusterConfig: headGroupSpec: rayStartParams: dashboard-host: "0.0.0.0" num-cpus: "0" template: spec: containers: - name: ray-head image: rayproject/ray-llm:2.54.0-py311-cu128 resources: limits: memory: "8Gi" ephemeral-storage: "32Gi" requests: cpu: "2" memory: "8Gi" ephemeral-storage: "32Gi" ports: - containerPort: 6379 name: gcs-server - containerPort: 8265 name: dashboard - containerPort: 10001 name: client - containerPort: 8000 name: serve env: - name: RAY_SERVE_THROUGHPUT_OPTIMIZED value: "1" - name: RAY_SERVE_ENABLE_HA_PROXY value: "1" - name: HUGGING_FACE_HUB_TOKEN valueFrom: secretKeyRef: name: hf-secret key: hf_api_token rayVersion: 2.54.0 workerGroupSpecs: - replicas: 2 minReplicas: 2 maxReplicas: 2 groupName: gpu-group rayStartParams: {} template: spec: containers: - name: llm image: rayproject/ray-llm:2.54.0-py311-cu128 env: - name: RAY_SERVE_THROUGHPUT_OPTIMIZED value: "1" - name: RAY_SERVE_ENABLE_HA_PROXY value: "1" - name: HUGGING_FACE_HUB_TOKEN valueFrom: secretKeyRef: name: hf-secret key: hf_api_token resources: limits: nvidia.com/gpu: "1" ephemeral-storage: "24Gi" requests: cpu: "6" memory: "24Gi" nvidia.com/gpu: "1" ephemeral-storage: "24Gi" nodeSelector: cloud.google.com/gke-accelerator: nvidia-l4部署模型:
kubectl apply -f gemma-2b-it.yaml kubectl apply -f qwen2.5-3b.yaml
配置健康检查
为帮助确保负载均衡器准确监控 Ray 工作器健康状况,您必须应用 HealthCheckPolicy 资源。
创建一个名为
healthcheck-policy.yaml的文件,其中包含以下内容:apiVersion: networking.gke.io/v1 kind: HealthCheckPolicy metadata: name: gemma-serve-healthcheck namespace: default spec: default: checkIntervalSec: 5 timeoutSec: 5 healthyThreshold: 2 unhealthyThreshold: 2 config: type: HTTP httpHealthCheck: port: 8000 requestPath: /-/healthz targetRef: group: "" kind: Service name: gemma-2b-it-serve-svc --- apiVersion: networking.gke.io/v1 kind: HealthCheckPolicy metadata: name: qwen-serve-healthcheck namespace: default spec: default: checkIntervalSec: 5 timeoutSec: 5 healthyThreshold: 2 unhealthyThreshold: 2 config: type: HTTP httpHealthCheck: port: 8000 requestPath: /-/healthz targetRef: group: "" kind: Service name: qwen-25-3b-serve-svc应用健康检查政策:
kubectl apply -f healthcheck-policy.yaml
配置路由
如需配置路由,您必须应用 Gateway 和 HTTPRoute 清单。
HTTPRoute 包含与 X-Gateway-Model-Name 标头(由基于正文的路由器填充)匹配的规则,以将流量路由到相应的 Ray 服务。
创建一个名为
gateway.yaml的文件,其中包含以下内容:apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: ray-multi-model-gateway namespace: default spec: gatewayClassName: gke-l7-rilb listeners: - allowedRoutes: namespaces: from: Same name: http port: 80 protocol: HTTP --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: ray-multi-model-route spec: parentRefs: - name: ray-multi-model-gateway rules: - matches: - headers: - type: Exact name: X-Gateway-Model-Name value: gemma-2b-it # Must match model named in JSON request! path: type: PathPrefix value: / backendRefs: - name: gemma-2b-it-serve-svc # Ray service name plus "-serve-svc". kind: Service port: 8000 - matches: - headers: - type: Exact name: X-Gateway-Model-Name value: qwen-2.5-3b # Matches another extracted model name path: type: PathPrefix value: / backendRefs: - name: qwen-25-3b-serve-svc # Target Ray Service. kind: Service port: 8000应用网关和路由:
kubectl apply -f gateway.yaml
测试部署
预配网关且两个 Ray 集群都准备就绪后,您可以通过在 JSON 正文中发送具有不同模型名称的请求来测试路由。
获取网关 IP 地址:
kubectl get gateways ray-multi-model-gateway在可以访问网关地址的网络中启动 shell。 您可以在其中一个 Ray 集群 Pod 上使用 curl:
POD_NAME=$(kubectl get pods -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD_NAME -- bash通过测试到 Gemma 的路由来发送请求:
curl http://GATEWAY_IP_ADDRESS/v1/chat/completions \ --header 'Content-Type: application/json' \ --data '{ "model": "gemma-2b-it", "messages": [{"role": "user", "content": "Tell me about GKE."}] }'将
GATEWAY_IP_ADDRESS替换为上一步中的 IP 地址。输出类似于以下内容:
{"id":"chatcmpl-594f7cab-f991-4522-9829-acdbb65d9f67","object":"chat.completion","created":1776379509,"model":"gemma-2b-it","choices":[{"index":0,"message":{"role":"assistant","content":"**Google Kubernetes Engine (GKE)** is a fully managed container orchestration service for Kubernetes [...]测试到 Qwen 的路由:
curl http://GATEWAY_IP_ADDRESS/v1/chat/completions \ --header 'Content-Type: application/json' \ --data '{ "model": "qwen-2.5-3b", "messages": [{"role": "user", "content": "How does Ray Serve work?"}] }'输出类似于以下内容:
{"id":"chatcmpl-dfe3f3b7-45fc-481c-b53e-2fc09c033cdb","object":"chat.completion","created":1776380249,"model":"qwen-2.5-3b","choices":[{"index":0,"message":{"role":"assistant","content":"Ray Serve facilitates the hosting and deployment of scalable microservices. [...]
基于正文的路由器会自动提取 model 字段的值,并确保每个请求都到达 gateway.yaml 文件中配置的正确后端服务。
清理
删除集群:
gcloud container clusters delete ${CLUSTER}