Menyajikan LLM dengan Ray Serve multi-cluster dan GKE Inference Gateway

Dokumen ini menjelaskan cara mengelola permintaan inferensi di beberapa cluster Ray Serve di Google Kubernetes Engine (GKE) dengan mengonfigurasi Kubernetes Gateway API dan GKE Inference Gateway. Konfigurasi ini memungkinkan Anda memusatkan pengelolaan traffic untuk beberapa tim, mendistribusikan workload di seluruh region untuk kapasitas yang lebih tinggi, dan menerapkan perutean yang mendukung model berdasarkan konten isi permintaan.

Manfaat menggunakan GKE Inference Gateway dan Ray Serve

Penggunaan GKE Inference Gateway dan Ray Serve menawarkan manfaat berikut:

  • Perutean jalur: mengonfigurasi setiap RayService dengan awalan jalur, lalu menayangkan nya dengan satu Gateway yang merutekan ke beberapa Layanan Ray.
    • Untuk mengetahui informasi selengkapnya tentang cara menyiapkan aturan awalan jalur, lihat dokumentasi Gateway API.
  • Perutean yang mendukung model: memilih RayService yang akan dirutekan berdasarkan isi permintaan—misalnya, dengan mengekstrak model yang diminta dari permintaan JSON OpenAI-API.
  • Tata kelola: mewajibkan kunci API untuk menggunakan layanan Anda, atau menerapkan kuota untuk pengguna dengan menggunakan Apigee untuk autentikasi dan pengelolaan API.
  • Multi-region: membagi traffic di beberapa cluster GKE dengan RayServices untuk mencapai ketersediaan atau kapasitas yang lebih tinggi dengan Gateway multi-cluster.
  • Pemisahan tanggung jawab: menggunakan RayServices terpisah, yang dapat dikelola oleh tim terpisah, mengikuti peluncuran terpisah, dan berjalan pada topologi yang berbeda.
  • Keamanan: menggunakan Gateway untuk bertindak sebagai terminator SSL guna membantu mengamankan traffic pengguna Anda melalui internet. Untuk mengetahui informasi selengkapnya, lihat Keamanan gateway.

Untuk mengonfigurasi perutean, Anda harus men-deploy Gateway, HTTPRoute, dan RayService. Layanan Kubernetes untuk setiap cluster Ray target biasanya dibuat oleh KubeRay. Ray Serve menyebarkan beban permintaan dalam cluster, tanpa perlu membuat InferencePool atau Pemilih Endpoint.

Perutean yang mendukung model untuk Ray Serve di GKE

Perutean yang mendukung model diaktifkan oleh ekstensi perutean berbasis isi. Perutean berbasis isi memungkinkan Anda mengarahkan traffic ke RayServices yang berbeda hanya berdasarkan model yang diberi nama dalam permintaan pengguna, yang memungkinkan Anda memiliki satu endpoint yang dapat menayangkan banyak model yang dihosting di beberapa cluster Ray. Pengguna Anda memiliki akses yang disederhanakan, dan developer aplikasi Anda memiliki kontrol atas konfigurasi setiap endpoint Ray.

Untuk mengonfigurasi perutean yang mendukung model, Anda men-deploy komponen utama berikut:

  • Ekstensi router berbasis isi untuk mengekstrak nama model dari payload JSON. Ekstensi router ini di-deploy menggunakan Helm.
  • GKE Gateway (Load Balancer Aplikasi internal regional L7) untuk menangani traffic masuk.
  • Aturan HTTPRoute untuk mengarahkan traffic ke Layanan Ray yang benar menggunakan header yang diisi oleh ekstensi router.
  • Beberapa cluster Ray Serve untuk mengelola siklus proses dan penskalaan otomatis model yang terisolasi.

Sebelum memulai

Sebelum memulai, pastikan Anda telah melakukan tugas berikut:

  • Aktifkan Google Kubernetes Engine API.
  • Aktifkan Google Kubernetes Engine API
  • Jika ingin menggunakan Google Cloud CLI untuk tugas ini, instal lalu lakukan inisialisasi gcloud CLI. Jika sebelumnya Anda telah menginstal gcloud CLI, dapatkan versi terbaru dengan menjalankan perintah gcloud components update. Versi gcloud CLI yang lebih lama mungkin tidak mendukung menjalankan perintah dalam dokumen ini.

