GKE'de Ray ile çok ana makine TPU'lu vLLM çıkarımı dağıtma

1. Giriş

Bu codelab'de, Google Cloud TPU'ları kullanarak Google Kubernetes Engine'de (GKE) yüksek performanslı, çok ana bilgisayarlı vLLM (Sanal Büyük Dil Modeli) çıkarım hizmetlerini nasıl dağıtacağınızı öğreneceksiniz. Ray'i kullanarak dağıtılmış çıkarımı yapılandıracak ve LeaderWorkerSets'i kullanarak iş yükünü GKE'de yerel olarak yöneteceksiniz.

Bu adım adım açıklama, Qwen 30B gibi büyük modellerin sunulması için üretim kurulumunu simüle eder.

Yapacaklarınız

  • Hızlandırıcı trafiği için özel bir VPC ağı oluşturun.
  • Ray Operator ve GCS Fuse CSI sürücüsü ile bir GKE kümesi sağlayın.
  • Hızlandırılmış model yükleme için GCS Rapid Cache'i başlatın.
  • Ayrılmış kapasiteye sahip çok ana bilgisayarlı bir TPU v6e düğüm havuzu sağlayın.
  • Model ağırlıklarına güvenli erişim için Workload Identity'yi yapılandırın.
  • 30 milyar parametreli bir modele hizmet veren vLLM motorunu dağıtın ve test edin.

İhtiyacınız olanlar

  • Faturalandırmanın etkin olduğu bir Google Cloud projesi.
  • TPU v6e kaynakları (32 çip, ct6e-standard-4t) için Google Cloud Rezervasyonu.
  • Kaynak paketten model ağırlıklarını kopyalama erişimi.
  • Cloud Shell veya gcloud, kubectl ve helm'nin yüklü olduğu yerel bir terminal.
  • Tahmini Süre: 60 dakika
  • Tahmini Maliyet: 60 ABD dolarının altında (sökme işleminin hemen yapıldığı varsayılır).

2. Başlamadan önce

Google Cloud projesi oluşturma veya seçme

  1. Google Cloud Console'da bir Google Cloud projesi seçin veya oluşturun.
  2. Cloud projeniz için faturalandırmanın etkinleştirildiğinden emin olun.

Cloud Shell'i Başlatma

  1. Google Cloud Console'un üst kısmından Cloud Shell'i etkinleştir'i tıklayın.
  2. Kimlik doğrulamayı doğrulayın:
gcloud auth list
  1. Projenizi onaylayın:
gcloud config get project
  1. Gerekirse ayarlayın:
export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID

Ortam değişkenlerini ayarlama

Komut yürütmeyi kolaylaştırmak için kabuğunuzda aşağıdaki değişkenleri tanımlayın. <YOUR_ZONE> kısmını ayrılan TPU bölgenizle, <YOUR_RESERVATION_NAME> kısmını ise rezervasyon kimliğinizle değiştirin. Kısıtlanmış model ağırlıklarını indirmek için Hugging Face kullanıcı erişim jetonu oluşturmanız gerekir. Oluşturduktan sonra <YOUR_HUGGING_FACE_TOKEN> ifadesini yeni oluşturduğunuz jetonla değiştirin.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format="value(projectNumber)")
export ZONE="<YOUR_ZONE>" # e.g., us-east5-a
export REGION=${ZONE%-*}
export CLUSTER_NAME="qwen-serving-cluster"
export GVNIC_NETWORK_PREFIX="qwen-serving"
export BUCKET_NAME="inf-demo-model-storage-${PROJECT_NUMBER}"
export RESERVATION_NAME="<YOUR_RESERVATION_NAME>"
export NODE_POOL_NAME="tpu-v6e-32-resvd-pool"
export MULTIHOST_COLLECTION_NAME="tpu-6-collection"
export HF_TOKEN="<YOUR_HUGGING_FACE_TOKEN>" # Token with access to Qwen model if restricted

API'leri etkinleştir

Gerekli Google Cloud hizmetlerini etkinleştirin:

gcloud services enable \
    container.googleapis.com \
    compute.googleapis.com \
    iam.googleapis.com \
    cloudresourcemanager.googleapis.com

3. Özel ağ oluşturma

