Diffuser Qwen3-Coder-480B sur GKE avec SGLang TorchTPU multi-hôte sur des accélérateurs TPU7x

Ce tutoriel vous explique comment déployer et diffuser Qwen3-Coder-480B (Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8), un modèle de codage Mixture-of-Experts, sur Google Kubernetes Engine (GKE). Le déploiement utilise des accélérateurs TPU7x (Ironwood) multi-hôtes et le framework de diffusion SGLang avec TorchTPU (torchtpu) et la désagrégation préremplissage-décodage (PD).

Dans la configuration de déploiement décrite dans ce tutoriel, vous provisionnez deux pools de nœuds tpu7x-standard-4t multi-hôtes, chacun organisé selon une topologie 2x2x2 (huit puces TPU7x sur deux nœuds par pool de nœuds, pour un total de 16 puces TPU7x). Vous utilisez l'API Kubernetes LeaderWorkerSet pour effectuer les opérations suivantes :

  • Exécutez des serveurs de préremplissage et de décodage multi-hôtes désagrégés sur des tranches TPU7x distinctes avec le parallélisme de tenseur et le parallélisme d'expert activés.
  • Acheminez les requêtes compatibles avec OpenAI via un routeur de désagrégation SGLang.

Objectifs

  • Configurez votre cluster GKE et vos pools de nœuds TPU7x multi-hôtes.
  • Configurez les identifiants et installez le contrôleur LeaderWorkerSet.
  • Déployez les charges de travail de préremplissage, de décodage et de routeur SGLang multihôtes.
  • Envoyez une requête d'inférence au service de routeur SGLang.
  • Exécutez une charge de travail de benchmark de diffusion.
  • Effectuer un nettoyage.

Coûts

