Menyajikan Qwen3-Coder-480B di GKE dengan SGLang TorchTPU multi-host di akselerator TPU7x

Tutorial ini menunjukkan cara men-deploy dan menyajikan Qwen3-Coder-480B (Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8), model coding Mixture-of-Experts, di Google Kubernetes Engine (GKE). Deployment ini menggunakan akselerator TPU7x (Ironwood) multi-host dan framework inferensi SGLang dengan TorchTPU (torchtpu) dan disagregasi prefill-decode (PD).

Dalam konfigurasi deployment yang dijelaskan dalam tutorial ini, Anda akan menyediakan dua node pool tpu7x-standard-4t multi-host, yang masing-masing disusun dalam topologi 2x2x2 (delapan chip TPU7x di dua node per node pool, dengan total 16 chip TPU7x). Anda menggunakan Kubernetes LeaderWorkerSet API untuk melakukan hal berikut:

  • Jalankan server pra-pengisian dan dekode multi-host yang dipisahkan pada slice TPU7x terpisah dengan tensor parallelism dan expert parallelism diaktifkan.
  • Merutekan permintaan yang kompatibel dengan OpenAI melalui router disagregasi SGLang.

Tujuan

  • Siapkan cluster GKE dan node pool TPU7x multi-host.
  • Mengonfigurasi kredensial dan menginstal pengontrol LeaderWorkerSet.
  • Men-deploy workload prefill, dekode, dan router SGLang multi-host.
  • Kirim permintaan inferensi ke layanan router SGLang.
  • Menjalankan workload benchmark inferensi.
  • Jalankan pembersihan.

Biaya

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