Çok ana bilgisayarlı TPU iş yükleri, verimli hızlandırıcı iletişimi için daha yüksek MTU boyutları da dahil olmak üzere belirli ağ yapılandırmaları gerektirir. Kümeniz için özel bir VPC ağı oluşturun.

  1. Büyük bir MTU (8896) ile VPC ağı oluşturun:
    gcloud compute --project=${PROJECT_ID} \
        networks create ${GVNIC_NETWORK_PREFIX}-main \
        --subnet-mode=custom \
        --mtu=8896
    
  2. Küme için alt ağ oluşturun:
    gcloud compute --project=${PROJECT_ID} \
        networks subnets create ${GVNIC_NETWORK_PREFIX}-tpu \
        --network=${GVNIC_NETWORK_PREFIX}-main \
        --region=${REGION} \
        --range=192.168.100.0/24
    
  3. Çalışanların iletişim kurmasını sağlamak için dahili trafiğe izin veren güvenlik duvarı kuralları oluşturun:
    gcloud compute --project=${PROJECT_ID} firewall-rules create ${GVNIC_NETWORK_PREFIX}-allow-internal \
        --network=${GVNIC_NETWORK_PREFIX}-main \
        --allow=all \
        --source-ranges=172.16.0.0/12,192.168.0.0/16,10.0.0.0/8 \
        --description="Allow all internal traffic within the network."
    

4. GKE kümesi sağlama

GCS Fuse bağlamalarını ve Ray Operator iş yüklerini destekleyecek şekilde yapılandırılmış bir Standart GKE kümesi kurulumu oluşturun.

  1. Kümeyi oluşturun:
    gcloud container clusters create ${CLUSTER_NAME} \
        --project=${PROJECT_ID} \
        --location=${REGION} \
        --release-channel=rapid \
        --machine-type=e2-standard-4 \
        --network=${GVNIC_NETWORK_PREFIX}-main \
        --subnetwork=${GVNIC_NETWORK_PREFIX}-tpu \
        --num-nodes=1 \
        --gateway-api=standard \
        --enable-managed-prometheus \
        --enable-dataplane-v2 \
        --enable-dataplane-v2-metrics \
        --workload-pool=${PROJECT_ID}.svc.id.goog \
        --addons=GcsFuseCsiDriver,RayOperator \
        --enable-ip-alias
    
  2. Küme Kimlik Bilgilerini Alma:
    gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${REGION}
    
  3. Hugging Face gizli anahtarı oluşturma: Kapsayıcı erişimi indirmeleri için jetonunuzu güvenli bir şekilde kaydedin:
    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN} \
        --dry-run=client -o yaml | kubectl apply -f -
    
  4. Helm aracılığıyla LeaderWorkerSet'i (LWS) yükleyin. LWS, birlikte planlanması gereken pod gruplarını yönetir:
    helm install lws oci://registry.k8s.io/lws/charts/lws \
        --version=0.7.0 \
        --namespace lws-system \
        --create-namespace \
        --wait
    

5. GCS Rapid Cache'i etkinleştirme

Sunum sırasında Cloud Storage'dan onlarca GB ağırlık okumayı hızlandırmak için bir GCS paketi oluşturun ve bölgenizde GCS Rapid Cache'i etkinleştirin.

  1. Paketi oluşturun:
    gcloud storage buckets create gs://$BUCKET_NAME \
        --location=$REGION \
        --uniform-bucket-level-access
    
  2. TPU bölgenizde Rapid Cache'i başlatın:
    gcloud storage buckets anywhere-caches create gs://$BUCKET_NAME $ZONE \
        --ttl=1d \
        --admission-policy=ADMIT_ON_FIRST_MISS
    

6. Workload Identity ve depolama alanı izinlerini ayarlama

Uzun süreli anahtarlar yerleştirmeden ağırlık grubunu GKE pod'larınıza güvenli bir şekilde monte etmek için kimlik bağlantılarını yapılandırın.

  1. Özel bir IAM hizmet hesabı oluşturun:
    gcloud iam service-accounts create tpu-reader-sa
    
  2. Paket okuma izinleri verme:
    gcloud storage buckets add-iam-policy-binding gs://${BUCKET_NAME} \
        --member="serviceAccount:tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com" \
        --role="roles/storage.objectAdmin"
    
  3. default ad alanı Kubernetes hizmet hesabı için Workload Identity bağlaması oluşturun:
    gcloud iam service-accounts add-iam-policy-binding tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com \
        --role="roles/iam.workloadIdentityUser" \
        --member="serviceAccount:${PROJECT_ID}.svc.id.goog[default/default]"
    
  4. Kubernetes SA'yı açıklama ekleyin:
    kubectl annotate serviceaccount default iam.gke.io/gcp-service-account=tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com
    

7. Model Ağırlıkları Ayarları

