Ce document explique comment gérer les requêtes d'inférence sur plusieurs clusters Ray Serve sur Google Kubernetes Engine (GKE) en configurant l'API Kubernetes Gateway et GKE Inference Gateway. Cette configuration vous permet de centraliser la gestion du trafic pour plusieurs équipes, de répartir les charges de travail entre les régions pour augmenter la capacité et d'implémenter un routage basé sur le modèle en fonction du contenu du corps de la requête.
Avantages de l'utilisation de GKE Inference Gateway et de Ray Serve
L'utilisation de GKE Inference Gateway et de Ray Serve offre les avantages suivants :
- Routage basé sur le chemin d'accès : configurez chaque RayService avec un préfixe de chemin d'accès, puis mettez-
les en service avec une passerelle qui achemine le trafic vers plusieurs services Ray.
- Pour en savoir plus sur la configuration des règles de préfixe de chemin d'accès, consultez la documentation de l'API Gateway.
- Routage basé sur le modèle : choisissez un RayService vers lequel effectuer le routage en fonction du corps de la requête, par exemple en extrayant le modèle demandé à partir d'une requête JSON de l'API OpenAI.
- Gouvernance : exigez des clés API pour utiliser votre service ou appliquez un quota aux utilisateurs en utilisant Apigee pour l'authentification et la gestion des API.
- Multirégion : répartissez le trafic sur plusieurs clusters GKE avec des RayServices pour obtenir une disponibilité ou une capacité plus élevées avec des passerelles multiclusters.
- Séparation des préoccupations : utilisez des RayServices distincts, qui peuvent être administrés par des équipes distinctes, suivre des déploiements distincts et s'exécuter sur différentes topologies.
- Sécurité : utilisez Gateway comme terminateur SSL pour sécuriser le trafic utilisateur sur Internet. Pour en savoir plus, consultez la section Sécurité des passerelles.
Pour configurer le routage, vous devez déployer une passerelle, une route HTTP et un RayService. Un service Kubernetes pour chaque cluster Ray cible est généralement créé par KubeRay. Ray Serve répartit la charge des requêtes dans le cluster, sans qu'il soit nécessaire de créer un InferencePool ou un sélecteur de point de terminaison.
Routage basé sur le modèle pour Ray Serve sur GKE
Le routage basé sur le modèle est activé par une extension de routage basée sur le corps. Le routage basé sur le corps vous permet de diriger le trafic vers différents RayServices en fonction uniquement du modèle nommé dans la requête de l'utilisateur. Vous pouvez ainsi disposer d'un point de terminaison unique capable de mettre en service de nombreux modèles hébergés dans plusieurs clusters Ray. Vos utilisateurs bénéficient d'un accès simplifié, et les développeurs de votre application peuvent contrôler la configuration de chaque point de terminaison Ray.
Pour configurer le routage basé sur le modèle, vous devez déployer les composants clés suivants :
- Une extension de routeur basée sur le corps pour extraire les noms de modèles des charges utiles JSON. Cette extension de routeur est déployée à l'aide de Helm.
- Une passerelle GKE (équilibreur de charge d'application interne régional de couche 7) pour gérer le trafic entrant.
- Des règles HTTPRoute pour diriger le trafic vers le service Ray approprié à l'aide d'en-têtes renseignés par l'extension de routeur.
- Plusieurs clusters Ray Serve pour gérer le cycle de vie et l'autoscaling des modèles isolés.
Avant de commencer
Avant de commencer, effectuez les tâches suivantes :
- Activez l'API Google Kubernetes Engine. Activer l'API Google Kubernetes Engine
- Si vous souhaitez utiliser la Google Cloud CLI pour cette tâche, installez et initialisez la gcloud CLI. Si vous avez déjà installé la gcloud CLI, obtenez la dernière version en exécutant la commande
gcloud components update. Il est possible que les versions antérieures de la gcloud CLI ne permettent pas d'exécuter les commandes de ce document.
- Assurez-vous que Helm est installé.
- Créez un compte Hugging Face si vous n'en avez pas déjà un.
- Assurez-vous de disposer d'un jeton Hugging Face.
Préparer votre environnement
Configurez des variables d'environnement :
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
Remplacez YOUR_HUGGING_FACE_TOKEN par votre jeton d'accès Hugging Face.
Préparer votre infrastructure
Dans cette section, vous allez configurer un cluster GKE compatible avec Ray et Gateway avec des GPU L4.
Créez un cluster avec l'opérateur Ray et l'API Gateway activés :
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-4Créez un pool de nœuds GPU pour vos charges de travail de modèle :
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=4Créez un sous-réseau proxy réservé pour l'équilibreur de charge d'application interne régional, qui est requis par le routage basé sur le corps :
gcloud compute networks subnets create bbr-proxy-only-subnet \ --purpose=REGIONAL_MANAGED_PROXY \ --role=ACTIVE \ --region=${REGION} \ --network=default \ --range=192.168.10.0/24Déployez votre secret Hugging Face :
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HUGGING_FACE_TOKEN}
Déployer le routeur basé sur le corps pour le routage basé sur le modèle
L'extension de routeur basée sur le corps intercepte les requêtes, analyse le corps JSON et extrait le champ du modèle dans un en-tête X-Gateway-Model-Name.
Créez un fichier nommé
helm-values.yamlavec le contenu suivant :bbr: plugins: - type: "body-field-to-header" name: "openai-model-extractor" json: field_name: "model" header_name: "X-Gateway-Model-Name"Installez le routeur basé sur le corps à l'aide de 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
Déployer des RayServices
Pour déployer vos modèles, vous devez appliquer les fichiers manifestes RayService. Chaque fichier manifeste définit un cluster Ray qui exécute un LLM spécifique.
Créez un fichier nommé
gemma-2b-it.yamlavec le contenu suivant :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-l4Créez un fichier nommé
qwen2.5-3b.yamlavec le contenu suivant :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-l4Déployez les modèles :
kubectl apply -f gemma-2b-it.yaml kubectl apply -f qwen2.5-3b.yaml
Configurer les vérifications d'état
Pour vous assurer que l'équilibreur de charge surveille avec précision l'état des workers Ray, vous devez appliquer la ressource HealthCheckPolicy.
Créez un fichier nommé
healthcheck-policy.yamlavec le contenu suivant :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-svcAppliquez la règle de vérification de l'état :
kubectl apply -f healthcheck-policy.yaml
Configurer le routage
Pour configurer le routage, vous devez appliquer les fichiers manifestes Gateway et HTTPRoute.
HTTPRoute contient des règles qui correspondent à l'en-tête X-Gateway-Model-Name (renseigné par le routeur basé sur le corps) pour acheminer le trafic vers le service Ray approprié.
Créez un fichier nommé
gateway.yamlavec le contenu suivant :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: 8000Appliquez la passerelle et la route :
kubectl apply -f gateway.yaml
Tester le déploiement
Une fois la passerelle provisionnée et les deux clusters Ray prêts, vous pouvez tester le routage en envoyant des requêtes avec différents noms de modèles dans le corps JSON.
Obtenez l'adresse IP de la passerelle :
kubectl get gateways ray-multi-model-gatewayDémarrez un shell dans un réseau pouvant atteindre l'adresse de la passerelle. Vous pouvez utiliser curl sur l'un des pods du cluster Ray :
POD_NAME=$(kubectl get pods -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD_NAME -- bashEnvoyez des requêtes en testant le routage vers 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."}] }'Remplacez
GATEWAY_IP_ADDRESSpar l'adresse IP de l'étape précédente.Le résultat ressemble à ce qui suit :
{"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 [...]Testez le routage vers 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?"}] }'Le résultat ressemble à ce qui suit :
{"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. [...]
Le routeur basé sur le corps extrait automatiquement la valeur du champ model et s'assure que chaque requête atteint le service de backend approprié configuré dans le fichier gateway.yaml.
Libérer de l'espace
Supprimez le cluster à l'aide de la commande suivante :
gcloud container clusters delete ${CLUSTER}
Étape suivante
- Découvrez les optimisations des performances pour Ray Serve.
- En savoir plus sur Gateway sur GKE.