Menyiapkan lingkungan Anda

Siapkan variabel lingkungan:

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

Ganti YOUR_HUGGING_FACE_TOKEN dengan token akses Hugging Face Anda.

Menyiapkan infrastruktur Anda

Di bagian ini, Anda akan menyiapkan cluster GKE yang mendukung Ray dan Gateway dengan GPU L4.

  1. Buat cluster dengan Ray Operator dan Gateway API diaktifkan:

    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-4
    
  2. Buat node pool GPU untuk workload model Anda:

    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=4
    
  3. Buat subnet khusus proxy untuk Load Balancer Aplikasi internal regional, yang diperlukan oleh perutean berbasis isi:

    gcloud compute networks subnets create bbr-proxy-only-subnet \
        --purpose=REGIONAL_MANAGED_PROXY \
        --role=ACTIVE \
        --region=${REGION} \
        --network=default \
        --range=192.168.10.0/24
    
  4. Deploy secret Hugging Face Anda:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HUGGING_FACE_TOKEN}
    

Men-deploy router berbasis isi untuk perutean yang mendukung model

Ekstensi router berbasis isi mencegat permintaan, mengurai isi JSON, dan mengekstrak kolom model ke dalam header X-Gateway-Model-Name.

  1. Buat file bernama helm-values.yaml dengan konten berikut:

    bbr:
      plugins:
        - type: "body-field-to-header"
          name: "openai-model-extractor"
          json:
            field_name: "model"
            header_name: "X-Gateway-Model-Name"
    
  2. Instal router berbasis isi menggunakan 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
    

Men-deploy RayServices

Untuk men-deploy model, Anda harus menerapkan manifes RayService. Setiap manifes menentukan cluster Ray yang menjalankan LLM tertentu.

  1. Buat file bernama gemma-2b-it.yaml dengan konten berikut:

    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-l4
    
  2. Buat file bernama qwen2.5-3b.yaml dengan konten berikut:

    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-l4
    
  3. Deploy model:

    kubectl apply -f gemma-2b-it.yaml
    kubectl apply -f qwen2.5-3b.yaml
    

Mengonfigurasi health check

Untuk membantu memastikan load balancer memantau kesehatan pekerja Ray secara akurat, Anda harus menerapkan resource HealthCheckPolicy.

  1. Buat file bernama healthcheck-policy.yaml dengan konten berikut:

    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-svc
    
  2. Terapkan kebijakan health check:

    kubectl apply -f healthcheck-policy.yaml
    

Mengonfigurasi perutean

Untuk mengonfigurasi perutean, Anda harus menerapkan manifes Gateway dan HTTPRoute. HTTPRoute berisi aturan yang cocok dengan header X-Gateway-Model-Name (diisi oleh router berbasis isi) untuk merutekan traffic ke layanan Ray yang sesuai.

  1. Buat file bernama gateway.yaml dengan konten berikut:

    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: 8000
    
  2. Terapkan gateway dan rute:

    kubectl apply -f gateway.yaml
    

Melakukan pengujian deployment

Setelah Gateway disediakan dan kedua cluster Ray siap, Anda dapat menguji perutean dengan mengirim permintaan dengan nama model yang berbeda dalam isi JSON.

  1. Dapatkan alamat IP Gateway:

    kubectl get gateways ray-multi-model-gateway
    
  2. Mulai shell di jaringan yang dapat menjangkau alamat Gateway. Anda dapat menggunakan curl di salah satu Pod cluster Ray:

    POD_NAME=$(kubectl get pods -l ray.io/node-type=head -o jsonpath='{.items[0].metadata.name}')
    kubectl exec -it $POD_NAME -- bash
    
  3. Kirim permintaan dengan menguji perutean ke 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."}]
        }'
    

    Ganti GATEWAY_IP_ADDRESS dengan alamat IP dari langkah sebelumnya.

    Outputnya mirip dengan hal berikut ini:

    {"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 [...]
    
  4. Uji perutean ke 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?"}]
        }'
    

    Outputnya mirip dengan hal berikut ini:

    {"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. [...]
    

Router berbasis isi secara otomatis mengekstrak nilai kolom model dan memastikan setiap permintaan mencapai layanan backend yang benar yang dikonfigurasi dalam file gateway.yaml.

Pembersihan

Hapus cluster:

gcloud container clusters delete ${CLUSTER}

Langkah berikutnya