Questo documento spiega come gestire le richieste di inferenza su più cluster Ray Serve in Google Kubernetes Engine (GKE) configurando l'API Kubernetes Gateway e GKE Inference Gateway. Questa configurazione consente di centralizzare la gestione del traffico per più team, distribuire i carichi di lavoro tra le regioni per una maggiore capacità e implementare il routing basato sul 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 del percorso: configura ogni RayService con un prefisso del percorso, quindi gestiscili
con un routing del gateway a più servizi Ray.
- 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 le chiavi API per utilizzare il servizio o applica la quota per gli utenti utilizzando Apigee per l'autenticazione e la gestione delle API.
- Multi-regione: dividi il traffico su più cluster GKE con RayServices per ottenere una maggiore disponibilità o capacità con gateway multi-cluster.
- Separazione delle responsabilità: utilizza RayServices 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 utente su internet. Per saperne di più, consulta Sicurezza del gateway.
Per configurare il routing, devi eseguire il deployment di un gateway, di un HTTPRoute e di un RayService. In genere, KubeRay crea un servizio Kubernetes per ogni cluster Ray di destinazione. Ray Serve distribuisce il carico delle richieste nel cluster, senza la necessità di creare un InferencePool o un Endpoint Picker.
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 ti consente di indirizzare il traffico a diversi RayServices in base al modello denominato nella richiesta dell'utente, il che ti consente di avere un singolo endpoint in grado di gestire molti modelli ospitati in più cluster Ray. Gli utenti hanno un accesso semplificato e gli sviluppatori di app hanno il controllo sulla configurazione di ogni endpoint Ray.
Per configurare il routing basato sul modello, esegui il deployment dei seguenti componenti chiave:
- Un'estensione del router basata sul corpo per estrarre i nomi dei modelli dai payload JSON. Questa estensione del router viene eseguita 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 isolati.
Prima di iniziare
Prima di iniziare, assicurati di aver eseguito le seguenti attività:
- Abilita l'API Google Kubernetes Engine. Abilita l'API Google 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
gcloud components updatecomando. 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 tuo token di accesso Hugging Face.
Prepara l'infrastruttura
In questa sezione configurerai un cluster GKE abilitato per Ray e per il 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 carichi di lavoro 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 secret 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 basata 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à dei 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 indirizzare 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 che il gateway è stato sottoposto a provisioning ed entrambi i cluster Ray sono pronti, 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.