Untuk membuat perkiraan biaya berdasarkan proyeksi penggunaan Anda, gunakan [kalkulator harga]((https://cloud.google.com/products/calculator).

Setelah menyelesaikan tugas yang dijelaskan dalam dokumen ini, Anda dapat menghindari penagihan berkelanjutan dengan menghapus resource yang Anda buat. Untuk mengetahui informasi selengkapnya, lihat Pembersihan.

Sebelum memulai

  • 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.

  • Aktifkan Kubernetes Engine API, Compute Engine API, Cloud TPU API, dan Artifact Registry API di project Google Cloud Anda:

    Aktifkan API

  • Pastikan project Anda memiliki kuota disk dan kuota alamat IP eksternal yang memadai di region tempat kapasitas TPU Anda berada:

    1. Di konsol Google Cloud , buka halaman Quotas & System Limits.

      Buka Quotas & System Limits

    2. Pastikan Anda memiliki setidaknya 2.800 GiB kuota Google Cloud Hyperdisk Balanced yang tersedia di empat volume efemeral 700 GiB yang digunakan untuk caching model oleh Pod prefill dan decode.

    3. Filter menurut In-use regional external IPv4 addresses dan pastikan Anda memiliki kuota alamat IP yang cukup di region target.

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.

Pastikan akun layanan default Compute Engine Anda memiliki peran Identity and Access Management (IAM) berikut di project Anda:

Menyiapkan variabel lingkungan Anda

Ekspor variabel lingkungan yang Anda gunakan di sepanjang tutorial ini:

export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="YOUR_REGION"
export ZONE="YOUR_ZONE"
export RESERVATION="YOUR_RESERVATION_NAME"
export HF_TOKEN="YOUR_HF_TOKEN"
export SGLANG_IMAGE="lmsysorg/sglang:v0.1.0-tpu"

Ganti kode berikut:

  • YOUR_CLUSTER_NAME: nama cluster GKE Anda.
  • YOUR_PROJECT_ID: ID project Google Cloud Anda.
  • YOUR_REGION: region tempat Anda ingin men-deploy cluster, seperti us-central1.
  • YOUR_ZONE: zona tempat Anda ingin men-deploy node pool TPU7x, seperti us-central1-c.
  • YOUR_RESERVATION_NAME: jika Anda menggunakan pemesanan, nama pemesanan TPU7x Anda.
  • YOUR_HF_TOKEN: token akses Hugging Face Anda.
  • YOUR_SGLANG_TPU_IMAGE: jika Anda menggunakan image container TorchTPU SGLang kustom, URI image Anda; jika tidak, image TorchTPU SGLang default.

Membuat cluster GKE

Untuk membuat cluster GKE dalam mode Standard dan memasang dua node pool TPU7x multi-host untuk disaggregasi prefill dan decode, selesaikan langkah-langkah berikut:

  1. Buat cluster GKE regional yang menggunakan node e2-standard-4 untuk node pool default, yang menyediakan resource CPU dan memori yang memadai untuk pengontrol LeaderWorkerSet dan router disagregasi SGLang:

    gcloud container clusters create $CLUSTER_NAME \
      --project=$PROJECT_ID \
      --location=$REGION \
      --machine-type=e2-standard-4
  2. Buat kebijakan penempatan workload dengan throughput tinggi dengan topologi akselerator 2x2x2, yang diperlukan untuk node pool TPU7x multi-host:

    gcloud compute resource-policies create workload-policy tpu7x-2x2x2-workload-policy \
        --type=HIGH_THROUGHPUT \
        --accelerator-topology=2x2x2 \
        --project=${PROJECT_ID} \
        --region=${REGION}
  3. Buat dua node pool TPU7x multi-host (tpu7x-pool-0 dan tpu7x-pool-1), masing-masing berisi dua node tpu7x-standard-4t (delapan chip TPU7x dalam topologi 2x2x2 per node pool, dengan total 16 chip TPU7x):

    gcloud container node-pools create tpu7x-pool-0 \
        --cluster=$CLUSTER_NAME \
        --location=$REGION \
        --node-locations=$ZONE \
        --machine-type=tpu7x-standard-4t \
        --num-nodes=2 \
        --placement-policy=tpu7x-2x2x2-workload-policy \
        --reservation-affinity=specific \
        --reservation=$RESERVATION \
        --project=$PROJECT_ID
    
    gcloud container node-pools create tpu7x-pool-1 \
        --cluster=$CLUSTER_NAME \
        --location=$REGION \
        --node-locations=$ZONE \
        --machine-type=tpu7x-standard-4t \
        --num-nodes=2 \
        --placement-policy=tpu7x-2x2x2-workload-policy \
        --reservation-affinity=specific \
        --reservation=$RESERVATION \
        --project=$PROJECT_ID

    Alih-alih menggunakan pemesanan tertentu, Anda juga dapat membuat node pool menggunakan kapasitas Spot atau VM Flex-start dengan meneruskan flag berikut:

    • Spot VM: ganti --reservation-affinity=specific dan --reservation=${RESERVATION} dengan --spot untuk menggunakan kapasitas Spot. Instance yang menggunakan kapasitas Spot mungkin memerlukan waktu untuk dibuat setelah node pool dibuat, dan mungkin di-preempt selama workload berjalan.
    • Flex-start VM: ganti --num-nodes=2, --reservation-affinity=specific, dan --reservation=${RESERVATION} dengan --flex-start, --enable-autoscaling, --num-nodes=0, --min-nodes=0, dan --max-nodes=2 untuk menyediakan node pool menggunakan opsi konsumsi Flex-start. Untuk mengetahui informasi selengkapnya, lihat Menjalankan workload batch kecil dengan TPU dan VM Flex-start.
  4. Konfigurasi kredensial kubectl untuk berkomunikasi dengan cluster Anda:

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

Mengonfigurasi kredensial dan menginstal LeaderWorkerSet

Untuk menyiapkan cluster Anda untuk deployment SGLang multi-host, selesaikan langkah-langkah berikut:

  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}
  2. Instal CustomResourceDefinition dan pengontrol LeaderWorkerSet di cluster Anda:

    kubectl apply --server-side -k github.com/kubernetes-sigs/lws/config/default?ref=main
    echo "Waiting for LWS controller to be ready..."
    kubectl wait --for=condition=Available --timeout=300s deployment/lws-controller-manager -n lws-system

Men-deploy server SGLang multi-host

Manifes workload (qwen3-coder-480b-multihost-disagg-v7x.yaml) menentukan resource berikut untuk mengoordinasikan prefill dan inferensi decode yang dipisahkan di dua node pool 2x2x2 TPU7x:

  • StorageClass (hyperdisk-balanced-tpu): menyediakan volume ReadWriteOnce 700 GiB efemeral yang didukung oleh penyimpanan Google Cloud Hyperdisk Balanced. Setiap dari empat Pod prefill dan dekode memasang volumenya sendiri di /data untuk menyimpan checkpoint Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 yang didownload secara lokal.
  • Prefill pool (sglang-prefill-service dan sglang-tpu-prefill): menjalankan LeaderWorkerSet size: 2 (satu Pod leader dan satu Pod worker) yang disematkan secara eksklusif ke satu node pool 2x2x2 TPU7x (delapan TPU chip, yang menyediakan 16 TensorCore). LeaderWorkerSet menggunakan anotasi leaderworkerset.sigs.k8s.io/exclusive-topology yang ditetapkan ke "cloud.google.com/gke-nodepool". Pod meluncurkan sglang.launch_server dengan flag --disaggregation-mode=prefill, --tensor-parallel-size=16, dan --ep-size=16, yang mengekspos port HTTP 8000 dan port bootstrap 8998.
  • Decode pool (sglang-decode-service dan sglang-tpu-decode): menjalankan LeaderWorkerSet size: 2 yang disematkan ke node pool TPU7x 2x2x2 kedua (delapan TPU chip, yang menyediakan 16 TensorCore). LeaderWorkerSet meluncurkan sglang.launch_server dengan flag --disaggregation-mode=decode, --tensor-parallel-size=16, dan --ep-size=16.
  • Router disagregasi (sglang-router dan sglang-service): menjalankan Deployment CPU yang mengeksekusi modul sglang_router.launch_router dengan flag --pd-disaggregation dan mengekspos Layanan ClusterIP di port 8000. Router mengarahkan perintah dan mengoordinasikan transfer cache KV antara layanan sglang-prefill-service dan sglang-decode-service.

Untuk men-deploy dan memantau server SGLang multi-host, selesaikan langkah-langkah berikut:

  1. Download manifes workload:

    curl -LO https://raw.githubusercontent.com/GoogleCloudSamples/AIHypercomputerSamples/main/tpu/inference/qwen3-coder/qwen3-coder-480b-multihost-disagg-v7x.yaml

    Atau, buat file bernama qwen3-coder-480b-multihost-disagg-v7x.yaml dengan konten berikut:

    # ==============================================================================
    # Serve Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 on GKE / Ironwood (TPU v7x)
    # using SGLang TorchTPU with Prefill/Decode Disaggregation, multi-host
    # ==============================================================================
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: hyperdisk-balanced-tpu
    provisioner: pd.csi.storage.gke.io
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true
    parameters:
      type: hyperdisk-balanced
    ---
    # ==============================================================================
    # PREFILL POOL: Internal ClusterIP Service exposing SGLang Prefill Endpoint
    # ==============================================================================
    apiVersion: v1
    kind: Service
    metadata:
      name: sglang-prefill-service
      labels:
        app: sglang-prefill
    spec:
      type: ClusterIP
      selector:
        app: sglang-prefill
        role: leader
      ports:
        - name: http
          protocol: TCP
          port: 8000
          targetPort: 8000
        - name: bootstrap
          protocol: TCP
          port: 8998
          targetPort: 8998
    ---
    # ==============================================================================
    # PREFILL POOL: LeaderWorkerSet (TPU v7x, 1 slice = 2 nodes, 8 chips / 16 TensorCores)
    # ==============================================================================
    apiVersion: leaderworkerset.x-k8s.io/v1
    kind: LeaderWorkerSet
    metadata:
      name: sglang-tpu-prefill
      annotations:
        leaderworkerset.sigs.k8s.io/exclusive-topology: "cloud.google.com/gke-nodepool"
      labels:
        app: sglang-prefill
    spec:
      replicas: 1
      rolloutStrategy:
        type: RollingUpdate
      leaderWorkerTemplate:
        size: 2
        restartPolicy: RecreateGroupOnPodRestart
        leaderTemplate:
          metadata:
            labels:
              app: sglang-prefill
              role: leader
          spec: &prefill-pod-spec
            securityContext:
              fsGroup: 65534
            nodeSelector:
              cloud.google.com/gke-tpu-accelerator: tpu7x
              cloud.google.com/gke-tpu-topology: 2x2x2
            tolerations:
              - key: "google.com/tpu"
                operator: "Exists"
                effect: "NoSchedule"
            containers:
              - name: sglang
                image: $SGLANG_IMAGE
                command:
                  - /bin/bash
                  - -c
                  - |
                    set -ex
                    ulimit -c 0
                    LEADER_POD="${LWS_LEADER_ADDRESS%%.*}"
                    DNS_SUFFIX="${LWS_LEADER_ADDRESS#*.}"
                    HOSTS="${LWS_LEADER_ADDRESS}"
                    for ((i = 1; i < LWS_GROUP_SIZE; i++)); do
                      HOSTS="${HOSTS},${LEADER_POD}-${i}.${DNS_SUFFIX}"
                    done
                    export TPU_WORKER_HOSTNAMES="${HOSTS}"
                    export TPU_WORKER_ID="${LWS_WORKER_INDEX}"
                    export TPU_TOPOLOGY="${TPU_TOPOLOGY:-2x2x2}"
                    echo "=== [prefill rank ${LWS_WORKER_INDEX}/${LWS_GROUP_SIZE}] hosts=${TPU_WORKER_HOSTNAMES} ==="
                    exec python3 -m sglang.launch_server \
                      --device=tpu \
                      --host=0.0.0.0 \
                      --port=8000 \
                      --model-path=Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 \
                      --tensor-parallel-size=16 \
                      --ep-size=16 \
                      --nnodes="${LWS_GROUP_SIZE}" \
                      --node-rank="${LWS_WORKER_INDEX}" \
                      --dist-init-addr="${LWS_LEADER_ADDRESS}:20000" \
                      --attention-backend=pallas_tpu_rpa_batched \
                      --mem-fraction-static=0.8 \
                      --max-running-requests=256 \
                      --chunked-prefill-size=8192 \
                      --max-total-tokens=786432 \
                      --context-length=9216 \
                      --enable-torch-compile \
                      --torch-compile-max-bs=256 \
                      --tpu-compile-tracer=direct \
                      --sampling-defaults=openai \
                      --enable-fused-qk-norm-rope \
                      --disaggregation-mode=prefill \
                      --disaggregation-transfer-backend=tpu \
                      --download-dir=/data \
                      --tpu-compile-tokens=8192 \
                      --disable-radix-cache
                env:
                  - name: HF_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                        optional: true
                resources:
                  requests:
                    cpu: "16"
                    memory: 128Gi
                    google.com/tpu: "4"
                  limits:
                    memory: 768Gi
                    google.com/tpu: "4"
                ports:
                  - containerPort: 8000
                    name: http
                  - containerPort: 8998
                    name: bootstrap
                startupProbe:
                  httpGet:
                    path: /health
                    port: 8000
                  initialDelaySeconds: 60
                  periodSeconds: 15
                  timeoutSeconds: 10
                  failureThreshold: 236
                readinessProbe:
                  httpGet:
                    path: /health
                    port: 8000
                  periodSeconds: 10
                  timeoutSeconds: 5
                  failureThreshold: 3
                volumeMounts:
                  - name: model-cache
                    mountPath: /data
                  - name: dshm
                    mountPath: /dev/shm
            volumes:
              - name: dshm
                emptyDir:
                  medium: Memory
                  sizeLimit: 32Gi
              - name: model-cache
                ephemeral:
                  volumeClaimTemplate:
                    spec:
                      accessModes: ["ReadWriteOnce"]
                      storageClassName: hyperdisk-balanced-tpu
                      resources:
                        requests:
                          storage: 700Gi
        workerTemplate:
          metadata:
            labels:
              app: sglang-prefill
              role: worker
          spec: *prefill-pod-spec
    ---
    # ==============================================================================
    # DECODE POOL: Internal ClusterIP Service exposing SGLang Decode Endpoint
    # ==============================================================================
    apiVersion: v1
    kind: Service
    metadata:
      name: sglang-decode-service
      labels:
        app: sglang-decode
    spec:
      type: ClusterIP
      selector:
        app: sglang-decode
        role: leader
      ports:
        - name: http
          protocol: TCP
          port: 8000
          targetPort: 8000
        - name: bootstrap
          protocol: TCP
          port: 8998
          targetPort: 8998
    ---
    # ==============================================================================
    # DECODE POOL: LeaderWorkerSet (TPU v7x, 1 slice = 2 nodes, 8 chips / 16 TensorCores)
    # ==============================================================================
    apiVersion: leaderworkerset.x-k8s.io/v1
    kind: LeaderWorkerSet
    metadata:
      name: sglang-tpu-decode
      annotations:
        leaderworkerset.sigs.k8s.io/exclusive-topology: "cloud.google.com/gke-nodepool"
      labels:
        app: sglang-decode
    spec:
      replicas: 1
      rolloutStrategy:
        type: RollingUpdate
      leaderWorkerTemplate:
        size: 2
        restartPolicy: RecreateGroupOnPodRestart
        leaderTemplate:
          metadata:
            labels:
              app: sglang-decode
              role: leader
          spec: &decode-pod-spec
            securityContext:
              fsGroup: 65534
            nodeSelector:
              cloud.google.com/gke-tpu-accelerator: tpu7x
              cloud.google.com/gke-tpu-topology: 2x2x2
            tolerations:
              - key: "google.com/tpu"
                operator: "Exists"
                effect: "NoSchedule"
            containers:
              - name: sglang
                image: $SGLANG_IMAGE
                command:
                  - /bin/bash
                  - -c
                  - |
                    set -ex
                    ulimit -c 0
                    LEADER_POD="${LWS_LEADER_ADDRESS%%.*}"
                    DNS_SUFFIX="${LWS_LEADER_ADDRESS#*.}"
                    HOSTS="${LWS_LEADER_ADDRESS}"
                    for ((i = 1; i < LWS_GROUP_SIZE; i++)); do
                      HOSTS="${HOSTS},${LEADER_POD}-${i}.${DNS_SUFFIX}"
                    done
                    export TPU_WORKER_HOSTNAMES="${HOSTS}"
                    export TPU_WORKER_ID="${LWS_WORKER_INDEX}"
                    export TPU_TOPOLOGY="${TPU_TOPOLOGY:-2x2x2}"
                    echo "=== [decode rank ${LWS_WORKER_INDEX}/${LWS_GROUP_SIZE}] hosts=${TPU_WORKER_HOSTNAMES} ==="
                    exec python3 -m sglang.launch_server \
                      --device=tpu \
                      --host=0.0.0.0 \
                      --port=8000 \
                      --model-path=Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 \
                      --tensor-parallel-size=16 \
                      --ep-size=16 \
                      --nnodes="${LWS_GROUP_SIZE}" \
                      --node-rank="${LWS_WORKER_INDEX}" \
                      --dist-init-addr="${LWS_LEADER_ADDRESS}:20000" \
                      --attention-backend=pallas_tpu_rpa_batched \
                      --mem-fraction-static=0.8 \
                      --max-running-requests=256 \
                      --chunked-prefill-size=8192 \
                      --max-total-tokens=786432 \
                      --context-length=9216 \
                      --enable-torch-compile \
                      --torch-compile-max-bs=256 \
                      --tpu-compile-tracer=direct \
                      --sampling-defaults=openai \
                      --enable-fused-qk-norm-rope \
                      --disaggregation-mode=decode \
                      --disaggregation-transfer-backend=tpu \
                      --download-dir=/data
                env:
                  - name: HF_TOKEN
                    valueFrom:
                      secretKeyRef:
                        name: hf-secret
                        key: hf_api_token
                        optional: true
                resources:
                  requests:
                    cpu: "16"
                    memory: 128Gi
                    google.com/tpu: "4"
                  limits:
                    memory: 768Gi
                    google.com/tpu: "4"
                ports:
                  - containerPort: 8000
                    name: http
                  - containerPort: 8998
                    name: bootstrap
                startupProbe:
                  httpGet:
                    path: /health
                    port: 8000
                  initialDelaySeconds: 60
                  periodSeconds: 15
                  timeoutSeconds: 10
                  failureThreshold: 236
                readinessProbe:
                  httpGet:
                    path: /health
                    port: 8000
                  periodSeconds: 10
                  timeoutSeconds: 5
                  failureThreshold: 3
                volumeMounts:
                  - name: model-cache
                    mountPath: /data
                  - name: dshm
                    mountPath: /dev/shm
            volumes:
              - name: dshm
                emptyDir:
                  medium: Memory
                  sizeLimit: 32Gi
              - name: model-cache
                ephemeral:
                  volumeClaimTemplate:
                    spec:
                      accessModes: ["ReadWriteOnce"]
                      storageClassName: hyperdisk-balanced-tpu
                      resources:
                        requests:
                          storage: 700Gi
        workerTemplate:
          metadata:
            labels:
              app: sglang-decode
              role: worker
          spec: *decode-pod-spec
    ---
    # ==============================================================================
    # SGLANG DISAGGREGATION ROUTER / PROXY DEPLOYMENT
    # ==============================================================================
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: sglang-router
      labels:
        app: sglang-router
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: sglang-router
      template:
        metadata:
          labels:
            app: sglang-router
        spec:
          containers:
            - name: router
              image: $SGLANG_IMAGE
              command:
                - python3
                - -m
                - sglang_router.launch_router
              args:
                - --pd-disaggregation
                - --prefill
                - http://sglang-prefill-service:8000
                - "8998"
                - --decode
                - http://sglang-decode-service:8000
                - --host
                - 0.0.0.0
                - --port
                - "8000"
                - --model-path
                - Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8
                - --request-timeout-secs
                - "3600"
                - --worker-startup-timeout-secs
                - "5400"
              env:
                - name: HF_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: hf-secret
                      key: hf_api_token
                      optional: true
              ports:
                - containerPort: 8000
                  name: http
              readinessProbe:
                httpGet:
                  path: /health_generate
                  port: 8000
                initialDelaySeconds: 15
                periodSeconds: 30
                timeoutSeconds: 10
                failureThreshold: 3
              resources:
                requests:
                  cpu: "1"
                  memory: 4Gi
                limits:
                  cpu: "4"
                  memory: 12Gi
    ---
    # ==============================================================================
    # SGLANG EXTERNAL SERVICE: Public / Client Endpoint for OpenAI-compatible Serving
    # ==============================================================================
    apiVersion: v1
    kind: Service
    metadata:
      name: sglang-service
      labels:
        app: sglang-router
    spec:
      type: ClusterIP
      selector:
        app: sglang-router
      ports:
        - name: http
          protocol: TCP
          port: 8000
          targetPort: 8000
  2. Terapkan manifes workload ke cluster Anda:

    envsubst '$SGLANG_IMAGE' < qwen3-coder-480b-multihost-disagg-v7x.yaml | kubectl apply -f -
  3. Pastikan Pod router, prefill, dan decode telah dibuat:

    kubectl get pods

    Jika semua Pod sudah siap, output-nya akan mirip dengan berikut ini:

    NAME                             READY   STATUS    RESTARTS   AGE
    sglang-router-6d87cb8c7d-gv4wr   1/1     Running   0          39m
    sglang-tpu-decode-0              1/1     Running   0          40m
    sglang-tpu-decode-0-1            1/1     Running   0          40m
    sglang-tpu-prefill-0             1/1     Running   0          40m
    sglang-tpu-prefill-0-1           1/1     Running   0          40m
    
  4. Pantau log Pod leader prefill dan decode hingga startup selesai:

    kubectl logs -f -l role=leader --prefix=true &
    LOG_PID=$!
    kubectl wait --for=condition=Ready --timeout=3600s pod -l role=leader
    kill "${LOG_PID}" 2>/dev/null || true

    Download model awal, pemuatan bobot, dan kompilasi memerlukan waktu sekitar 15 hingga 30 menit pada operasi pertama.

    Jika Anda menggunakan kapasitas preemptible seperti kapasitas Spot atau VM Flex-start, mungkin perlu waktu lebih lama setelah pembuatan node pool untuk membuat node slice TPU, dan node tersebut mungkin di-preempt selama workload berjalan. Untuk mengetahui informasi selengkapnya, lihat Instance preemptible VM. Untuk memeriksa apakah node slice TPU Anda telah dibuat, lihat Memeriksa status dan kondisi node.

    Tunggu hingga log Pod pemimpin prefill dan dekode menghasilkan pesan berikut dan kubectl get pods menampilkan 1/1 di kolom READY untuk semua Pod:

    The server is fired up and ready to roll!
    
  5. Buka sesi terminal kedua dan teruskan port lokal 8000 ke sglang-service:

    kubectl port-forward service/sglang-service 8000:8000

    Biarkan penerusan port berjalan di sesi terminal ini.

Mengirim permintaan inferensi

Kembali ke sesi terminal pertama Anda dan kirim permintaan penyelesaian chat ke endpoint /v1/chat/completions yang diekspos oleh router SGLang:

curl -s -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8",
    "messages": [
      {"role": "user", "content": "Explain quicksort in two sentences."}
    ],
    "max_tokens": 60,
    "temperature": 0.0
  }'

