Menyajikan Qwen3.8-2.4T di GKE dengan disaggregasi PD multi-host menggunakan Dynamo dan SGLang di VM A4

Tutorial ini menunjukkan cara men-deploy dan menyajikan Qwen3.8-2.4T (nvidia/Qwen3.8-2.4T-A95B-NVFP4), model Mixture-of-Experts dengan 2,4 triliun parameter dan 95 miliar parameter aktif yang dikuantisasi ke NVFP4, di Google Kubernetes Engine (GKE) pada Google Cloud. Deployment ini menggunakan Cluster Toolkit, empat instance virtual machine (VM) A4 (a4-highgpu-8g) multi-host dengan total 32 GPU NVIDIA B200, NVIDIA Dynamo, dan framework inferensi SGLang dengan disagregasi prefill-decode (PD).

Dalam konfigurasi deployment yang dijelaskan dalam tutorial ini, Anda akan menggunakan Cluster Toolkit untuk menyediakan cluster GKE dalam mode Standard. Cluster ini berisi empat node a4-highgpu-8g yang terhubung melalui jaringan GPUDirect Remote Direct Memory Access (RDMA). Kemudian, Anda menginstal platform KAI Scheduler dan NVIDIA Dynamo dengan penjadwalan gang Grove, dan melakukan staging bobot model di Cloud Storage. Selanjutnya, Anda men-deploy resource kustom DynamoGraphDeployment yang memasang bucket dengan Cloud Storage FUSE serta memisahkan prefill dan dekode di dua worker pool multi-host 16 GPU dengan paralelisme tensor, paralelisme pakar, dan cache KV FP8.

Tujuan

  • Instal Cluster Toolkit dan sediakan cluster GKE A4 multi-host dalam mode Standard.
  • Instal platform KAI Scheduler dan NVIDIA Dynamo dengan penjadwalan gang Grove di cluster Anda.
  • Tempatkan bobot model Qwen3.8-2.4T di bucket Cloud Storage.
  • Men-deploy server inferensi SGLang multi-host yang dipisahkan PD di empat node A4, dengan total 32 GPU NVIDIA B200.
  • Mengirim permintaan penyelesaian chat ke router frontend Dynamo.
  • Menjalankan workload benchmark inferensi.
  • Bersihkan resource yang di-deploy.

Biaya

Dalam dokumen ini, Anda akan menggunakan komponenGoogle Cloudyang dapat ditagih berikut:

Untuk membuat perkiraan biaya berdasarkan proyeksi penggunaan Anda, gunakan kalkulator harga.

Sebelum memulai

Siapkan lingkungan Anda untuk tutorial:

  1. Instal dan lakukan inisialisasi Google Cloud CLI, serta instal kubectl dan Helm (versi 3.10 atau yang lebih baru). Jika Anda menggunakan Cloud Shell, alat ini sudah diinstal sebelumnya.
  2. Jika Anda menjalankan tutorial ini dari workstation Linux atau macOS, bukan Cloud Shell, instal dependensi Cluster Toolkit yang diperlukan, seperti Terraform dan Git.
  3. Aktifkan Kubernetes Engine API, Compute Engine API, Cloud Storage API, Identity and Access Management API, Cloud Resource Manager API, dan Service Usage API di project Google Cloud Anda:

    Aktifkan API

Mendapatkan token akses Hugging Face

Anda memerlukan token akses Hugging Face untuk menggunakan tutorial ini. Anda dapat mendaftar untuk mendapatkan akun gratis di Hugging Face. Setelah Anda memiliki akun, buat token akses:

  1. Di halaman Welcome to Hugging Face, klik avatar akun Anda, lalu pilih Token akses.
  2. Di halaman Token akses, klik Create new token.
  3. Pilih jenis token Read dan masukkan nama untuk token Anda.
  4. Token akses Anda akan ditampilkan. Simpan token di tempat yang aman.

Peran yang diperlukan

Untuk mendapatkan izin yang Anda perlukan untuk menyelesaikan tutorial ini, minta administrator untuk memberi Anda peran IAM berikut di project Anda:

Untuk mengetahui informasi selengkapnya tentang pemberian peran, lihat Mengelola akses ke project, folder, dan organisasi.

Anda mungkin juga bisa mendapatkan izin yang diperlukan melalui peran khusus atau peran bawaan lainnya.

Menyiapkan variabel lingkungan Anda

Ekspor variabel lingkungan yang Anda gunakan di sepanjang tutorial ini:

