En este documento, se explica cómo administrar solicitudes de inferencia en varios clústeres de Ray Serve en Google Kubernetes Engine (GKE) configurando la API de Gateway de Kubernetes y GKE Inference Gateway. Esta configuración te permite centralizar la administración del tráfico para varios equipos, distribuir las cargas de trabajo en diferentes regiones para aumentar la capacidad y, además, implementar el enrutamiento que tiene en cuenta el modelo según el contenido del cuerpo de la solicitud.
Beneficios de usar GKE Inference Gateway y Ray Serve
El uso de GKE Inference Gateway y Ray Serve ofrece los siguientes beneficios:
- Enrutamiento de rutas de acceso: Configura cada RayService con un prefijo de ruta de acceso y, luego,
proporciónalos con una puerta de enlace que enrute a varios servicios de Ray.
- Para obtener más información sobre cómo configurar reglas de prefijo de ruta, consulta la documentación de la API de Gateway.
- Enrutamiento que tiene en cuenta el modelo: Elige un RayService para enrutarlo según el cuerpo de la solicitud, por ejemplo, extrayendo el modelo solicitado de una solicitud JSON de la API de OpenAI.
- Gobernanza: Exige claves de API para usar tu servicio o aplica cuotas para los usuarios con Apigee para la autenticación y la administración de APIs.
- Multirregión: Divide el tráfico entre varios clústeres de GKE con RayServices para lograr una mayor disponibilidad o capacidad con puertas de enlace de varios clústeres.
- Separación de responsabilidades: Usa RayServices separados, que pueden ser administrados por equipos separados, seguir lanzamientos separados y ejecutarse en diferentes topologías.
- Seguridad: Usa Gateway para que actúe como el terminador de SSL y ayude a proteger el tráfico de los usuarios en Internet. Para obtener más información, consulta Seguridad de la puerta de enlace.
Para configurar el enrutamiento, debes implementar una Gateway, una HTTPRoute y un RayService. KubeRay suele crear un servicio de Kubernetes para cada clúster de Ray de destino. Ray Serve distribuye la carga de solicitudes en el clúster, sin necesidad de crear un InferencePool o un selector de extremos.
Enrutamiento compatible con el modelo para Ray Serve en GKE
El enrutamiento que tiene en cuenta el modelo se habilita con una extensión de enrutamiento basada en el cuerpo. El enrutamiento basado en el cuerpo te permite dirigir el tráfico a diferentes RayServices basándote únicamente en el modelo nombrado en la solicitud del usuario, lo que te permite tener un solo endpoint que puede entregar muchos modelos alojados en varios clústeres de Ray. Tus usuarios tienen acceso simplificado, y los desarrolladores de apps tienen control sobre la configuración de cada extremo de Ray.
Para configurar el enrutamiento que tiene en cuenta el modelo, implementa los siguientes componentes clave:
- Es una extensión del router basada en el cuerpo para extraer nombres de modelos de cargas útiles JSON. Esta extensión del router se implementa con Helm.
- Una puerta de enlace de GKE (balanceador de cargas de aplicaciones interno regional de L7) para controlar el tráfico entrante
- Reglas de HTTPRoute para dirigir el tráfico al servicio de Ray correcto con encabezados completados por la extensión del router.
- Varios clústeres de Ray Serve para administrar el ciclo de vida y el ajuste de escala automático de los modelos aislados
Antes de comenzar
Antes de comenzar, asegúrate de haber realizado las siguientes tareas:
- Habilita la API de Google Kubernetes Engine. Habilitar la API de Google Kubernetes Engine
- Si deseas usar Google Cloud CLI para esta tarea,
instala y, luego,
inicializa
gcloud CLI. Si ya instalaste gcloud CLI, ejecuta el comando
gcloud components updatepara obtener la versión más reciente. Es posible que las versiones anteriores de gcloud CLI no admitan la ejecución de los comandos que se indican en este documento.
- Asegúrate de tener Helm instalado.
- Crea una cuenta de Hugging Face, si todavía no la tienes.
- Asegúrate de tener un token de Hugging Face.
Prepara el entorno
Configure las variables de entorno:
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
Reemplaza YOUR_HUGGING_FACE_TOKEN por tu token de acceso de Hugging Face.
Prepara tu infraestructura
En esta sección, configurarás un clúster de GKE habilitado para Ray y Gateway con GPUs L4.
Crea un clúster con el operador de Ray y la API de Gateway habilitados:
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 grupo de nodos de GPU para las cargas de trabajo de tu modelo:
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 subred de solo proxy para el balanceador de cargas de aplicaciones interno regional, que es necesaria para el enrutamiento basado en el cuerpo:
gcloud compute networks subnets create bbr-proxy-only-subnet \ --purpose=REGIONAL_MANAGED_PROXY \ --role=ACTIVE \ --region=${REGION} \ --network=default \ --range=192.168.10.0/24Implementa tu Secret de Hugging Face:
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HUGGING_FACE_TOKEN}
Implementa el router basado en el cuerpo para el enrutamiento que tiene en cuenta el modelo
La extensión del router basada en el cuerpo intercepta las solicitudes, analiza el cuerpo JSON y extrae el campo del modelo en un encabezado X-Gateway-Model-Name.
Crea un archivo llamado
helm-values.yamlcon el siguiente contenido:bbr: plugins: - type: "body-field-to-header" name: "openai-model-extractor" json: field_name: "model" header_name: "X-Gateway-Model-Name"Instala el router basado en el cuerpo con 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
Implementa RayServices
Para implementar tus modelos, debes aplicar los manifiestos de RayService. Cada manifiesto define un clúster de Ray que ejecuta un LLM específico.
Crea un archivo llamado
gemma-2b-it.yamlcon el siguiente contenido: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 archivo llamado
qwen2.5-3b.yamlcon el siguiente contenido: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-l4Implementa los modelos:
kubectl apply -f gemma-2b-it.yaml kubectl apply -f qwen2.5-3b.yaml
Configura las verificaciones de estado
Para garantizar que el balanceador de cargas supervise con precisión el estado de los trabajadores de Ray, debes aplicar el recurso HealthCheckPolicy.
Crea un archivo llamado
healthcheck-policy.yamlcon el siguiente contenido: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-svcAplica la política de verificación de estado:
kubectl apply -f healthcheck-policy.yaml
Configura el enrutamiento
Para configurar el enrutamiento, debes aplicar los manifiestos Gateway y HTTPRoute.
El HTTPRoute contiene reglas que coinciden con el encabezado X-Gateway-Model-Name (completado por el router basado en el cuerpo) para enrutar el tráfico al servicio de Ray adecuado.
Crea un archivo llamado
gateway.yamlcon el siguiente contenido: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: 8000Aplica la puerta de enlace y la ruta:
kubectl apply -f gateway.yaml
Prueba la implementación
Después de que se aprovisione la puerta de enlace y ambos clústeres de Ray estén listos, puedes probar el enrutamiento enviando solicitudes con diferentes nombres de modelos en el cuerpo JSON.
Obtén la dirección IP de la puerta de enlace:
kubectl get gateways ray-multi-model-gatewayInicia una shell en una red que pueda acceder a la dirección de la puerta de enlace. Puedes usar curl en uno de los Pods del clúster de Ray:
POD_NAME=$(kubectl get pods -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD_NAME -- bashEnvía solicitudes probando el enrutamiento 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."}] }'Reemplaza
GATEWAY_IP_ADDRESSpor la dirección IP del paso anterior.El resultado es similar a lo siguiente:
{"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 [...]Prueba el enrutamiento 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?"}] }'El resultado es similar a lo siguiente:
{"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. [...]
El router basado en el cuerpo extrae automáticamente el valor del campo model y garantiza que cada solicitud llegue al servicio de backend correcto configurado en la configuración del servicio gateway.yaml.
Realiza una limpieza
Borra el clúster:
gcloud container clusters delete ${CLUSTER}
¿Qué sigue?
- Obtén más información sobre las optimizaciones de rendimiento para Ray Serve.
- Obtén más información sobre la puerta de enlace en GKE.