Dans ce document, vous utilisez les composants facturables suivants deGoogle Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du [simulateur de coût]((https://cloud.google.com/products/calculator).

Une fois que vous avez terminé les tâches décrites dans ce document, vous pouvez éviter de continuer à payer des frais en supprimant les ressources que vous avez créées. Pour en savoir plus, consultez la section Effectuer un nettoyage.

Avant de commencer

  • Installez et initialisez la Google Cloud CLI, puis installez kubectl. Si vous utilisez Cloud Shell, les deux outils sont préinstallés.

  • Vous avez besoin d'un jeton d'accès Hugging Face pour suivre ce tutoriel. Vous pouvez créer un compte sans frais sur Hugging Face. Une fois que vous disposez d'un compte, générez un jeton d'accès :

    1. Sur la page Welcome to Hugging Face (Bienvenue sur Hugging Face), cliquez sur l'avatar de votre compte, puis sélectionnez Access tokens (Jetons d'accès).
    2. Sur la page Access tokens (Jetons d'accès), cliquez sur Create new token (Créer un jeton).
    3. Sélectionnez le type de jeton Read (Lecture) et saisissez un nom pour votre jeton.
    4. Votre jeton d'accès s'affiche. Enregistrez le jeton dans un endroit sûr.

  • Activez l'API Kubernetes Engine, l'API Compute Engine, l'API Cloud TPU et l'API Artifact Registry dans votre projet Google Cloud  :

    Activer les API

  • Assurez-vous que votre projet dispose d'un quota de disque et d'un quota d'adresses IP externes suffisants dans la région où se trouve votre capacité TPU :

    1. Dans la console Google Cloud , accédez à la page Quotas et limites du système.

      Accéder à la page Quotas et limites du système

    2. Vérifiez que vous disposez d'au moins 2 800 Gio de quota Google Cloud Hyperdisk Balanced disponible sur les quatre volumes éphémères de 700 Gio utilisés pour la mise en cache du modèle par les pods de préremplissage et de décodage.

    3. Filtrez par Adresses IPv4 externes régionales en cours d'utilisation et vérifiez que vous disposez d'un quota d'adresses IP suffisant dans votre région cible.

Pour obtenir les autorisations nécessaires pour suivre ce tutoriel, demandez à votre administrateur de vous accorder les rôles IAM suivants sur votre projet :

Pour en savoir plus sur l'attribution de rôles, consultez la page Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises avec des rôles personnalisés ou d'autres rôles prédéfinis.

Assurez-vous que votre compte de service Compute Engine par défaut dispose des rôles IAM (Identity and Access Management) suivants sur votre projet :

Configurer vos variables d'environnement

Exportez les variables d'environnement que vous utiliserez tout au long de ce tutoriel :

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"

Remplacez les éléments suivants :

  • YOUR_CLUSTER_NAME : nom de votre cluster GKE.
  • YOUR_PROJECT_ID : ID de votre projet Google Cloud.
  • YOUR_REGION : région dans laquelle vous souhaitez déployer votre cluster, par exemple us-central1.
  • YOUR_ZONE : zone dans laquelle vous souhaitez déployer vos pools de nœuds TPU7x, par exemple us-central1-c.
  • YOUR_RESERVATION_NAME : si vous utilisez une réservation, nom de votre réservation TPU7x.
  • YOUR_HF_TOKEN : votre jeton d'accès Hugging Face.
  • YOUR_SGLANG_TPU_IMAGE : si vous utilisez une image de conteneur SGLang TorchTPU personnalisée, l'URI de votre image ; sinon, l'image SGLang TorchTPU par défaut.

Créer le cluster GKE

Pour créer un cluster GKE en mode Standard et associer deux pools de nœuds TPU7x multi-hôtes pour la désagrégation du préremplissage et du décodage, procédez comme suit :

  1. Créez un cluster GKE régional qui utilise des nœuds e2-standard-4 pour le pool de nœuds par défaut, qui fournit suffisamment de ressources de processeur et de mémoire pour le contrôleur LeaderWorkerSet et le routeur de désagrégation SGLang :

    gcloud container clusters create $CLUSTER_NAME \
      --project=$PROJECT_ID \
      --location=$REGION \
      --machine-type=e2-standard-4
  2. Créez une règle d'emplacement de charge de travail à haut débit avec une topologie d'accélérateur 2x2x2, qui est requise pour les pools de nœuds TPU7x multi-hôtes :

    gcloud compute resource-policies create workload-policy tpu7x-2x2x2-workload-policy \
        --type=HIGH_THROUGHPUT \
        --accelerator-topology=2x2x2 \
        --project=${PROJECT_ID} \
        --region=${REGION}
  3. Créez deux pools de nœuds TPU7x multi-hôtes (tpu7x-pool-0 et tpu7x-pool-1), chacun contenant deux nœuds tpu7x-standard-4t (huit puces TPU7x dans une topologie 2x2x2 par pool de nœuds, pour un total de 16 puces 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

    Au lieu d'utiliser une réservation spécifique, vous pouvez également créer les pools de nœuds à l'aide de la capacité Spot ou de VM Flex-start en transmettant les options suivantes :

    • VM Spot : remplacez --reservation-affinity=specific et --reservation=${RESERVATION} par --spot pour utiliser la capacité Spot. La création d'instances utilisant la capacité Spot peut prendre du temps après la création du pool de nœuds, et elles peuvent être préemptées pendant l'exécution de la charge de travail.
    • VM à démarrage flexible : remplacez --num-nodes=2, --reservation-affinity=specific et --reservation=${RESERVATION} par --flex-start, --enable-autoscaling, --num-nodes=0, --min-nodes=0 et --max-nodes=2 pour provisionner le pool de nœuds à l'aide de l'option de consommation Démarrage flexible. Pour en savoir plus, consultez Exécuter une petite charge de travail par lot avec des TPU et des VM à démarrage flexible.
  4. Configurez les identifiants kubectl pour communiquer avec votre cluster :

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

Configurer les identifiants et installer LeaderWorkerSet

Pour préparer votre cluster au déploiement SGLang multi-hôte, procédez comme suit :

  1. Créez un secret Kubernetes nommé hf-secret pour stocker votre jeton d'accès Hugging Face :

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN}
  2. Installez la CustomResourceDefinition et le contrôleur LeaderWorkerSet sur votre 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

Déployer le serveur SGLang multi-hôte

Le fichier manifeste de la charge de travail (qwen3-coder-480b-multihost-disagg-v7x.yaml) définit les ressources suivantes pour coordonner la diffusion désagrégée de préremplissage et de décodage sur les deux pools de nœuds TPU7x 2x2x2 :

  • StorageClass (hyperdisk-balanced-tpu) : provisionne des volumes ReadWriteOnce éphémères de 700 Gio, sauvegardés par un stockage Google Cloud Hyperdisk Balanced. Chacun des quatre pods de préremplissage et de décodage installe son propre volume sur /data pour stocker localement le point de contrôle Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 téléchargé.
  • Pool de préremplissage (sglang-prefill-service et sglang-tpu-prefill) : exécute un LeaderWorkerSet de size: 2 (un pod leader et un pod de nœud de calcul) épinglé exclusivement à un pool de nœuds TPU7x 2x2x2 (huit puces TPU, qui fournissent 16 TensorCores). LeaderWorkerSet utilise l'annotation leaderworkerset.sigs.k8s.io/exclusive-topology définie sur "cloud.google.com/gke-nodepool". Les pods lancent sglang.launch_server avec les options --disaggregation-mode=prefill, --tensor-parallel-size=16 et --ep-size=16, exposant le port HTTP 8000 et le port d'amorçage 8998.
  • Pool de décodage (sglang-decode-service et sglang-tpu-decode) : exécute un LeaderWorkerSet de size: 2 épinglé au deuxième pool de nœuds TPU7x 2x2x2 (huit puces TPU, qui fournissent 16 TensorCores). LeaderWorkerSet lance sglang.launch_server avec les options --disaggregation-mode=decode, --tensor-parallel-size=16 et --ep-size=16.
  • Routeur de désagrégation (sglang-router et sglang-service) : exécute un déploiement de processeur qui exécute le module sglang_router.launch_router avec l'option --pd-disaggregation et expose un service ClusterIP sur le port 8000. Le routeur dirige les requêtes et coordonne le transfert du cache KV entre les services sglang-prefill-service et sglang-decode-service.

Pour déployer et surveiller les serveurs SGLang multi-hôtes, procédez comme suit :

  1. Téléchargez le fichier manifeste de la charge de travail :

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

    Vous pouvez également créer un fichier nommé qwen3-coder-480b-multihost-disagg-v7x.yaml avec le contenu suivant :

    # ==============================================================================
    # 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. Appliquez le fichier manifeste de la charge de travail à votre cluster :

    envsubst '$SGLANG_IMAGE' < qwen3-coder-480b-multihost-disagg-v7x.yaml | kubectl apply -f -
  3. Vérifiez que les pods de routeur, de préremplissage et de décodage sont créés :

    kubectl get pods

    Lorsque tous les pods sont prêts, la sortie ressemble à ce qui suit :

    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. Surveillez les journaux du pod leader de préremplissage et de décodage jusqu'à la fin du démarrage :

    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

    Le téléchargement initial du modèle, le chargement des pondérations et la compilation prennent environ 15 à 30 minutes lors de la première exécution.

    Si vous utilisez une capacité préemptive telle que la capacité Spot ou des VM Flex-start, la création de vos nœuds de tranche TPU peut prendre plus de temps après la création du pool de nœuds, et ils peuvent être préemptés pendant l'exécution des charges de travail. Pour en savoir plus, consultez Instances de VM préemptives. Pour vérifier si vos nœuds de tranche TPU ont été créés, consultez Vérifier l'état et les conditions du nœud.

    Attendez que les journaux des pods leaders de préremplissage et de décodage affichent le message suivant et que kubectl get pods affiche 1/1 dans la colonne READY pour tous les pods :

    The server is fired up and ready to roll!
    
  5. Ouvrez une deuxième session de terminal et transférez le port local 8000 vers sglang-service :

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

    Laissez le transfert de port s'exécuter dans cette session de terminal.

Envoyer des requêtes d'inférence

Revenez à votre première session de terminal et envoyez une requête de complétion de chat au point de terminaison /v1/chat/completions exposé par le routeur 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
  }'

Exécuter une charge de travail de benchmark

Pour évaluer le modèle Qwen3-Coder déployé à l'aide d'une charge de travail d'entrée de 1 024 jetons et de sortie de 1 024 jetons (1k/1k) à partir du pod sglang-router, exécutez la commande suivante :

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

Effectuer un nettoyage

Pour éviter que des frais supplémentaires ne soient facturés sur votre compte Google Cloud pour les ressources utilisées dans ce tutoriel, supprimez les ressources suivantes :

  1. Supprimez les charges de travail de préremplissage, de décodage et de routeur SGLang :

    kubectl delete -f qwen3-coder-480b-multihost-disagg-v7x.yaml --ignore-not-found
  2. Supprimez les pools de nœuds TPU7x, la règle d'emplacement des charges de travail et le 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

Étapes suivantes