export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="YOUR_REGION"
export ZONE="YOUR_ZONE"
export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export RESERVATION_NAME="YOUR_RESERVATION_NAME"
export GCS_BUCKET="YOUR_BUCKET_NAME"
export HF_TOKEN="YOUR_HF_TOKEN"
export CLUSTER_TOOLKIT_VERSION="v1.104.0"
export DYNAMO_RELEASE_VERSION="1.5.0"

gcloud config set project "${PROJECT_ID}"
gcloud config set billing/quota_project "${PROJECT_ID}"

Ganti kode berikut:

  • YOUR_PROJECT_ID: ID project Google Cloud Anda.
  • YOUR_REGION: region tempat kapasitas A4 Anda berada dan tempat Anda ingin men-deploy cluster, seperti us-central1.
  • YOUR_ZONE: zona tempat kapasitas A4 Anda berada, seperti us-central1-b.
  • YOUR_CLUSTER_NAME: nama untuk cluster GKE dan deployment Cluster Toolkit Anda. Gunakan nama yang terdiri dari 20 karakter atau kurang agar ID akun layanan node pool yang dihasilkan (YOUR_CLUSTER_NAME-gke-np-sa) tetap berada dalam batas 30 karakter Identity and Access Management (IAM).
  • YOUR_RESERVATION_NAME: nama pemesanan A4 Anda, atau jalur blok pemesanan yang diperluas dalam format RESERVATION_NAME/reservationBlocks/RESERVATION_BLOCK_NAME.
  • YOUR_BUCKET_NAME: nama unik global untuk bucket Cloud Storage yang menyimpan status Terraform untuk Cluster Toolkit dan bobot model Qwen3.8-2.4T yang di-stage.
  • YOUR_HF_TOKEN: token akses Hugging Face Anda.

Membuat cluster GKE

Download Cluster Toolkit dan sediakan cluster GKE dalam mode Standard dengan empat node a4-highgpu-8g, jaringan GPUDirect RDMA, dan dukungan DRA:

  1. Login ke Google Cloud CLI dan buat Kredensial Default Aplikasi untuk Terraform:

    gcloud auth login
    gcloud auth application-default login
  2. Download dan ekstrak paket Cluster Toolkit bawaan untuk versi yang ditentukan dalam CLUSTER_TOOLKIT_VERSION (v1.104.0):

    TAG="${CLUSTER_TOOLKIT_VERSION:-v1.104.0}"
    OS="linux"
    ARCH="amd64"
    mkdir -p cluster-toolkit && curl -L "https://github.com/GoogleCloudPlatform/cluster-toolkit/releases/download/${TAG}/gcluster_bundle_${OS}_${ARCH}.tgz" | tar -xz -C cluster-toolkit

    Jika Anda menjalankan perintah ini di macOS atau di workstation arm64, tetapkan OS="mac" atau ARCH="arm64" agar sesuai dengan platform Anda.

  3. Untuk menyimpan status Terraform untuk deployment Cluster Toolkit dan bobot model yang di-stage, buat bucket Cloud Storage di region yang sama dengan pemesanan A4 Anda dengan akses level bucket yang seragam dan pembuatan versi objek diaktifkan:

    gcloud storage buckets create "gs://${GCS_BUCKET}" \
        --default-storage-class=STANDARD \
        --project="${PROJECT_ID}" \
        --location="${REGION}" \
        --uniform-bucket-level-access
    gcloud storage buckets update "gs://${GCS_BUCKET}" --versioning
  4. Buat file konfigurasi deployment cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml untuk empat node a4-highgpu-8g:

    cat <<EOF > cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml
    terraform_backend_defaults:
      type: gcs
      configuration:
        bucket: ${GCS_BUCKET}
    
    vars:
      project_id: ${PROJECT_ID}
      deployment_name: ${CLUSTER_NAME}
      region: ${REGION}
      zone: ${ZONE}
      static_node_count: 4
      authorized_cidr: 0.0.0.0/0
      reservation_affinity:
        consume_reservation_type: SPECIFIC_RESERVATION
        specific_reservations:
        - name: ${RESERVATION_NAME}
    EOF

    Secara default, file deployment dikonfigurasi untuk menggunakan pemesanan tertentu. Jika Anda tidak memiliki pemesanan khusus, sebelum men-deploy cluster, Anda dapat memperbarui cluster-toolkit/examples/gke-a4/gke-a4-deployment.yaml untuk menggunakan opsi konsumsi alternatif, seperti Spot VM atau Flex-start VM. Untuk petunjuk konfigurasi, lihat Opsi pemakaian GKE.

  5. Ubah ke direktori cluster-toolkit, deploy cluster GKE menggunakan perintah gcluster deploy, lalu kembali ke direktori kerja Anda:

    cd cluster-toolkit
    ./gcluster deploy \
        -d examples/gke-a4/gke-a4-deployment.yaml \
        examples/gke-a4/gke-a4.yaml \
        --auto-approve -w
    cd ..

    Pembuatan cluster memerlukan waktu sekitar 20 hingga 30 menit. Jika Anda menjalankan ./gcluster deploy tanpa flag --auto-approve, untuk menerapkan perubahan yang diusulkan, masukkan a saat diminta.

  6. Konfigurasi kredensial kubectl untuk berkomunikasi dengan cluster Anda:

    gcloud container clusters get-credentials "${CLUSTER_NAME}" \
        --location="${REGION}" \
        --project="${PROJECT_ID}"