30B parametreli bir model sunmak için ağırlıkları Hugging Face'ten GCS paketinize indirmeniz gerekir. Cloud Shell disk kotası sınırını (5 GB) aşmak için doğrudan kümenin içine indirmek ve bağlı GCS Fuse birimine güvenli bir şekilde yazmak üzere Standart Kubernetes işi kullanın.

  1. Model İndirici İşini Dağıtma: İndirme işlemini başlatmak için aşağıdaki manifest'i oluşturup uygulayın:
    cat <<EOF | kubectl apply -f -
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: model-downloader
    spec:
      ttlSecondsAfterFinished: 60
      template:
        metadata:
          annotations:
            gke-gcsfuse/volumes: "true"
            gke-gcsfuse/memory-limit: "0"
        spec:
          serviceAccountName: default
          restartPolicy: OnFailure
          containers:
          - name: downloader
            image: python:3.10-slim
            command: ["/bin/sh", "-c"]
            args:
            - |
              pip install -U "huggingface_hub[hf_transfer]" filelock
              export HF_HUB_ENABLE_HF_TRANSFER=1
    
              python -c '
              import filelock
    
              class DummyLock:
                  def __init__(self, *args, **kwargs): pass
                  def __enter__(self): return self
                  def __exit__(self, *args): pass
                  def acquire(self, *args, **kwargs): pass
                  def release(self, *args, **kwargs): pass
    
              filelock.FileLock = DummyLock
    
              from huggingface_hub import snapshot_download
              snapshot_download(
                  repo_id="Qwen/Qwen3-30B-A3B", 
                  local_dir="/models/qwen3-30b-weights",
                  local_dir_use_symlinks=False
              )
              '
            env:
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            volumeMounts:
            - name: model-weights
              mountPath: /models
          volumes:
          - name: model-weights
            csi:
              driver: gcsfuse.csi.storage.gke.io
              volumeAttributes:
                bucketName: ${BUCKET_NAME}
                mountOptions: "implicit-dirs"
    EOF
    
  2. İndirme İşlemini İzleme: İlerleme durumunu takip etmek için indirici pod'un günlüklerini kontrol edin:
    kubectl logs -f job/model-downloader
    
    İşin başarı durumuyla tamamlanmasını bekleyin.

8. Ayrılmış TPU düğüm havuzu oluşturma

Mevcut kapasite rezervasyonunuzu kullanarak gerçek çok ana bilgisayarlı TPU dilimini sağlayın.

  1. Oluşturma komutunu çalıştırın:
    gcloud beta container node-pools create ${NODE_POOL_NAME} \
        --project=${PROJECT_ID} \
        --cluster=${CLUSTER_NAME} \
        --region=${REGION} \
        --node-locations=${ZONE} \
        --machine-type=ct6e-standard-4t \
        --tpu-topology=4x8 \
        --num-nodes=8 \
        --scopes=https://www.googleapis.com/auth/cloud-platform \
        --reservation-affinity=specific \
        --reservation=${RESERVATION_NAME} \
        --accelerator-network-profile=auto \
        --node-labels=cloud.google.com/gke-nodepool-group-name=${MULTIHOST_COLLECTION_NAME} \
        --node-labels=cloud.google.com/gke-workload-type=HIGH_AVAILABILITY \
        --node-labels=cloud.google.com/gke-networking-dra-driver=true
    
  2. Düğümlerin katılması bekleniyor: Düğüm toplama ölçeklendirmesini doğrudan gözlemleyebilirsiniz. ct6e içeren 8 düğüm kubectl get nodes'ye katılana kadar bekleyin.

