Neste documento, explicamos como gerenciar solicitações de inferência em vários clusters do Ray Serve no Google Kubernetes Engine (GKE) configurando a API Kubernetes Gateway e o GKE Inference Gateway. Essa configuração permite centralizar o gerenciamento de tráfego para várias equipes, distribuir cargas de trabalho entre regiões para maior capacidade e implementar o roteamento com reconhecimento de modelo com base no conteúdo do corpo da solicitação.
Benefícios de usar o gateway de inferência do GKE e o Ray Serve
O uso do gateway de inferência do GKE e do Ray Serve oferece os seguintes benefícios:
- Roteamento por caminho: configure cada RayService com um prefixo de caminho e veicule
com um roteamento de gateway para vários RayServices.
- Para mais informações sobre como configurar regras de prefixo de caminho, consulte a documentação da API Gateway.
- Roteamento com reconhecimento de modelo: escolha um RayService para rotear com base no corpo da solicitação, por exemplo, extraindo o modelo solicitado de uma solicitação JSON da API OpenAI.
- Governança: exija chaves de API para usar seu serviço ou aplique cota para usuários usando Apigee para autenticação e gerenciamento de APIs.
- Multirregional: divida o tráfego em vários clusters do GKE com RayServices para alcançar maior disponibilidade ou capacidade com gateways de vários clusters.
- Separação de responsabilidades: use RayServices separados, que podem ser administrados por equipes separadas, seguir lançamentos separados e ser executados em topologias diferentes.
- Segurança: use o Gateway como terminador SSL para ajudar a proteger o tráfego do usuário na Internet. Para mais informações, consulte Segurança do gateway.
Para configurar o roteamento, é preciso implantar um gateway, uma HTTPRoute e um RayService. Normalmente, o KubeRay cria um serviço do Kubernetes para cada cluster de destino do Ray. O Ray Serve distribui a carga de solicitações no cluster, sem a necessidade de criar um InferencePool ou um Endpoint Picker.
Roteamento com reconhecimento de modelo para Ray Serve no GKE
O roteamento com reconhecimento de modelo é ativado por uma extensão de roteamento baseada no corpo. O roteamento baseado em corpo permite direcionar o tráfego para diferentes RayServices com base apenas no modelo nomeado na solicitação do usuário, o que permite ter um único endpoint que pode disponibilizar muitos modelos hospedados em vários clusters do Ray. Seus usuários têm acesso simplificado, e os desenvolvedores de apps controlam a configuração de cada endpoint do Ray.
Para configurar o roteamento com reconhecimento de modelo, implante os seguintes componentes principais:
- Uma extensão de roteador baseada em corpo para extrair nomes de modelos de payloads JSON. Essa extensão de roteador é implantada usando o Helm.
- Um gateway do GKE (balanceador de carga de aplicativo interno regional da camada 7) para processar o tráfego de entrada.
- Regras HTTPRoute para direcionar o tráfego ao serviço do Ray correto usando cabeçalhos preenchidos pela extensão do roteador.
- Vários clusters do Ray Serve para gerenciar o ciclo de vida e o escalonamento automático de modelos isolados.
Antes de começar
Antes de começar, verifique se você realizou as tarefas a seguir:
- Ative a API Google Kubernetes Engine. Ativar a API Google Kubernetes Engine
- Se você quiser usar a Google Cloud CLI para essa tarefa,
instale e, em seguida,
inicialize a
CLI gcloud. Se você instalou a CLI gcloud anteriormente, instale a versão
mais recente executando o comando
gcloud components update. Talvez as versões anteriores da CLI gcloud não sejam compatíveis com a execução dos comandos neste documento.
- Verifique se o Helm está instalado.
- Crie uma conta do Hugging Face caso ainda não tenha uma.
- Verifique se você tem um token do Hugging Face.
Preparar o ambiente
Configure as variáveis de 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
Substitua YOUR_HUGGING_FACE_TOKEN pelo seu token de acesso do Hugging Face.
Preparar sua infraestrutura
Nesta seção, você configura um cluster do GKE com GPUs L4 e Ray e Gateway ativados.
Crie um cluster com o operador Ray e a API Gateway ativados:
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-4Crie um pool de nós de GPU para as cargas de trabalho do 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=4Crie uma sub-rede somente proxy para o balanceador de carga de aplicativo regional interno, que é exigido pelo roteamento baseado em 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/24Implante seu secret do Hugging Face:
kubectl create secret generic hf-secret \ --from-literal=hf_api_token=${HUGGING_FACE_TOKEN}
Implante o roteador baseado no corpo para o roteamento com reconhecimento de modelo
A extensão de roteador baseada em corpo intercepta solicitações, analisa o corpo JSON e
extrai o campo do modelo em um cabeçalho X-Gateway-Model-Name.
Crie um arquivo chamado
helm-values.yamlcom o conteúdo a seguir:bbr: plugins: - type: "body-field-to-header" name: "openai-model-extractor" json: field_name: "model" header_name: "X-Gateway-Model-Name"Instale o roteador com base no corpo usando o 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
Implantar RayServices
Para implantar seus modelos, aplique os manifestos RayService. Cada manifesto
define um cluster do Ray que executa um LLM específico.
Crie um arquivo chamado
gemma-2b-it.yamlcom o conteúdo a seguir: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-l4Crie um arquivo chamado
qwen2.5-3b.yamlcom o conteúdo a seguir: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-l4Implante os modelos:
kubectl apply -f gemma-2b-it.yaml kubectl apply -f qwen2.5-3b.yaml
Configurar verificações de integridade
Para garantir que o balanceador de carga monitore com precisão a integridade do worker do Ray, aplique o recurso HealthCheckPolicy.
Crie um arquivo chamado
healthcheck-policy.yamlcom o conteúdo a seguir: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-svcAplique a política de verificação de integridade:
kubectl apply -f healthcheck-policy.yaml
Configurar o roteamento
Para configurar o roteamento, aplique os manifestos Gateway e HTTPRoute.
O HTTPRoute contém regras que correspondem ao cabeçalho X-Gateway-Model-Name
(preenchido pelo roteador com base no corpo) para encaminhar o tráfego ao serviço
do Ray apropriado.
Crie um arquivo chamado
gateway.yamlcom o conteúdo a seguir: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: 8000Aplique o gateway e a rota:
kubectl apply -f gateway.yaml
Teste a implantação
Depois que o gateway for provisionado e os dois clusters do Ray estiverem prontos, será possível testar o roteamento enviando solicitações com nomes de modelos diferentes no corpo JSON.
Consiga o endereço IP do gateway:
kubectl get gateways ray-multi-model-gatewayInicie um shell em uma rede que possa acessar o endereço do gateway. Você pode usar curl em um dos pods do cluster do Ray:
POD_NAME=$(kubectl get pods -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}') kubectl exec -it $POD_NAME -- bashEnvie solicitações testando o roteamento para 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."}] }'Substitua
GATEWAY_IP_ADDRESSpelo endereço IP da etapa anterior.O resultado será o seguinte:
{"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 [...]Testar o encaminhamento para o 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?"}] }'O resultado será o seguinte:
{"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. [...]
O roteador baseado em corpo extrai automaticamente o valor do campo model e
garante que cada solicitação chegue ao serviço de back-end correto configurado no arquivo
gateway.yaml.
Limpar
Exclua o cluster:
gcloud container clusters delete ${CLUSTER}
A seguir
- Saiba mais sobre as otimizações de desempenho para o Ray Serve.
- Leia mais sobre o gateway no GKE.