Menginstal KAI Scheduler dan NVIDIA Dynamo

Siapkan cluster Anda dengan membuat Secret Hugging Face dan menginstal KAI Scheduler dan platform NVIDIA Dynamo dengan penjadwalan gang Grove:

  1. Buat Secret Kubernetes bernama hf-secret untuk menyimpan token akses Hugging Face Anda:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token="${HF_TOKEN}" \
        --dry-run=client -o yaml | kubectl apply -f -
  2. Download diagram Helm platform NVIDIA Dynamo (v1.5.0), instal KAI Scheduler, dan instal platform Dynamo dengan Grove diaktifkan:

    RELEASE_VERSION="${DYNAMO_RELEASE_VERSION:-1.5.0}"
    helm pull "https://helm.ngc.nvidia.com/nvidia/ai-dynamo/charts/dynamo-platform-${RELEASE_VERSION}.tgz"
    tar -zxf "dynamo-platform-${RELEASE_VERSION}.tgz"
    
    helm upgrade --install kai-scheduler ./dynamo-platform/charts/kai-scheduler \
        --set scheduler.args.default-staleness-grace-period="-1s" \
        --set topologyMigration.enabled=false
    
    kubectl wait --for=condition=Available deployment/kai-operator --timeout=120s
    until kubectl get deployment/queue-controller >/dev/null 2>&1; do sleep 2; done
    kubectl wait --for=condition=Available deployment/queue-controller --timeout=120s
    
    helm upgrade --install dynamo-platform ./dynamo-platform \
        --set global.grove.install=true \
        --set global.grove.enabled=true \
        --set global.kai-scheduler.enabled=true

Menyiapkan bobot model di Cloud Storage