9. vLLM hizmetini dağıtma

  1. Ağ talepleri oluşturma: Ağ ortamını talep etmeniz gerekir:
    cat <<EOF | kubectl apply -f -
    apiVersion: resource.k8s.io/v1
    kind: ResourceClaimTemplate
    metadata:
      name: all-netdev
    spec:
      spec:
        devices:
          requests:
          - name: req-netdev
            exactly:
              deviceClassName: netdev.google.com
              allocationMode: All
    EOF
    
  2. Yük Dengeleyici API Uç Noktasını Dağıtma:
    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: vllm-tpu-service
    spec:
      type: LoadBalancer
      selector:
        leaderworkerset.sigs.k8s.io/name: vllm-tpu-qwen
        leaderworkerset.sigs.k8s.io/worker-index: "0"
      ports:
      - protocol: TCP
        port: 8000
        targetPort: 8000
    EOF
    
  3. Deploy LeaderWorkerSet workload: Bu bildirim, 8 dilim ana makinesinde Ray başlığı/çalışanı toplama işlemini dinamik olarak başlatır.
    cat <<EOF | kubectl apply -f -
    apiVersion: leaderworkerset.x-k8s.io/v1
    kind: LeaderWorkerSet
    metadata:
      name: vllm-tpu-qwen
    spec:
      replicas: 1
      leaderWorkerTemplate:
        size: 8
        restartPolicy: RecreateGroupOnPodRestart
        workerTemplate:
          metadata:
            annotations:
              gke-gcsfuse/volumes: "true"
              gke-gcsfuse/memory-limit: "0"
            labels:
              leaderworkerset.sigs.k8s.io/name: vllm-tpu-qwen
              gke-gcsfuse/volumes: "true"
          spec:
            hostname: vllm-tpu-qwen
            serviceAccountName: default
            containers:
            - name: vllm-tpu
              image: vllm/vllm-tpu:nightly
              command: ["sh", "-c"]
              args:
              - |
                MY_TPU_IP=\$(hostname -I | awk '{print \$1}')
                echo "My TPU Network IP is: \$MY_TPU_IP"
    
                LEADER_DNS="vllm-tpu-qwen-0.vllm-tpu-qwen"
                until getent hosts \$LEADER_DNS; do
                  echo "DNS not ready. Sleeping 5s..."
                  sleep 5
                  done
                LEADER_IP=\$(getent hosts \$LEADER_DNS | awk '{print \$1}')
    
                export JAX_PLATFORMS=''
                export SCAN_TPU_CHIPS=True
                export TPU_MULTIHOST_BACKEND=ray
                export JAX_DISTRIBUTED_INITIALIZATION_TIMEOUT=300
                export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/local/lib
                export VLLM_HOST_IP=\$MY_TPU_IP
    
                if [ "\$LWS_WORKER_INDEX" = "0" ]; then
                  echo "Starting Ray Head..."
                  ray start --head --port=6379 --node-ip-address=\$MY_TPU_IP --resources='{"TPU": 4}' --block &
                  sleep 20
                  until ray status; do sleep 5; done
    
                  echo "Starting vLLM API Server..."
                  python3 -m vllm.entrypoints.openai.api_server \
                    --model=/models/qwen3-30b-weights \
                    --tensor-parallel-size=32 \
                    --pipeline-parallel-size=1 \
                    --distributed-executor-backend=ray \
                    --host=0.0.0.0 --port=8000 \
                    --enforce-eager \
                    --gpu-memory-utilization=0.90
                else
                  ray start --address=\${LEADER_IP}:6379 --node-ip-address=\$MY_TPU_IP --resources='{"TPU": 4}' --block
                fi
              ports:
              - containerPort: 8000
              - containerPort: 6379
              volumeMounts:
              - name: model-weights
                mountPath: /models
                readOnly: true
              - name: dshm
                mountPath: /dev/shm
              resources:
                claims:
                - name: net-resources
                limits:
                  google.com/tpu: 4
                  memory: "100Gi"
                requests:
                  google.com/tpu: 4
                  memory: "100Gi"
            nodeSelector:
              cloud.google.com/gke-tpu-accelerator: tpu-v6e-slice
              cloud.google.com/gke-tpu-topology: 4x8
              gke.networks.io/accelerator-network-profile: auto
            resourceClaims:
            - name: net-resources
              resourceClaimTemplateName: all-netdev
            volumes:
            - name: model-weights
              csi:
                driver: gcsfuse.csi.storage.gke.io
                volumeAttributes:
                  bucketName: ${BUCKET_NAME}
                  mountOptions: "implicit-dirs"
            - name: dshm
              emptyDir:
                medium: Memory
    EOF
    

10. Test Dağıtım Yanıtı

LeaderWorkerSet'teki tüm kapsüllerin container görüntülerini çekmesi, Ray'i başlatması ve tamamen Ready olması 5-10 dakika sürebilir. Pod başlatma işlemini izleyerek durumu takip edebilirsiniz:

kubectl get pods -l leaderworkerset.sigs.k8s.io/name=vllm-tpu-qwen -w

