在 GKE 上使用 TPU7x 加速器上的多主机 SGLang TorchTPU 部署 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的以下收费组件:

如需根据您的预计使用量来估算费用,请使用 [价格计算器](https://cloud.google.com/products/calculator)。

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。

准备工作

  • 安装并初始化 Google Cloud CLI,然后安装 kubectl。如果您使用 Cloud Shell,则这两个工具均已预安装。

  • 您需要拥有 Hugging Face 访问令牌才能使用本教程。您可以在 Hugging Face 注册免费账号。拥有账号后,生成访问令牌:

    1. 在欢迎使用 Hugging Face 页面上,点击您的账号头像,然后选择访问令牌。
    2. 在访问令牌页面上,点击创建新令牌。
    3. 选择读取令牌类型,然后输入令牌名称。
    4. 系统会显示您的访问令牌。将令牌保存在安全的位置。

  • 在 Google Cloud 项目中启用 Kubernetes Engine API、Compute Engine API、Cloud TPU API 和 Artifact Registry API:

    启用 API

  • 确保您的项目在 TPU 容量所在的区域中有足够的磁盘配额和外部 IP 地址配额:

    1. 在 Google Cloud 控制台中,前往配额和系统限制页面。

      进入“配额和系统限制”

    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 集群

如需在 Standard 模式下创建 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 节点(每个节点池在 2x2x2 拓扑中包含 8 个 TPU7x 芯片,共 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 容量或灵活启动型虚拟机创建节点池:

    • Spot 虚拟机:将 --reservation-affinity=specific 和 --reservation=${RESERVATION} 替换为 --spot,以使用 Spot 容量。使用 Spot 容量的实例在节点池创建后可能需要一些时间才能创建,并且可能会在工作负载运行期间被抢占。
    • 灵活启动型虚拟机:将 --num-nodes=2、--reservation-affinity=specific 和 --reservation=${RESERVATION} 替换为 --flex-start、--enable-autoscaling、--num-nodes=0、--min-nodes=0 和 --max-nodes=2,以使用灵活启动使用选项预配节点池。如需了解详情,请参阅使用 TPU 和灵活启动虚拟机运行小型批量工作负载。
  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(一个 Leader Pod 和一个 Worker Pod),该 LeaderWorkerSet 专门固定到一个 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):运行固定到第二个 2x2x2 TPU7x 节点池(8 个 TPU 芯片,提供 16 个 TensorCore)的 size: 2 LeaderWorkerSet。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 容量或灵活启动型虚拟机),则在创建节点池后,TPU 切片节点可能需要更长时间才能创建,并且可能会在工作负载运行期间被抢占。如需了解详情,请参阅抢占式虚拟机实例。 如需检查 TPU 切片节点是否已创建,请参阅检查节点的状态和条件。

    等待预填充和解码主 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 个 token 输入和 1,024 个 token 输出 (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

后续步骤