在 GKE 上使用多主機 SGLang TorchTPU,透過 TPU7x 加速器提供 Qwen3-Coder-480B

本教學課程說明如何在 Google Kubernetes Engine (GKE) 部署及提供 Qwen3-Coder-480B (Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8),這是一種專家混合編碼模型。部署作業會使用多主機 TPU7x (Ironwood) 加速器和 SGLang 服務架構,並搭配 TorchTPU (torchtpu) 和預填解碼 (PD) 分解。

在本教學課程說明的部署設定中,您會佈建兩個多主機 tpu7x-standard-4t 節點集區,每個集區都以 2x2x2 拓撲排列 (每個節點集區有兩個節點,共八個 TPU7x 晶片,總計 16 個 TPU7x 晶片)。您將使用 Kubernetes LeaderWorkerSet API 執行下列操作:

  • 在不同的 TPU7x 切片上執行分散式多主機預填和解碼伺服器,並啟用張量平行處理和專家平行處理。
  • 透過 SGLang 解構路由器轉送與 OpenAI 相容的要求。

目標

  • 設定 GKE 叢集和多主機 TPU7x 節點集區。
  • 設定憑證並安裝 LeaderWorkerSet 控制器。
  • 部署多主機 SGLang 預先填入、解碼和路由器工作負載。
  • 向 SGLang 路由器服務傳送推論要求。
  • 執行推論基準測試工作負載。
  • 清除所用資源。

費用

在本文件中,您會使用下列Google Cloud計費元件:

您可以使用 [Pricing Calculator](https://cloud.google.com/products/calculator),根據預測用量估算費用。

完成本文所述工作後,您可以刪除建立的資源,避免繼續計費,詳情請參閱「清除所用資源」一節。

事前準備

  • 安裝並初始化 Google Cloud CLI,然後安裝 kubectl。如果使用 Cloud Shell,這兩項工具都已預先安裝。

  • 您需要 Hugging Face 存取權杖,才能使用本教學課程。您可以前往 Hugging Face 註冊免費帳戶。擁有帳戶後,請產生存取權杖:

    1. 在「Welcome to Hugging Face」頁面,點選帳戶顯示圖片,然後選取「存取權杖」。
    2. 在「存取權杖」頁面,按一下「建立新權杖」。
    3. 選取「Read」權杖類型,並輸入權杖名稱。
    4. 系統會顯示存取權杖。將權杖儲存在安全的地方。

  • 在 Google Cloud 專案中啟用 Kubernetes Engine API、Compute Engine API、Cloud TPU API 和 Artifact Registry API:

    啟用 API

  • 確認專案在 TPU 容量所在區域有足夠的磁碟配額和外部 IP 位址配額:

    1. 在 Google Cloud 控制台,前往「Quotas & System Limits」(配額與系統限制) 頁面。

      前往「Quotas & System Limits」(配額與系統限制) 頁面

    2. 確認您至少有 2,800 GiB 的可用 Google Cloud Hyperdisk Balanced 配額,可供預先填入和解碼 Pod 用於模型快取的四個 700 GiB 臨時磁碟區使用。

    3. 依「使用中的區域性外部 IPv4 位址」篩選,確認目標區域有足夠的 IP 位址配額。

如要取得完成本教學課程所需的權限,請要求管理員在專案中授予您下列 IAM 角色:

如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和組織的存取權」。

您或許也能透過自訂角色或其他預先定義的角色,取得必要權限。

確認您的 Compute Engine 預設服務帳戶在專案中具備下列 Identity and Access Management (IAM) 角色:

設定環境變數

匯出本教學課程中使用的環境變數:

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"

更改下列內容:

  • YOUR_CLUSTER_NAME:GKE 叢集名稱。
  • YOUR_PROJECT_ID: Google Cloud專案的 ID。
  • YOUR_REGION:要部署叢集的區域,例如 us-central1。
  • YOUR_ZONE:要部署 TPU7x 節點集區的可用區,例如 us-central1-c。
  • YOUR_RESERVATION_NAME:如果使用預留項目,請提供 TPU7x 預留項目的名稱。
  • YOUR_HF_TOKEN:您的 Hugging Face 存取權杖。
  • YOUR_SGLANG_TPU_IMAGE:如果使用自訂 SGLang TorchTPU 容器映像檔,請提供映像檔的 URI;否則,請使用預設 SGLang TorchTPU 映像檔。

建立 GKE 叢集

如要以標準模式建立 GKE 叢集,並連接兩個多主機 TPU7x 節點集區,用於預先填入和解碼拆解,請完成下列步驟:

  1. 建立區域性 GKE 叢集,並在預設節點集區使用 e2-standard-4 節點,為 LeaderWorkerSet 控制器和 SGLang 分解路由器提供充足的 CPU 和記憶體資源:

    gcloud container clusters create $CLUSTER_NAME \
      --project=$PROJECT_ID \
      --location=$REGION \
      --machine-type=e2-standard-4
  2. 建立高處理量工作負載配置政策,並採用 2x2x2 加速器拓撲,這是多主機 TPU7x 節點集區的必要條件:

    gcloud compute resource-policies create workload-policy tpu7x-2x2x2-workload-policy \
        --type=HIGH_THROUGHPUT \
        --accelerator-topology=2x2x2 \
        --project=${PROJECT_ID} \
        --region=${REGION}
  3. 建立兩個多主機 TPU7x 節點集區 (tpu7x-pool-0 和 tpu7x-pool-1),每個集區包含兩個 tpu7x-standard-4t 節點 (每個節點集區有八個 TPU7x 晶片,採用 2x2x2 拓撲,總共 16 個 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

    除了使用特定預留項目,您也可以傳遞下列旗標,透過 Spot 容量或彈性啟動型 VM 建立節點集區:

    • Spot VM:將 --reservation-affinity=specific 和 --reservation=${RESERVATION} 替換為 --spot,即可使用 Spot 容量。建立節點集區後,使用 Spot 容量的執行個體可能需要一段時間才能建立,且在工作負載執行期間可能會遭到先占。
    • 彈性啟動型 VM:將 --num-nodes=2、--reservation-affinity=specific 和 --reservation=${RESERVATION} 替換為 --flex-start、--enable-autoscaling、--num-nodes=0、--min-nodes=0 和 --max-nodes=2,即可使用彈性啟動消耗選項佈建節點集區。詳情請參閱「使用 TPU 和彈性啟動型 VM 執行小型批次工作負載」。
  4. 設定 kubectl 憑證,以便與叢集通訊:

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

設定憑證並安裝 LeaderWorkerSet

如要準備叢集,以便部署多主機 SGLang,請完成下列步驟:

  1. 建立名為 hf-secret 的 Kubernetes Secret,用於儲存 Hugging Face 存取權杖:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN}
  2. 在叢集上安裝 LeaderWorkerSet CustomResourceDefinition 和控制器:

    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

部署多主機 SGLang 伺服器

工作負載資訊清單 (qwen3-coder-480b-multihost-disagg-v7x.yaml) 定義下列資源,用於協調兩個 2x2x2 TPU7x 節點集區中,分散式預先填入和解碼服務:

  • StorageClass (hyperdisk-balanced-tpu):佈建由 Google Cloud Hyperdisk Balanced 儲存空間支援的暫時性 700 GiB ReadWriteOnce 磁碟區。四個預先填入和解碼 Pod 各自會在 /data 掛接自己的磁碟區,以便在本機儲存下載的 Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 檢查點。
  • 預先填入集區 (sglang-prefill-service 和 sglang-tpu-prefill):執行 size: 2 的 LeaderWorkerSet (一個領導者 Pod 和一個工作站 Pod),專門固定至一個 2x2x2 TPU7x 節點集區 (八個 TPU 晶片,提供 16 個 TensorCore)。LeaderWorkerSet 會使用設為 "cloud.google.com/gke-nodepool" 的 leaderworkerset.sigs.k8s.io/exclusive-topology 註解。Pod 會使用 --disaggregation-mode=prefill、--tensor-parallel-size=16 和 --ep-size=16 旗標啟動 sglang.launch_server,並公開 HTTP 通訊埠 8000 和啟動程序通訊埠 8998。
  • 解碼集區 (sglang-decode-service 和 sglang-tpu-decode):執行 size: 2 的 LeaderWorkerSet,並固定至第二個 2x2x2 TPU7x 節點集區 (八個 TPU 晶片,提供 16 個 TensorCore)。LeaderWorkerSet 會使用 --disaggregation-mode=decode、--tensor-parallel-size=16 和 --ep-size=16 旗標啟動 sglang.launch_server。
  • 分解路由器 (sglang-router 和 sglang-service):執行 CPU Deployment,該 Deployment 會使用 --pd-disaggregation 旗標執行 sglang_router.launch_router 模組,並在通訊埠 8000 公開 ClusterIP Service。路由器會導向提示,並協調 sglang-prefill-service 和 sglang-decode-service 服務之間的 KV 快取移轉。

如要部署及監控多主機 SGLang 伺服器,請完成下列步驟:

  1. 下載工作負載資訊清單:

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

    或者,您也可以建立名為 qwen3-coder-480b-multihost-disagg-v7x.yaml 的檔案,並在當中加入下列內容:

    # ==============================================================================
    # 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. 將工作負載資訊清單套用至叢集:

    envsubst '$SGLANG_IMAGE' < qwen3-coder-480b-multihost-disagg-v7x.yaml | kubectl apply -f -
  3. 確認路由器、預先填入和解碼 Pod 已建立:

    kubectl get pods

    所有 Pod 都準備就緒後,輸出內容會類似如下:

    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. 監控預先填入和解碼前導 Pod 記錄,直到啟動完成:

    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

    首次執行時,初始模型下載、權重載入和編譯作業約需 15 到 30 分鐘。

    如果您使用 Spot 容量或彈性啟動型 VM 等先占容量,建立節點集區後,可能需要較長時間才能建立 TPU 切片節點,而且這些節點可能會在工作負載執行期間遭到先占。詳情請參閱「先占 VM 執行個體」。如要確認 TPU 切片節點是否已建立,請參閱「檢查節點的狀態和條件」。

    等待預先填入和解碼 Leader Pod 記錄輸出下列訊息,且 kubectl get pods 在所有 Pod 的 READY 欄中顯示 1/1:

    The server is fired up and ready to roll!
    
  5. 開啟第二個終端機工作階段,並將本機通訊埠 8000 轉送至 sglang-service:

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

    在這個終端機工作階段中,讓通訊埠轉送功能保持運作。

傳送推論要求

返回第一個終端機工作階段,並將對話完成要求傳送至 SGLang 路由器公開的 /v1/chat/completions 端點:

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
  }'

執行基準工作負載

如要使用 sglang-router Pod 的 1,024 個權杖輸入和 1,024 個權杖輸出 (1k/1k) 工作負載,對部署的 Qwen3-Coder 模型進行基準測試,請執行下列指令:

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

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程所用資源的額外費用,請刪除下列資源:

  1. 刪除 SGLang 預先填入、解碼和路由器工作負載:

    kubectl delete -f qwen3-coder-480b-multihost-disagg-v7x.yaml --ignore-not-found
  2. 刪除 TPU7x 節點集區、工作負載配置政策和 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

後續步驟