8 kapsülün tamamı vllm-tpu-qwen- STATUS olarak Running ve READY olarak 2/2 gösterilene kadar bekleyin ve devam etmeden önce yük dengeleyicinin harici IP aldığından emin olun. Bu işlem 7-10 dakika sürebilir.

  1. Harici IP'yi alma:
    export EXTERNAL_IP=$(kubectl get svc vllm-tpu-service -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo $EXTERNAL_IP
    

Dikkat: Üretim hizmetinde bu uç nokta, Identity-Aware Proxy (IAP) gibi bir yöntemle güvenli hale getirilmelidir.

  1. curl kullanarak çıkarım isteği gönderin:
        curl -N -s http://$EXTERNAL_IP:8000/v1/chat/completions \
            -H "Content-Type: application/json" \
            -d '{
                "model": "/models/qwen3-30b-weights",
                "messages": [{"role": "user", "content": "Write a haiku about high-performance computing on TPUs."}],
                "temperature": 0.7,
                "max_tokens": 100,
                "stream": true
            }' | sed 's/^data: //' | grep -v '\[DONE\]' | grep -v '^$' | jq -rj '.choices[0].delta.content // empty' ; echo ""
    
    Oluşturulan çıkarımınızı temsil eden metni içeren bir JSON yanıtına benzeyen bir çıkış görmelisiniz.

11. Temizleme

Google Cloud hesabınızın sürekli olarak ücretlendirilmesini önlemek için bu codelab sırasında oluşturulan kaynakları silin.

  1. Düğüm havuzunu silme:
    gcloud container node-pools delete "${NODE_POOL_NAME}" \
        --cluster="${CLUSTER_NAME}" \
        --region="${REGION}" \
        --project="${PROJECT_ID}" --quiet
    
  2. Kümeyi Sil:
    gcloud container clusters delete "${CLUSTER_NAME}" \
        --region="${REGION}" \
        --project="${PROJECT_ID}" --quiet
    
  3. Ağ ve güvenlik duvarı ayarlarını silme:
    gcloud compute firewall-rules delete \
        "${GVNIC_NETWORK_PREFIX}-allow-internal" \
        --project="${PROJECT_ID}" --quiet
    
    gcloud compute networks subnets delete "${GVNIC_NETWORK_PREFIX}-tpu" \
        --region="${REGION}" --quiet
    
    gcloud compute networks delete "${GVNIC_NETWORK_PREFIX}-main" --quiet
    
  4. Hizmet Hesabının Bağlantısını Kaldırma ve Silme:
        # 1. Create the cleanup script
        cat << 'EOF' > clean_up_sa.sh
        #!/bin/bash
    
        # Validate that PROJECT_ID is available
        if [ -z "$PROJECT_ID" ]; then
          echo "Error: PROJECT_ID environment variable is not set."
          exit 1
        fi
    
        SA_EMAIL="tpu-reader-sa@${PROJECT_ID}.iam.gserviceaccount.com"
        SA_MEMBER="serviceAccount:${SA_EMAIL}"
    
        echo "Gathering IAM policy for ${SA_EMAIL}..."
    
        # Fetch roles assigned to this specific SA
        ROLES=$(gcloud projects get-iam-policy ${PROJECT_ID} \
            --flatten="bindings[].members" \
            --filter="bindings.members:${SA_MEMBER}" \
            --format="value(bindings.role)")
    
        if [ -z "$ROLES" ]; then
            echo "No IAM bindings found for this service account."
        else
            for ROLE in $ROLES; do
                echo "Removing binding for: ${ROLE}..."
                gcloud projects remove-iam-policy-binding ${PROJECT_ID} \
                    --member="${SA_MEMBER}" \
                    --role="${ROLE}" --quiet > /dev/null
            done
            echo "Successfully unbound all roles."
        fi
    
        # 2. Delete the service account itself
        echo "Deleting service account..."
        gcloud iam service-accounts delete ${SA_EMAIL} --project=${PROJECT_ID} --quiet
    
        echo "Cleanup complete."
        EOF
    
        # 2. Make the script executable and run it
        chmod +x clean_up_sa.sh
        ./clean_up_sa.sh
    
  5. GCS paketini silme: Cloud Console'a gidin, Cloud Storage -> Buckets'ı (Cloud Storage -> Paketler) seçin, inf-demo-model-storage'ı seçin ve ardından "Sil"i seçin.

12. Tebrikler

Tebrikler! Google Kubernetes Engine'de Ray'i yerel olarak kullanan, çok ana makine TPU'lu yüksek çıkarım hızlı vLLM yığınını başarıyla dağıttınız.

Öğrendikleriniz

  • Yüksek hızlı TPU trafiğine göre uyarlanmış özel yollar sağlama.
  • GCS Fuse ve bölgesel hızlı önbellekler kullanılarak ağırlıkların monte edilmesi.
  • LeaderWorkerSets aracılığıyla yerel olarak senkronize edilen çok ana bilgisayarlı iş yükü dilimlerini düzenleme.
  • Daha fazla bilgi edinmek için vLLM Kullanıcı Kılavuzu ve llm-d Dağıtım Kılavuzları'na göz atın.