Tempatkan checkpoint nvidia/Qwen3.8-2.4T-A95B-NVFP4 di bucket Cloud Storage dalam region Anda (gs://${GCS_BUCKET}) agar Pod worker inferensi dapat memasang bobot dengan Cloud Storage FUSE, bukan mendownloadnya dari Hugging Face pada setiap cold start:

  1. Berikan peran Storage Object User (roles/storage.objectUser) di bucket gs://${GCS_BUCKET} yang Anda buat di bagian Membuat cluster GKE ke akun layanan Google yang terikat ke akun layanan Kubernetes workload-identity-k8s-sa:

    GSA=$(kubectl get sa workload-identity-k8s-sa -o jsonpath='{.metadata.annotations.iam\.gke\.io/gcp-service-account}')
    gcloud storage buckets add-iam-policy-binding "gs://${GCS_BUCKET}" \
        --member="serviceAccount:${GSA}" \
        --role="roles/storage.objectUser"
  2. Download manifes qwen3.8-2.4t-nvfp4-stage-weights-job.yaml dan qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml:

    curl -O https://raw.githubusercontent.com/GoogleCloudSamples/AIHypercomputerSamples/refs/heads/main/gpu/inference/qwen38-2t/qwen3.8-2.4t-nvfp4-stage-weights-job.yaml
    curl -O https://raw.githubusercontent.com/GoogleCloudSamples/AIHypercomputerSamples/refs/heads/main/gpu/inference/qwen38-2t/qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml

    Atau, buat file bernama qwen3.8-2.4t-nvfp4-stage-weights-job.yaml dengan konten berikut. Job Terindeks ini menjalankan empat Pod paralel di empat node A4 Anda. Setiap Pod mendownload slice checkpoint nvidia/Qwen3.8-2.4T-A95B-NVFP4 dengan ukuran seimbang ke SSD NVMe Lokal node dan menguploadnya ke gs://${GCS_BUCKET}/hf_models/models--nvidia--qwen3_8-2_4t-a95b-nvfp4 menggunakan gcloud storage rsync:

    # ==============================================================================
    # Stage nvidia/Qwen3.8-2.4T-A95B-NVFP4 (~1.44 TB, 210 files) from Hugging Face Hub
    # into an in-region Cloud Storage bucket for qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml
    #
    # Each Indexed Job pod downloads a size-balanced slice of the repo onto the node's
    # local NVMe SSD, then uploads that slice with `gcloud storage rsync`.
    # ==============================================================================
    apiVersion: batch/v1
    kind: Job
    metadata:
      name: qwen3-8-2-4t-stage-weights
    spec:
      completionMode: Indexed
      completions: 4
      parallelism: 4
      backoffLimitPerIndex: 2
      template:
        metadata:
          labels:
            app: qwen3-8-2-4t-stage-weights
        spec:
          restartPolicy: Never
          serviceAccountName: workload-identity-k8s-sa
          nodeSelector:
            cloud.google.com/gke-accelerator: nvidia-b200
          tolerations:
            - key: "nvidia.com/gpu"
              operator: "Exists"
              effect: "NoSchedule"
          topologySpreadConstraints:
            - maxSkew: 1
              topologyKey: kubernetes.io/hostname
              whenUnsatisfiable: DoNotSchedule
              labelSelector:
                matchLabels:
                  app: qwen3-8-2-4t-stage-weights
          initContainers:
            - name: download
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              command: ["python3", "-c"]
              args:
                - |
                  import os, time
                  from huggingface_hub import HfApi, snapshot_download
                  repo = os.environ["REPO_ID"]
                  n = int(os.environ["NUM_SHARDS"])
                  i = int(os.environ["JOB_COMPLETION_INDEX"])
                  info = HfApi().model_info(repo, files_metadata=True)
                  files = sorted(((s.size or 0, s.rfilename) for s in info.siblings), reverse=True)
                  bins = [[0, []] for _ in range(n)]
                  for size, name in files:
                      b = min(bins, key=lambda x: x[0])
                      b[0] += size
                      b[1].append(name)
                  size, mine = bins[i]
                  print(f"shard {i}/{n}: {len(mine)} files, {size / 1e9:.1f} GB", flush=True)
                  for attempt in range(1, 7):
                      try:
                          snapshot_download(repo, local_dir="/ssd/model", allow_patterns=mine, max_workers=16)
                          break
                      except Exception as e:
                          print(f"shard {i}/{n}: attempt {attempt} failed: {e!r}", flush=True)
                          if attempt == 6:
                              raise
                          time.sleep(30)
                  print(f"shard {i}/{n}: download complete", flush=True)
              env:
                - name: REPO_ID
                  value: "nvidia/Qwen3.8-2.4T-A95B-NVFP4"
                - name: NUM_SHARDS
                  value: "4"
                - name: HF_HOME
                  value: "/ssd/hf"
                - name: HF_HUB_DISABLE_XET
                  value: "1"
                - name: HF_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: hf-secret
                      key: hf_api_token
                      optional: true
              resources:
                requests:
                  cpu: "16"
                  memory: "64Gi"
                  ephemeral-storage: "450Gi"
                limits:
                  cpu: "16"
                  memory: "64Gi"
                  ephemeral-storage: "700Gi"
              volumeMounts:
                - name: ssd
                  mountPath: /ssd
          containers:
            - name: upload
              image: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -euo pipefail
                  gcloud storage rsync --recursive --exclude='^\.cache/.*' /ssd/model "${DEST}"
                  echo "shard ${JOB_COMPLETION_INDEX}: upload complete"
              env:
                - name: DEST
                  value: "gs://${GCS_BUCKET}/hf_models/models--nvidia--qwen3_8-2_4t-a95b-nvfp4"
              resources:
                requests:
                  cpu: "16"
                  memory: "32Gi"
                limits:
                  cpu: "16"
                  memory: "32Gi"
              volumeMounts:
                - name: ssd
                  mountPath: /ssd
          volumes:
            - name: ssd
              emptyDir: {}
  3. Ganti nama bucket Cloud Storage Anda menjadi qwen3.8-2.4t-nvfp4-stage-weights-job.yaml, mulai Tugas staging, tunggu keempat Pod selesai mengupload bobot, lalu hapus Tugas yang telah selesai:

    envsubst '${GCS_BUCKET}' < qwen3.8-2.4t-nvfp4-stage-weights-job.yaml | kubectl apply -f -
    
    kubectl wait --for=condition=complete job/qwen3-8-2-4t-stage-weights --timeout=2h
    
    kubectl delete job qwen3-8-2-4t-stage-weights

    Mendownload bobot model ke SSD NVMe Lokal dan menguploadnya ke Cloud Storage memerlukan waktu sekitar 25 menit.

  4. Opsional: Saat Tugas staging berjalan, lakukan streaming log Pod staging dalam sesi terminal kedua untuk memantau progres download:

    POD_NAME="YOUR_POD_NAME"
    kubectl logs -f "${POD_NAME}"

    Ganti YOUR_POD_NAME dengan nama salah satu Pod qwen3-8-2-4t-stage-weights dari kubectl get pods.

Men-deploy server SGLang yang dipisahkan multi-host

Manifes workload (qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml) menentukan ResourceClaimTemplate Kubernetes (all-mrdma), yang mengalokasikan perangkat RDMA multi-NIC node (mrdma.google.com) melalui resource DRA, Role, dan RoleBinding untuk akun layanan workload-identity-k8s-sa, dan NVIDIA Dynamo DynamoGraphDeployment (qwen3-8-2-4t-a4-disagg) dengan penjadwalan gang Grove diaktifkan. Deployment terdiri dari hal berikut:

  • Frontend (Frontend): menjalankan satu replika router yang menerima permintaan klien, menemukan worker prefill dan dekode yang terdaftar, serta mengoordinasikan pemilihan rute permintaan yang dipisahkan.

  • Worker prefill (Prefill): menjalankan worker pool dua node di dua node a4-highgpu-8g dengan total 16 GPU NVIDIA B200. Setiap Pod memasang bobot nvidia/Qwen3.8-2.4T-A95B-NVFP4 yang di-stage dari bucket Cloud Storage Anda di /model-weights menggunakan driver CSI Cloud Storage FUSE (gcsfuse.csi.storage.gke.io) dengan caching file SSD NVMe Lokal, mengambil sumber lingkungan NCCL/gIB, dan meluncurkan python3 -m dynamo.sglang di --disaggregation-mode prefill dengan flag berikut:

    • --served-model-name "nvidia/Qwen3.8-2.4T-A95B-NVFP4"
    • --tensor-parallel-size 16
    • --ep-size 16
    • --nnodes 2
    • --moe-runner-backend flashinfer_trtllm
    • --attention-backend triton
    • --kv-cache-dtype fp8_e4m3
    • --disable-flashinfer-autotune
  • Worker dekode (Decode): menjalankan worker pool dua node di dua node a4-highgpu-8g yang tersisa, dengan total 16 GPU NVIDIA B200. Setiap Pod memasang bobot model yang di-stage dari Cloud Storage di /model-weights melalui driver CSI Cloud Storage FUSE dan meluncurkan python3 -m dynamo.sglang di --disaggregation-mode decode dengan flag berikut:

    • --served-model-name "nvidia/Qwen3.8-2.4T-A95B-NVFP4"
    • --tensor-parallel-size 16
    • --ep-size 16
    • --nnodes 2
    • --moe-runner-backend flashinfer_trtllm
    • --attention-backend triton
    • --kv-cache-dtype fp8_e4m3

Deploy dan pantau server inferensi yang dipisahkan:

  1. Tinjau manifes workload qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml yang Anda download sebelumnya, atau buat file bernama qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml dengan konten berikut:

    # ==============================================================================
    # Multihost Disaggregated Serving of Qwen3.8-2.4T-A95B (NVFP4 MoE) on GKE
    # using NVIDIA Dynamo Platform (v1.5.0) and SGLang on GPU A4 (4 x a4-highgpu-8g)
    # ==============================================================================
    apiVersion: resource.k8s.io/v1
    kind: ResourceClaimTemplate
    metadata:
      name: all-mrdma
    spec:
      spec:
        devices:
          requests:
            - name: req-mrdma
              exactly:
                deviceClassName: mrdma.google.com
                allocationMode: All
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: dynamo-workload-identity-discovery-role
      namespace: default
    rules:
      - apiGroups: [""]
        resources: ["endpoints", "pods"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["discovery.k8s.io"]
        resources: ["endpointslices"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["nvidia.com"]
        resources: ["dynamoworkermetadatas"]
        verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: dynamo-workload-identity-discovery-binding
      namespace: default
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: Role
      name: dynamo-workload-identity-discovery-role
    subjects:
      - kind: ServiceAccount
        name: workload-identity-k8s-sa
        namespace: default
    ---
    apiVersion: nvidia.com/v1alpha1
    kind: DynamoGraphDeployment
    metadata:
      name: qwen3-8-2-4t-a4-disagg
      annotations:
        nvidia.com/enable-grove: "true"
    spec:
      backendFramework: sglang
      services:
        # --------------------------------------------------------------------------
        # FRONTEND: Dynamo OpenAI-Compatible Router (`python3 -m dynamo.frontend`)
        # --------------------------------------------------------------------------
        Frontend:
          componentType: frontend
          replicas: 1
          resources:
            requests:
              cpu: "8"
              memory: "32Gi"
            limits:
              cpu: "8"
              memory: "32Gi"
          extraPodSpec:
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
            mainContainer:
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -ex
                  exec python3 -m dynamo.frontend --http-port 8000
    
        # --------------------------------------------------------------------------
        # PREFILL WORKER: 2 x GPU A4 (`a4-highgpu-8g`, 16 x B200 GPUs, TP=16, EP=16)
        # --------------------------------------------------------------------------
        Prefill:
          componentType: worker
          subComponentType: prefill
          replicas: 1
          multinode:
            nodeCount: 2
          extraPodMetadata:
            annotations:
              gke-gcsfuse/volumes: "true"
          livenessProbe:
            httpGet:
              path: /live
              port: system
            periodSeconds: 60
            timeoutSeconds: 30
            failureThreshold: 60
          readinessProbe:
            httpGet:
              path: /health
              port: system
            initialDelaySeconds: 120
            periodSeconds: 30
            timeoutSeconds: 30
            failureThreshold: 60
          sharedMemory:
            size: 128Gi
          resources:
            requests:
              gpu: "8"
              cpu: "128"
              memory: "1500Gi"
            limits:
              gpu: "8"
              cpu: "128"
              memory: "2000Gi"
          envs:
            - name: MODEL_PATH
              value: "/model-weights/hf_models/models--nvidia--qwen3_8-2_4t-a95b-nvfp4"
            - name: DYN_SYSTEM_PORT
              value: "9090"
            - name: LD_LIBRARY_PATH
              value: "/usr/local/gib/lib64:/usr/local/nvidia/lib64"
            - name: GLOO_SOCKET_IFNAME
              value: "eth0"
            - name: NCCL_ALGO
              value: "Ring,Tree"
            - name: WITH_NVIDIA_PEERMEM
              value: "0"
          extraPodSpec:
            serviceAccountName: workload-identity-k8s-sa
            automountServiceAccountToken: true
            resourceClaims:
              - name: rdma
                resourceClaimTemplateName: all-mrdma
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-b200
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
            mainContainer:
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              resources:
                claims:
                  - name: rdma
              startupProbe:
                httpGet:
                  path: /live
                  port: system
                initialDelaySeconds: 120
                periodSeconds: 30
                timeoutSeconds: 10
                failureThreshold: 120
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -ex
                  if [ -f /usr/local/gib/scripts/set_nccl_env.sh ]; then
                    . /usr/local/gib/scripts/set_nccl_env.sh
                  fi
    
                  echo "=== [Dynamo Prefill Worker] Launching dynamo.sglang ==="
                  exec python3 -m dynamo.sglang \
                    --dist-init-addr "${GROVE_PCSG_NAME}-${GROVE_PCSG_INDEX}-prefill-ldr-0.${GROVE_HEADLESS_SERVICE}.:29500" \
                    --model-path "${MODEL_PATH}" \
                    --served-model-name "nvidia/Qwen3.8-2.4T-A95B-NVFP4" \
                    --host 0.0.0.0 \
                    --disaggregation-mode prefill \
                    --disaggregation-bootstrap-port 30001 \
                    --tensor-parallel-size 16 \
                    --ep-size 16 \
                    --moe-runner-backend flashinfer_trtllm \
                    --attention-backend triton \
                    --disable-flashinfer-autotune \
                    --nnodes 2 \
                    --kv-cache-dtype fp8_e4m3 \
                    --context-length 16384 \
                    --mem-fraction-static 0.85 \
                    --enable-metrics
              volumeMounts:
                - name: library-dir-host
                  mountPath: /usr/local/nvidia
                - name: gib
                  mountPath: /usr/local/gib
                - name: gcs-model-dir
                  mountPath: /model-weights
                  readOnly: true
            volumes:
              - name: library-dir-host
                hostPath:
                  path: /home/kubernetes/bin/nvidia
              - name: gib
                hostPath:
                  path: /home/kubernetes/bin/gib
              - name: gcs-model-dir
                csi:
                  driver: gcsfuse.csi.storage.gke.io
                  readOnly: true
                  volumeAttributes:
                    bucketName: ${GCS_BUCKET}
                    mountOptions: "implicit-dirs,file-cache:max-size-mb:-1"
    
        # --------------------------------------------------------------------------
        # DECODE WORKER: 2 x GPU A4 (`a4-highgpu-8g`, 16 x B200 GPUs, TP=16, EP=16)
        # --------------------------------------------------------------------------
        Decode:
          componentType: worker
          subComponentType: decode
          replicas: 1
          multinode:
            nodeCount: 2
          extraPodMetadata:
            annotations:
              gke-gcsfuse/volumes: "true"
          livenessProbe:
            httpGet:
              path: /live
              port: system
            periodSeconds: 60
            timeoutSeconds: 30
            failureThreshold: 60
          readinessProbe:
            httpGet:
              path: /health
              port: system
            initialDelaySeconds: 120
            periodSeconds: 30
            timeoutSeconds: 30
            failureThreshold: 60
          sharedMemory:
            size: 128Gi
          resources:
            requests:
              gpu: "8"
              cpu: "128"
              memory: "1500Gi"
            limits:
              gpu: "8"
              cpu: "128"
              memory: "2000Gi"
          envs:
            - name: MODEL_PATH
              value: "/model-weights/hf_models/models--nvidia--qwen3_8-2_4t-a95b-nvfp4"
            - name: DYN_SYSTEM_PORT
              value: "9090"
            - name: LD_LIBRARY_PATH
              value: "/usr/local/gib/lib64:/usr/local/nvidia/lib64"
            - name: GLOO_SOCKET_IFNAME
              value: "eth0"
            - name: NCCL_ALGO
              value: "Ring,Tree"
            - name: WITH_NVIDIA_PEERMEM
              value: "0"
          extraPodSpec:
            serviceAccountName: workload-identity-k8s-sa
            automountServiceAccountToken: true
            resourceClaims:
              - name: rdma
                resourceClaimTemplateName: all-mrdma
            nodeSelector:
              cloud.google.com/gke-accelerator: nvidia-b200
            tolerations:
              - key: "nvidia.com/gpu"
                operator: "Exists"
                effect: "NoSchedule"
            mainContainer:
              image: nvcr.io/nvidia/ai-dynamo/sglang-runtime:1.5.0
              resources:
                claims:
                  - name: rdma
              startupProbe:
                httpGet:
                  path: /live
                  port: system
                initialDelaySeconds: 120
                periodSeconds: 30
                timeoutSeconds: 10
                failureThreshold: 120
              command: ["/bin/bash", "-c"]
              args:
                - |
                  set -ex
                  if [ -f /usr/local/gib/scripts/set_nccl_env.sh ]; then
                    . /usr/local/gib/scripts/set_nccl_env.sh
                  fi
    
                  echo "=== [Dynamo Decode Worker] Launching dynamo.sglang ==="
                  exec python3 -m dynamo.sglang \
                    --dist-init-addr "${GROVE_PCSG_NAME}-${GROVE_PCSG_INDEX}-decode-ldr-0.${GROVE_HEADLESS_SERVICE}.:29500" \
                    --model-path "${MODEL_PATH}" \
                    --served-model-name "nvidia/Qwen3.8-2.4T-A95B-NVFP4" \
                    --host 0.0.0.0 \
                    --disaggregation-mode decode \
                    --disaggregation-bootstrap-port 30001 \
                    --tensor-parallel-size 16 \
                    --ep-size 16 \
                    --moe-runner-backend flashinfer_trtllm \
                    --attention-backend triton \
                    --nnodes 2 \
                    --kv-cache-dtype fp8_e4m3 \
                    --context-length 16384 \
                    --mem-fraction-static 0.85 \
                    --enable-metrics
              volumeMounts:
                - name: library-dir-host
                  mountPath: /usr/local/nvidia
                - name: gib
                  mountPath: /usr/local/gib
                - name: gcs-model-dir
                  mountPath: /model-weights
                  readOnly: true
            volumes:
              - name: library-dir-host
                hostPath:
                  path: /home/kubernetes/bin/nvidia
              - name: gib
                hostPath:
                  path: /home/kubernetes/bin/gib
              - name: gcs-model-dir
                csi:
                  driver: gcsfuse.csi.storage.gke.io
                  readOnly: true
                  volumeAttributes:
                    bucketName: ${GCS_BUCKET}
                    mountOptions: "implicit-dirs,file-cache:max-size-mb:-1"
  2. Ganti nama bucket Cloud Storage Anda menjadi qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml dan terapkan manifes workload ke cluster Anda:

    envsubst '${GCS_BUCKET}' < qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml | kubectl apply -f -
  3. Untuk memantau pemuatan bobot dan inisialisasi server, buat daftar Pod di cluster Anda, dan streaming log Pod leader prefill dan Pod leader decode:

    kubectl get pods
    POD_NAME="YOUR_POD_NAME"
    kubectl logs -f "${POD_NAME}"

    Ganti YOUR_POD_NAME dengan nama Pod leader prefill atau Pod leader dekode dari output kubectl get pods.

    Pada cold start, diperlukan waktu sekitar 4 hingga 5 menit untuk melakukan streaming bobot model NVFP4 2.400.000.000.000 (~1,44 TB di 200 shard) dari Cloud Storage melalui Cloud Storage FUSE dan melakukan inisialisasi server SGLang multi-host. Sebelum Anda mengirim permintaan apa pun, tunggu hingga Pod frontend menampilkan 1/1 dan Pod prefill dan decode menampilkan 2/2 di kolom READY dari kubectl get pods:

    NAME                                                     READY   STATUS    RESTARTS   AGE
    qwen3-8-2-4t-a4-disagg-0-decode-0-decode-ldr-wz92h       2/2     Running   0          5m
    qwen3-8-2-4t-a4-disagg-0-decode-0-decode-wkr-fb8g6       2/2     Running   0          5m
    qwen3-8-2-4t-a4-disagg-0-frontend-jrx4s                  1/1     Running   0          5m
    qwen3-8-2-4t-a4-disagg-0-prefill-0-prefill-ldr-7nz25     2/2     Running   0          5m
    qwen3-8-2-4t-a4-disagg-0-prefill-0-prefill-wkr-7d5cw     2/2     Running   0          5m
    

Mengirim permintaan inferensi

Kirim permintaan penyelesaian chat ke layanan Qwen3.8-2.4T yang di-deploy:

  1. Teruskan port lokal 8000 ke layanan qwen3-8-2-4t-a4-disagg-frontend:

    kubectl port-forward svc/qwen3-8-2-4t-a4-disagg-frontend 8000:8000

    Biarkan penerusan port berjalan di sesi terminal ini, atau jalankan perintah di latar belakang.

  2. Di sesi terminal kedua, kirim permintaan penyelesaian chat ke endpoint /v1/chat/completions:

    curl -s http://127.0.0.1:8000/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "nvidia/Qwen3.8-2.4T-A95B-NVFP4",
        "messages": [{"role": "user", "content": "Explain KV-cache disaggregation in one sentence."}],
        "max_tokens": 64
      }' | jq .

Menjalankan workload benchmark

Lakukan tolok ukur server Qwen3.8-2.4T yang di-deploy dari Pod frontend Dynamo menggunakan sglang.bench_serving dengan konfigurasi 1.024 token input, 1.024 token output (1k/1k), 256 prompt, dan konkurensi maksimum 128:

ROUTER_POD=$(kubectl get pod -l 'nvidia.com/dynamo-component-type=frontend' -o jsonpath='{.items[0].metadata.name}')

kubectl exec "${ROUTER_POD}" -- python3 -m sglang.bench_serving \
  --backend sglang-oai-chat \
  --base-url http://localhost:8000 \
  --model nvidia/Qwen3.8-2.4T-A95B-NVFP4 \
  --dataset-name random \
  --num-prompts 256 \
  --random-input-len 1024 \
  --random-output-len 1024 \
  --random-range-ratio 1.0 \
  --max-concurrency 128 | tee benchmark.log

Setelah benchmark selesai, cari output yang berisi metrik seperti berikut:

  • Output token throughput
  • Total Token throughput
  • TTFT
  • TPOT

Pembersihan

Agar tidak menimbulkan biaya berkelanjutan tambahan pada akun Google Cloud Anda untuk resource yang digunakan dalam tutorial ini, hapus workload Kubernetes, cluster GKE, dan bucket Cloud Storage Anda:

  1. Hapus resource DynamoGraphDeployment dan ResourceClaimTemplate:

    kubectl delete -f qwen3.8-2.4t-nvfp4-multihost-a4-gcsfuse.yaml --ignore-not-found || true
  2. Uninstal rilis Helm platform NVIDIA Dynamo dan KAI Scheduler:

    helm uninstall dynamo-platform kai-scheduler || true
  3. Hapus Secret Kubernetes Hugging Face:

    kubectl delete secret hf-secret --ignore-not-found || true
  4. Dari direktori cluster-toolkit, hancurkan cluster GKE dan infrastruktur terkaitnya menggunakan perintah gcluster destroy:

    cd cluster-toolkit
    ./gcluster destroy "${CLUSTER_NAME}" --auto-approve || true
    cd ..
  5. Hapus bucket Cloud Storage yang menyimpan bobot model yang di-stage dan status Terraform:

    gcloud storage rm --recursive "gs://${GCS_BUCKET}"
    

Langkah berikutnya