Questo documento spiega come gestire le richieste di inferenza in più cluster Ray Serve su Google Kubernetes Engine (GKE) configurando l'API Kubernetes Gateway e GKE Inference Gateway. Questa configurazione ti consente di centralizzare la gestione del traffico per più team, distribuire i workload tra le regioni per una maggiore capacità e implementare il routing basato su modello in base al contenuto del corpo della richiesta.
Vantaggi dell'utilizzo di GKE Inference Gateway e Ray Serve
L'utilizzo di GKE Inference Gateway e Ray Serve offre i seguenti vantaggi:
- Routing basato sul percorso: configura ogni RayService con un prefisso del percorso, quindi pubblicalo
con un gateway che esegue il routing a più Ray Services.
- Per saperne di più sulla configurazione delle regole del prefisso del percorso, consulta la documentazione dell'API Gateway.
- Routing basato sul modello: scegli un RayService a cui eseguire il routing in base al corpo della richiesta, ad esempio estraendo il modello richiesto da una richiesta JSON dell'API OpenAI.
- Governance: richiedi chiavi API per utilizzare il tuo servizio o applica la quota per gli utenti utilizzando Apigee per l'autenticazione e la gestione delle API.
- Multiregionale: dividi il traffico tra più cluster GKE con RayServices per ottenere una maggiore disponibilità o capacità con i gateway multicluster.
- Separazione delle responsabilità: utilizza RayService separati, che possono essere amministrati da team separati, seguire implementazioni separate ed essere eseguiti su topologie diverse.
- Sicurezza: utilizza il gateway come terminatore SSL per proteggere il traffico degli utenti su internet. Per ulteriori informazioni, vedi Sicurezza del gateway.
Per configurare il routing, devi eseguire il deployment di un gateway, di un HTTPRoute e di un RayService. Un servizio Kubernetes per ogni cluster Ray di destinazione viene in genere creato da KubeRay. Ray Serve distribuisce il carico delle richieste nel cluster, senza la necessità di creare un InferencePool o un selettore di endpoint.
Routing basato sul modello per Ray Serve su GKE
Il routing basato sul modello è abilitato da un'estensione di routing basata sul corpo. Il routing basato sul corpo consente di indirizzare il traffico a RayService diversi in base al modello indicato nella richiesta dell'utente, il che consente di avere un unico endpoint in grado di erogare molti modelli ospitati in più cluster Ray. I tuoi utenti hanno un accesso semplificato e gli sviluppatori di app hanno il controllo della configurazione di ogni endpoint Ray.
Per configurare il routing basato sul modello, devi eseguire il deployment dei seguenti componenti chiave:
- Un'estensione del router basata sul corpo per estrarre i nomi dei modelli dai payload JSON. Il deployment di questa estensione del router viene eseguito utilizzando Helm.
- Un gateway GKE (bilanciatore del carico delle applicazioni interno regionale di livello 7) per gestire il traffico in entrata.
- Regole HTTPRoute per indirizzare il traffico al servizio Ray corretto utilizzando le intestazioni compilate dall'estensione del router.
- Più cluster Ray Serve per gestire il ciclo di vita e la scalabilità automatica dei modelli in silos.
Prima di iniziare
Prima di iniziare, assicurati di aver eseguito le seguenti operazioni:
- Attiva l'API Google Kubernetes Engine. Attiva l'API Kubernetes Engine
- Se vuoi utilizzare Google Cloud CLI per questa attività,
installala e poi
inizializza
gcloud CLI. Se hai già installato gcloud CLI, scarica l'ultima
versione eseguendo il comando
gcloud components update. Le versioni precedenti di gcloud CLI potrebbero non supportare l'esecuzione dei comandi in questo documento.
- Assicurati di aver installato Helm.
- Crea un account Hugging Face, se non ne hai già uno.
- Assicurati di avere un token Hugging Face.
prepara l'ambiente
Imposta le variabili di ambiente:
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
Sostituisci YOUR_HUGGING_FACE_TOKEN con il token di accesso
Hugging Face.
Prepara l'infrastruttura
In questa sezione, configurerai un cluster GKE abilitato per Ray e Gateway con GPU L4.
Crea un cluster con l'operatore Ray e l'API Gateway abilitati:
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-4Crea un pool di nodi GPU per i workload del modello:
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=4Crea una subnet solo proxy per il bilanciatore del carico delle applicazioni interno regionale, richiesta dal routing basato sul corpo:
gcloud compute networks subnets create bbr-proxy-only-subnet \ --purpose=REGIONAL_MANAGED_PROXY \ --role=ACTIVE \ --region=${REGION} \ --network=default \ --range=192.168.10.0/24Esegui il deployment del segreto di Hugging Face:
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HUGGING_FACE_TOKEN}
Esegui il deployment del router basato sul corpo per il routing basato sul modello
L'estensione del router basato sul corpo intercetta le richieste, analizza il corpo JSON ed estrae il campo del modello in un'intestazione X-Gateway-Model-Name.
Crea un file denominato
helm-values.yamlcon i seguenti contenuti:bbr: plugins: - type: "body-field-to-header" name: "openai-model-extractor" json: field_name: "model" header_name: "X-Gateway-Model-Name"Installa il router basato sul corpo utilizzando 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
Esegui il deployment di RayServices
Per eseguire il deployment dei modelli, devi applicare i manifest RayService. Ogni manifest definisce un cluster Ray che esegue un LLM specifico.
Crea un file denominato
gemma-2b-it.yamlcon i seguenti contenuti: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-l4Crea un file denominato
qwen2.5-3b.yamlcon i seguenti contenuti: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-l4Esegui il deployment dei modelli:
kubectl apply -f gemma-2b-it.yaml kubectl apply -f qwen2.5-3b.yaml
Configura i controlli di integrità
Per assicurarti che il bilanciatore del carico monitori con precisione l'integrità del worker Ray, devi
applicare la risorsa HealthCheckPolicy.
Crea un file denominato
healthcheck-policy.yamlcon i seguenti contenuti: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-svcApplica il criterio di controllo di integrità:
kubectl apply -f healthcheck-policy.yaml
Configura il routing
Per configurare il routing, devi applicare i manifest Gateway e HTTPRoute.
HTTPRoute contiene regole che corrispondono all'intestazione X-Gateway-Model-Name
(compilata dal router basato sul corpo) per instradare il traffico al servizio Ray
appropriato.
Crea un file denominato
gateway.yamlcon i seguenti contenuti: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: 8000Applica il gateway e la route:
kubectl apply -f gateway.yaml
Testa il deployment
Dopo il provisioning del gateway e la preparazione di entrambi i cluster Ray, puoi testare il routing inviando richieste con nomi di modelli diversi nel corpo JSON.
Ottieni l'indirizzo IP del gateway:
kubectl get gateways ray-multi-model-gatewayAvvia una shell in una rete che può raggiungere l'indirizzo del gateway. Puoi utilizzare curl su uno dei pod del cluster Ray:
POD_NAME=$(kubectl get pods -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD_NAME -- bashInvia richieste testando il routing a 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."}] }'Sostituisci
GATEWAY_IP_ADDRESScon l'indirizzo IP del passaggio precedente.L'output è simile al seguente:
{"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 [...]Testa il routing a 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?"}] }'L'output è simile al seguente:
{"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. [...]
Il router basato sul corpo estrae automaticamente il valore del campo model e
garantisce che ogni richiesta raggiunga il servizio di backend corretto configurato nel file
gateway.yaml.
Esegui la pulizia
Elimina il cluster:
gcloud container clusters delete ${CLUSTER}
Passaggi successivi
- Scopri di più sulle ottimizzazioni delle prestazioni per Ray Serve.
- Scopri di più su Gateway su GKE.