Gestisci Qwen3-Coder-480B su GKE con SGLang TorchTPU multi-host su acceleratori TPU7x

Questo tutorial mostra come eseguire il deployment e l'addestramento di Qwen3-Coder-480B (Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8), un modello di programmazione Mixture of Experts, su Google Kubernetes Engine (GKE). Il deployment utilizza acceleratori TPU7x (Ironwood) multi-host e il framework di distribuzione SGLang con TorchTPU (torchtpu) e disaggregazione prefill-decode (DP).

Nella configurazione del deployment descritta in questo tutorial, esegui il provisioning di due node pool tpu7x-standard-4t multi-host, ciascuno disposto in una topologia 2x2x2 (otto chip TPU7x su due nodi per pool di nodi, per un totale di 16 chip TPU7x). Utilizza l'API LeaderWorkerSet di Kubernetes per:

  • Esegui server di precompilazione e decodifica multi-host disaggregati su sezioni TPU7x separate con parallelismo dei tensori e parallelismo degli esperti abilitati.
  • Instrada le richieste compatibili con OpenAI tramite un router di disaggregazione SGLang.

Obiettivi

  • Configura il cluster GKE e i pool di nodi TPU7x multi-host.
  • Configura le credenziali e installa il controller LeaderWorkerSet.
  • Esegui il deployment dei workload di pre-compilazione, decodifica e router SGLang multi-host.
  • Invia una richiesta di inferenza al servizio router SGLang.
  • Esegui un workload di benchmark di distribuzione.
  • Eseguire la pulizia.

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili diGoogle Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il [Calcolatore prezzi]((https://cloud.google.com/products/calculator).

Al termine delle attività descritte in questo documento, puoi evitare l'addebito di ulteriori costi eliminando le risorse che hai creato. Per saperne di più, consulta Esegui la pulizia.

Prima di iniziare

  • Per utilizzare questo tutorial, devi disporre di un token di accesso a Hugging Face. Puoi creare un account senza costi su Hugging Face. Dopo aver creato un account, genera un token di accesso:

    1. Nella pagina Welcome to Hugging Face, fai clic sull'avatar del tuo account e seleziona Token di accesso.
    2. Nella pagina Token di accesso, fai clic su Crea nuovo token.
    3. Seleziona il tipo di token Lettura e inserisci un nome per il token.
    4. Viene visualizzato il token di accesso. Salva il token in un luogo sicuro.

  • Abilita l'API Kubernetes Engine, l'API Compute Engine, l'API Cloud TPU e l'API Artifact Registry nel tuo progetto Google Cloud :

    Abilita le API

  • Assicurati che il tuo progetto disponga di una quota disco e di una quota di indirizzi IP esterni sufficienti nella regione in cui si trova la capacità della TPU:

    1. Nella console Google Cloud , vai alla pagina Quote e limiti di sistema.

      Vai a Quote e Limiti di sistema

    2. Verifica di avere almeno 2800 GiB di quota Google Cloud Hyperdisk Balanced disponibile tra i quattro volumi temporanei da 700 GiB utilizzati per la memorizzazione nella cache del modello dai pod di pre-riempimento e decodifica.

    3. Filtra in base a Indirizzi IPv4 esterni regionali in uso e verifica di avere una quota di indirizzi IP sufficiente nella regione di destinazione.

Per ottenere le autorizzazioni necessarie per completare questo tutorial, chiedi all'amministratore di concederti i seguenti ruoli IAM nel progetto:

Per saperne di più sulla concessione dei ruoli, consulta Gestisci l'accesso a progetti, cartelle e organizzazioni.

Potresti anche riuscire a ottenere le autorizzazioni richieste tramite i ruoli personalizzati o altri ruoli predefiniti.

Assicurati che il tuo service account predefinito di Compute Engine disponga dei seguenti ruoli IAM (Identity and Access Management) nel tuo progetto:

Configura le variabili di ambiente

Esporta le variabili di ambiente che utilizzi in questo tutorial:

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"

Sostituisci quanto segue:

  • YOUR_CLUSTER_NAME: il nome del tuo cluster GKE.
  • YOUR_PROJECT_ID: l'ID del tuo progetto Google Cloud.
  • YOUR_REGION: la regione in cui vuoi eseguire il deployment del cluster, ad esempio us-central1.
  • YOUR_ZONE: la zona in cui vuoi eseguire il deployment dei tuoi pool di nodi TPU7x, ad esempio us-central1-c.
  • YOUR_RESERVATION_NAME: se utilizzi una prenotazione, il nome della prenotazione TPU7x.
  • YOUR_HF_TOKEN: il tuo token di accesso a Hugging Face.
  • YOUR_SGLANG_TPU_IMAGE: se utilizzi un'immagine container TorchTPU SGLang personalizzata, l'URI dell'immagine; altrimenti, l'immagine TorchTPU SGLang predefinita.

Crea il cluster GKE

Per creare un cluster GKE in modalità Standard e collegare due pool di nodi TPU7x multi-host per la disaggregazione di precompilazione e decodifica, completa i seguenti passaggi:

  1. Crea un cluster GKE a livello di regione che utilizza nodi e2-standard-4 per il pool di nodi predefinito, che fornisce risorse di CPU e memoria sufficienti per il controller LeaderWorkerSet e il router di disaggregazione SGLang:

    gcloud container clusters create $CLUSTER_NAME \
      --project=$PROJECT_ID \
      --location=$REGION \
      --machine-type=e2-standard-4
  2. Crea una policy di posizionamento dei workload con throughput elevato con una topologia dell'acceleratore 2x2x2, obbligatoria per i 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. Crea due pool di nodi TPU7x multi-host (tpu7x-pool-0 e tpu7x-pool-1), ciascuno contenente due nodi tpu7x-standard-4t (otto chip TPU7x in una topologia 2x2x2 per pool di nodi, per un totale di 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

    Anziché utilizzare una prenotazione specifica, puoi anche creare i node pool utilizzando la capacità Spot o le Flex-start VM passando i seguenti flag:

    • VM spot: sostituisci --reservation-affinity=specific e --reservation=${RESERVATION} con --spot per utilizzare la capacità spot. La creazione delle istanze che utilizzano la capacità Spot potrebbe richiedere del tempo dopo la creazione del pool di nodi e potrebbe essere prerilasciata durante l'esecuzione dei workload.
    • VM con avvio flessibile: sostituisci --num-nodes=2, --reservation-affinity=specific e --reservation=${RESERVATION} con --flex-start, --enable-autoscaling, --num-nodes=0, --min-nodes=0 e --max-nodes=2 per eseguire il provisioning del pool di nodi utilizzando l'opzione di consumo con avvio flessibile. Per saperne di più, consulta Esegui un workload batch di piccole dimensioni con TPU e VM Flex-start.
  4. Configura le credenziali kubectl per comunicare con il tuo cluster:

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

Configura le credenziali e installa LeaderWorkerSet

Per preparare il cluster per il deployment SGLang multi-host, completa i seguenti passaggi:

  1. Crea un secret di Kubernetes denominato hf-secret per archiviare il tuo token di accesso Hugging Face:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN}
  2. Installa CustomResourceDefinition e il controller LeaderWorkerSet sul tuo cluster:

    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

Esegui il deployment del server SGLang multi-host

Il manifest del workload (qwen3-coder-480b-multihost-disagg-v7x.yaml) definisce le seguenti risorse per coordinare la precompilazione e la decodifica disaggregate tra i due pool di nodi TPU7x 2x2x2:

  • StorageClass (hyperdisk-balanced-tpu): esegue il provisioning di volumi ReadWriteOnce temporanei da 700 GiB supportati dallo spazio di archiviazione Google Cloud Hyperdisk bilanciato. Ognuno dei quattro pod di precompilazione e decodifica monta il proprio volume in /data per archiviare localmente il checkpoint Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 scaricato.
  • Pool di precompilazione (sglang-prefill-service e sglang-tpu-prefill): esegue un LeaderWorkerSet di size: 2 (un pod leader e un pod worker) assegnato esclusivamente a un pool di nodi TPU7x 2x2x2 (otto chip TPU, che forniscono 16 TensorCore). LeaderWorkerSet utilizza l'annotazione leaderworkerset.sigs.k8s.io/exclusive-topology impostata su "cloud.google.com/gke-nodepool". I pod avviano sglang.launch_server con i flag --disaggregation-mode=prefill, --tensor-parallel-size=16 e --ep-size=16, esponendo la porta HTTP 8000 e la porta di bootstrap 8998.
  • Pool di decodifica (sglang-decode-service e sglang-tpu-decode): esegue un LeaderWorkerSet di size: 2 assegnato al secondo pool di nodi TPU7x 2x2x2 (otto chip TPU, che forniscono 16 TensorCore). LeaderWorkerSet avvia sglang.launch_server con i flag --disaggregation-mode=decode, --tensor-parallel-size=16 e --ep-size=16.
  • Router di disaggregazione (sglang-router e sglang-service): esegue un deployment della CPU che esegue il modulo sglang_router.launch_router con il flag --pd-disaggregation ed espone un servizio ClusterIP sulla porta 8000. Il router indirizza i prompt e coordina il trasferimento della cache KV tra i servizi sglang-prefill-service e sglang-decode-service.

Per eseguire il deployment e monitorare i server SGLang multi-host, completa i seguenti passaggi:

  1. Scarica il manifest del workload:

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

    In alternativa, crea un file denominato qwen3-coder-480b-multihost-disagg-v7x.yaml con i seguenti contenuti:

    # ==============================================================================
    # 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. Applica il manifest del workload al cluster:

    envsubst '$SGLANG_IMAGE' < qwen3-coder-480b-multihost-disagg-v7x.yaml | kubectl apply -f -
  3. Verifica che i pod di router, precompilazione e decodifica siano stati creati:

    kubectl get pods

    Quando tutti i pod sono pronti, l'output è simile al seguente:

    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. Monitora i log dei pod leader di precompilazione e decodifica fino al completamento dell'avvio:

    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

    Il download iniziale del modello, il caricamento dei pesi e la compilazione richiedono circa 15-30 minuti alla prima esecuzione.

    Se utilizzi una capacità preemptible, ad esempio una capacità spot o VM con avvio flessibile, la creazione dei nodi di sezioni TPU potrebbe richiedere più tempo dopo la creazione del pool di nodi e questi nodi potrebbero essere prerilasciati durante l'esecuzione dei workload. Per saperne di più, consulta Istanze VM preemptible. Per verificare se i nodi della sezione TPU sono stati creati, consulta Controlla lo stato e le condizioni del nodo.

    Attendi che i log dei pod leader di precompilazione e decodifica restituiscano il seguente messaggio e che kubectl get pods mostri 1/1 nella colonna READY per tutti i pod:

    The server is fired up and ready to roll!
    
  5. Apri una seconda sessione del terminale e inoltra la porta locale 8000 a sglang-service:

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

    Lascia il port forwarding in esecuzione in questa sessione del terminale.

Invia richieste di inferenza

Torna alla prima sessione del terminale e invia una richiesta di completamento della chat all'endpoint /v1/chat/completions esposto dal 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
  }'

Esegui un workload di benchmark

Per eseguire il benchmark del modello Qwen3-Coder di cui è stato eseguito il deployment utilizzando un workload di input da 1024 token e di output da 1024 token (1k/1k) dal pod sglang-router, esegui il seguente comando:

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

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi aggiuntivi relativi alle risorse utilizzate in questo tutorial, elimina le seguenti risorse:

  1. Elimina i workload di pre-compilazione, decodifica e router SGLang:

    kubectl delete -f qwen3-coder-480b-multihost-disagg-v7x.yaml --ignore-not-found
  2. Elimina i node pool TPU7x, la policy di posizionamento dei workload e il 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

Passaggi successivi