Menjalankan workload benchmark

Untuk melakukan benchmark pada model Qwen3-Coder yang di-deploy menggunakan workload input 1.024 token dan output 1.024 token (1k/1k) dari Pod sglang-router, jalankan perintah berikut:

kubectl exec deployment/sglang-router -- python3 -m sglang.bench_serving \
  --backend sglang \
  --base-url http://localhost:8000 \
  --model Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 \
  --dataset-name random \
  --random-input-len 1024 \
  --random-output-len 1024 \
  --random-range-ratio 1.0 \
  --num-prompts 256 \
  --max-concurrency 64

Pembersihan

Agar tidak menimbulkan biaya berkelanjutan tambahan pada akun Google Cloud Anda untuk resource yang digunakan dalam tutorial ini, hapus resource berikut:

  1. Hapus workload prefill, dekode, dan router SGLang:

    kubectl delete -f qwen3-coder-480b-multihost-disagg-v7x.yaml --ignore-not-found
  2. Hapus node pool TPU7x, kebijakan penempatan workload, dan cluster GKE:

    gcloud container node-pools delete tpu7x-pool-0 \
        --cluster=${CLUSTER_NAME} \
        --location=${REGION} \
        --project=${PROJECT_ID} \
        --quiet
    
    gcloud container node-pools delete tpu7x-pool-1 \
        --cluster=${CLUSTER_NAME} \
        --location=${REGION} \
        --project=${PROJECT_ID} \
        --quiet
    
    gcloud container clusters delete ${CLUSTER_NAME} \
        --location=${REGION} \
        --project=${PROJECT_ID} \
        --quiet
    
    gcloud compute resource-policies delete tpu7x-2x2x2-workload-policy \
        --region=${REGION} \
        --project=${PROJECT_ID} \
        --quiet

Langkah berikutnya