本文档介绍了如何通过配置 Kubernetes Gateway API 和 GKE Inference Gateway,在 Google Kubernetes Engine (GKE) 上跨多个 Ray Serve 集群管理推理请求。此配置可让您集中管理多个团队的流量,在多个区域分配工作负载以提高容量,并根据请求正文内容实现模型感知型路由。
使用 GKE 推理网关和 Ray Serve 的优势
使用 GKE 推理网关和 Ray Serve 可带来以下优势:
- 路径路由:为每个 RayService 配置一个路径前缀,然后使用一个网关将它们路由到多个 Ray 服务。
- 如需详细了解如何设置路径前缀规则,请参阅 Gateway API 文档。
- 模型感知路由:根据请求正文选择要路由到的 RayService,例如通过从 OpenAI-API JSON 请求中提取所请求的模型。
- 治理:要求使用 API 密钥才能使用您的服务,或使用 Apigee 进行身份验证和 API 管理,以强制执行用户配额。
- 多区域:使用 RayService 通过多集群网关在多个 GKE 集群之间分配流量,以实现更高的可用性或容量。
- 关注点分离:使用单独的 RayService,这些服务可由不同的团队管理,遵循单独的发布流程,并在不同的拓扑上运行。
- 安全性:使用 Gateway 作为 SSL 终结器,有助于确保用户流量在互联网上的安全。如需了解详情,请参阅网关安全性。
如需配置路由,您需要部署网关、HTTPRoute 和 RayService。KubeRay 通常会为每个目标 Ray 集群创建一个 Kubernetes 服务。Ray Serve 在集群内分散请求负载,无需创建 InferencePool 或 Endpoint Picker。
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 和 Gateway 且配备了 L4 GPU 的 GKE 集群。
创建启用了 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}
后续步骤
- 了解 Ray Serve 的性能优化。
- 详细了解 GKE 上的网关。