Gestisci Qwen3-30B-A3B su GKE con vLLM TorchTPU su TPU v6e

Questo tutorial mostra come eseguire il deployment e l'erogazione del modello Mixture of Experts Qwen3-30B-A3B (Qwen/Qwen3-30B-A3B-FP8) su Google Kubernetes Engine (GKE) utilizzando un acceleratore TPU v6e (Trillium) single-host e il framework di erogazione TPU vLLM con TorchTPU (torchtpu). Esegui il workload su un singolo nodo ct6e-standard-4t, che contiene quattro chip TPU v6e in una topologia 2x2, con il parallelismo dei dati e il parallelismo degli esperti abilitati.

Per eseguire un workflow di distribuzione vLLM TorchTPU single-host simile su acceleratori TPU7x (Ironwood) con il modello Qwen3.5-397B più grande, consulta Distribuisci Qwen3.5-397B su GKE con vLLM TorchTPU su acceleratori TPU7x.

Obiettivi

  • Configura il cluster GKE e il pool di nodi TPU v6e a host singolo.
  • Esegui il deployment del server TorchTPU vLLM single-host su GKE.
  • Invia una richiesta di inferenza al server vLLM TorchTPU.
  • Esegui un workload di benchmark di gestione.
  • 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.

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

Configura l'ambiente per il tutorial:

  • Abilita l'API Kubernetes Engine nel tuo progetto Google Cloud :

    Abilita l'API Kubernetes Engine

  • Abilita l'API Artifact Registry nel tuo progetto Google Cloud :

    Abilita l'API Artifact Registry

  • Installa e inizializza Google Cloud CLI e installa kubectl. Se utilizzi Cloud Shell, entrambi gli strumenti sono preinstallati.

  • Assicurati che il tuo progetto disponga di una quota di SSD e di indirizzi IP esterni sufficiente 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. Filtra per Persistent Disk SSD (GB) e verifica di avere almeno 500 GB di quota disponibile per il volume della cache del modello.

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

Ottieni un token di accesso a Hugging Face

Per utilizzare questo tutorial, devi disporre di un token di accesso a Hugging Face. Puoi registrarti per 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 Read (Lettura) e inserisci un nome per il token.
  4. Viene visualizzato il token di accesso. Salva il token in un luogo sicuro.

Ruoli obbligatori

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.

Imposta le variabili di ambiente

Configura l'autenticazione:

gcloud auth login
gcloud auth application-default login

Imposta le variabili di ambiente:

export PROJECT_ID="YOUR_PROJECT_ID"
export REGION="YOUR_REGION"
export ZONE="YOUR_ZONE"
export CLUSTER_NAME="YOUR_CLUSTER_NAME"
export MACHINE_TYPE="tpu7x-standard-4t"
export NODE_POOL_NAME="np-0"
export RESERVATION_NAME="YOUR_RESERVATION_NAME"
export HF_TOKEN="YOUR_HF_TOKEN"
export NETWORK="YOUR_NETWORK_NAME"
export SUBNETWORK="YOUR_SUBNETWORK_NAME"
export VLLM_IMAGE="YOUR_VLLM_TPU_IMAGE"

gcloud config set project "${PROJECT_ID}"
gcloud config set billing/quota_project "${PROJECT_ID}"

Sostituisci quanto segue:

  • 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 del pool di nodi TPU v6e, ad esempio us-central1-c.
  • YOUR_CLUSTER_NAME: il nome del tuo cluster GKE, ad esempio tpu-v6e-tutorial-cluster.
  • YOUR_RESERVATION_NAME: se utilizzi una prenotazione, il nome della prenotazione TPU v6e.
  • YOUR_HF_TOKEN: il tuo token di accesso a Hugging Face.
  • YOUR_NETWORK_NAME: il nome della tua reteGoogle Cloud . Se hai creato una rete personalizzata, specifica quella rete. In caso contrario, specifica default.
  • YOUR_SUBNETWORK_NAME: il nome della tua subnetGoogle Cloud . Se hai creato una subnet personalizzata, specifica quella subnet. In caso contrario, specifica default.
  • YOUR_VLLM_TPU_IMAGE: l'immagine vLLM TorchTPU da utilizzare. Per le immagini pubbliche, consulta il repository vLLM TorchTPU.

Crea il cluster GKE

Per creare un cluster GKE in modalità Standard e collegare un pool di nodi TPU v6e a host singolo, completa i seguenti passaggi:

  1. Crea un cluster GKE a livello di regione:

    gcloud container clusters create "${CLUSTER_NAME}" \
        --project="${PROJECT_ID}" \
        --location="${REGION}" \
        --node-locations="${ZONE}" \
        --network="${NETWORK}" \
        --subnetwork="${SUBNETWORK}"

    Il tipo di macchina predefinito per i cluster GKE (e2-medium) non dispone di capacità della CPU sufficiente per i controller dei workload, pertanto questo comando specifica e2-standard-4.

    L'output è simile al seguente:

    NAME                      LOCATION     MASTER_VERSION      MASTER_IP      MACHINE_TYPE   NODE_VERSION        NUM_NODES  STATUS
    tpu-v6e-tutorial-cluster  us-central1  1.35.7-gke.1027000  34.63.222.246  e2-standard-4  1.35.7-gke.1027000  3          RUNNING
    
  2. Crea un pool di nodi TPU v6e a host singolo con un nodo ct6e-standard-4t (quattro chip TPU v6e in una topologia 2x2) utilizzando la tua prenotazione:

    gcloud container node-pools create "${NODE_POOL_NAME}" \
        --cluster="${CLUSTER_NAME}" \
        --machine-type="${MACHINE_TYPE}" \
        --location="${REGION}" \
        --node-locations="${ZONE}" \
        --project="${PROJECT_ID}" \
        --num-nodes=1 \
        --reservation-affinity=specific \
        --reservation="${RESERVATION_NAME}"

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

    • VM spot: sostituisci i flag --reservation-affinity e --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 potrebbero essere prerilasciate durante l'esecuzione dei workload. Se i pod richiedono tempo per essere creati o i workload si interrompono durante l'esecuzione, questa potrebbe essere la causa.
    • VM con avvio flessibile: per eseguire il provisioning del pool di nodi utilizzando l'opzione di consumo Avvio flessibile, passa il flag --flex-start. Per saperne di più, consulta Esegui un workload batch di piccole dimensioni con TPU e VM con avvio flessibile.

    L'output è simile al seguente:

    NAME  MACHINE_TYPE      DISK_SIZE_GB  NODE_VERSION
    np-0  ct6e-standard-4t  100           1.35.7-gke.1027000
    
  3. 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

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

kubectl create secret generic hf-secret \
    --from-literal=hf_api_token="${HF_TOKEN}" \
    --dry-run=client -o yaml | kubectl apply -f -

Esegui il deployment del server vLLM TorchTPU

Il manifest del workload (qwen3_5_server.yaml) definisce le seguenti risorse:

  • Un oggetto StorageClass (hyperdisk-balanced-qwen3) e un oggetto PersistentVolumeClaim da 500 GiB (hd-claim-qwen3) supportati dallo spazio di archiviazione Google Cloud Hyperdisk Balanced per archiviare i pesi del modello scaricato, la cache di compilazione XLA e la cache di compilazione PyTorch.

  • Un deployment (vllm-qwen3) pianificato su un singolo nodo TPU v6e con una topologia 2x2 (google.com/tpu: '4'). Il container esegue vllm serve per Qwen/Qwen3-30B-A3B-FP8 con i seguenti flag:

    • --data-parallel-size=4
    • --tensor-parallel-size=1
    • --enable-expert-parallel
    • --kv-cache-dtype=fp8
    • --max-model-len=9236
    • --max-num-seqs=64.
  • Un servizio ClusterIP (vllm-qwen3-service) che espone la porta 8000.

Per eseguire il deployment e monitorare il workload, completa i seguenti passaggi:

  1. Recupera il manifest del server:

    curl -O https://raw.githubusercontent.com/GoogleCloudSamples/AIHypercomputerSamples/refs/heads/main/tpu/inference/qwen3-5-397b/qwen3_5_server.yaml

    In alternativa, puoi scegliere di creare un file denominato qwen3_5_server.yaml con i seguenti contenuti:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: hyperdisk-balanced-qwen3-5
    provisioner: pd.csi.storage.gke.io
    parameters:
      type: hyperdisk-balanced
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: hd-claim-qwen3-5
      namespace: default
    spec:
      storageClassName: hyperdisk-balanced-qwen3-5
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 1500Gi
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: vllm-qwen3-5
      namespace: default
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: vllm-qwen3-5
      template:
        metadata:
          labels:
            app: vllm-qwen3-5
        spec:
          nodeSelector:
            cloud.google.com/gke-tpu-accelerator: tpu7x
            cloud.google.com/gke-tpu-topology: 2x2x1
          tolerations:
          - key: "cloud.google.com/gke-spot"
            operator: "Equal"
            value: "true"
            effect: "NoSchedule"
          containers:
          - name: vllm-tpu
            image: $VLLM_IMAGE
            command: ["vllm", "serve"]
            args:
            - --host=0.0.0.0
            - --port=8000
            - --model=Qwen/Qwen3.5-397B-A17B-FP8
            - --tokenizer=Qwen/Qwen3.5-397B-A17B-FP8
            - --max-model-len=9236
            - --max-num-batched-tokens=1024
            - --max-num-seqs=64
            - --no-enable-prefix-caching
            - --gpu-memory-utilization=0.9
            - --data-parallel-size=8
            - --tensor-parallel-size=1
            - --async-scheduling
            - --language-model-only
            - --enable-auto-tool-choice
            - --tool-call-parser=qwen3_coder
            - --reasoning-parser=qwen3
            - '--limit-mm-per-prompt={"image": 0, "video": 0}'
            - --kv-cache-dtype=fp8
            - --enable-expert-parallel
            - --download-dir=/data
            - --block-size=128
            env:
            - name: MODEL_IMPL_TYPE
              value: "vllm"
            - name: DP_SCHED_ENABLED
              value: "1"
            - name: ONEHOT_MOE_PERMUTE_THRESHOLD
              value: "32768"
            - name: VLLM_ENGINE_READY_TIMEOUT_S
              value: "9600"
            - name: VLLM_ENGINE_ITERATION_TIMEOUT_S
              value: "1800"
            - name: USE_MOE_SPARSE_CORE
              value: "1"
            - name: TPU_MOE_SKIP_PADDED_TOKENS
              value: "1"
            - name: VLLM_CACHE_ROOT
              value: "/data/vllm_cache"
            - name: VLLM_TORCH_COMPILE_CACHE_DIR
              value: "/data/torch_compile_cache"
            - name: XLA_CACHE_DIR
              value: "/data/xla_cache"
            - name: HF_HOME
              value: /data
            - name: HF_TOKEN
              valueFrom:
                secretKeyRef:
                  name: hf-secret
                  key: hf_api_token
            ports:
            - containerPort: 8000
            resources:
              limits:
                google.com/tpu: '4'
              requests:
                google.com/tpu: '4'
            readinessProbe:
              tcpSocket:
                port: 8000
              initialDelaySeconds: 15
              periodSeconds: 10
            volumeMounts:
            - mountPath: "/data"
              name: data-volume
            - mountPath: /dev/shm
              name: dshm
          volumes:
          - emptyDir:
              medium: Memory
            name: dshm
          - name: data-volume
            persistentVolumeClaim:
              claimName: hd-claim-qwen3-5
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: vllm-qwen3-5-service
      namespace: default
    spec:
      selector:
        app: vllm-qwen3-5
      type: ClusterIP
      ports:
      - name: http
        protocol: TCP
        port: 8000
        targetPort: 8000
  2. Applica il manifest del server vLLM TorchTPU al tuo cluster:

    envsubst '$VLLM_IMAGE' < "qwen3_5_server.yaml" | kubectl apply -f -
  3. Monitora i log di deployment del server fino al completamento dell'avvio dell'applicazione:

    kubectl wait --for=jsonpath='{.status.phase}'=Running pod -l app=vllm-qwen3-5 --timeout=900s
    kubectl logs deployment/vllm-qwen3-5 -c vllm-tpu -f

    L'avvio iniziale del servizio potrebbe richiedere circa 15-30 minuti, durante i quali il pod scarica i pesi del modello e compila il modello.

    Se utilizzi una capacità prerilasciabile come Spot o Flex-start, la creazione dei nodi della sezione TPU potrebbe richiedere più tempo dopo la creazione del pool di nodi e i nodi potrebbero essere prerilasciati durante l'esecuzione dei workload. Se i pod impiegano più tempo del previsto per avviarsi o se i workload si interrompono a metà, questa potrebbe essere la causa. Per ulteriori informazioni, consulta Prerilascio delle VM spot. Per verificare se i nodi della sezione TPU sono stati creati, consulta Controlla lo stato e le condizioni del nodo.

    Attendi che i log segnalino Application startup complete prima di inviare il traffico:

    (APIServer pid=1) INFO:     Started server process [1]
    (APIServer pid=1) INFO:     Waiting for application startup.
    (APIServer pid=1) INFO:     Application startup complete.
    
  4. Apri una seconda sessione del terminale.

  5. Nella seconda sessione del terminale, inoltra la porta locale 8000 a vllm-qwen3-service:

    kubectl port-forward service/vllm-qwen3-5-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:

curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "Qwen/Qwen3.5-397B-A17B-FP8",
        "messages": [
            {"role": "user", "content": "Hello! Tell me a joke."}
        ],
        "max_tokens": 512,
        "temperature": 0.7
    }' | jq .

Esegui un workload di benchmark

Per eseguire il benchmark del modello Qwen3-30B-A3B di cui è stato eseguito il deployment utilizzando un workload di input da 8192 token e di output da 1024 token (8k/1k) con la suite di benchmark InferenceX, completa i seguenti passaggi:

  1. Recupera il manifest del benchmark:

    curl -O https://raw.githubusercontent.com/GoogleCloudSamples/AIHypercomputerSamples/refs/heads/main/tpu/inference/qwen3-5-397b/qwen3_5-benchmark-8k1k.yaml

    In alternativa, crea un file denominato qwen3_5-benchmark-8k1k.yaml con i seguenti contenuti:

    apiVersion: v1
    kind: Pod
    metadata:
      name: qwen3-5-bench-8k1k
      namespace: default
    spec:
      restartPolicy: OnFailure
      terminationGracePeriodSeconds: 60
      containers:
      - name: vllm-bench
        image: $VLLM_IMAGE
        command: ["/bin/bash", "-c"]
        args:
        - |
          while ! curl http://vllm-qwen3-5-service:8000/ping; do sleep 30 && echo 'Waiting for server...'; done
          apt-get update && apt-get install -y git && \
          git clone https://github.com/SemiAnalysisAI/InferenceX.git /ubench/inferencex && \
          cd /ubench/inferencex && \
          git checkout 89ce6098ef2bc4576a735c43f39c7d972b091cfc && \
          python3 /ubench/inferencex/utils/bench_serving/benchmark_serving.py \
            --backend=vllm \
            --percentile-metrics='ttft,tpot,itl,e2el' \
            --host=vllm-qwen3-5-service \
            --port=8000 \
            --model=Qwen/Qwen3.5-397B-A17B-FP8 \
            --tokenizer=Qwen/Qwen3.5-397B-A17B-FP8 \
            --dataset-name=random \
            --random-input-len=8192 \
            --random-output-len=1024 \
            --random-range-ratio=1.0 \
            --num-prompts=2560 \
            --num-warmups=512 \
            --max-concurrency=256 \
            --ignore-eos
        env:
        - name: HUGGING_FACE_HUB_TOKEN
          valueFrom:
            secretKeyRef:
              key: hf_api_token
              name: hf-secret
  2. Applica il manifest del pod di benchmark:

    envsubst '$VLLM_IMAGE' < "qwen3_5-benchmark-8k1k.yaml" | kubectl apply -f -
  3. Monitora i log del benchmark:

    kubectl wait --for=jsonpath='{.status.phase}'=Running pod --field-selector=metadata.name=qwen3-5-bench-8k1k --timeout=600s
    kubectl logs -f qwen3-5-bench-8k1k

    Il completamento del benchmark richiede circa 25-30 minuti.

  4. Al termine del benchmark, cerca un output contenente metriche come le seguenti:

    • Output token throughput
    • Total token throughput
    • TTFT
    • TPOT

Esegui la pulizia

Per evitare che al tuo account Google Cloud vengano addebitati costi aggiuntivi per le risorse utilizzate in questo tutorial, elimina i workload Kubernetes, il pool di nodi TPU e il cluster GKE:

  1. Elimina il deployment, il servizio, il PersistentVolumeClaim e il secret Hugging Face del server vLLM TorchTPU:

    kubectl delete -f qwen3_5_server.yaml --ignore-not-found || true
    kubectl delete secret hf-secret --ignore-not-found || true
  2. Elimina il pool di nodi TPU v6e e il cluster GKE:

    gcloud container node-pools delete "${NODE_POOL_NAME}" \
        --cluster="${CLUSTER_NAME}" \
        --location="${REGION}" \
        --project="${PROJECT_ID}" \
        --quiet || true
    
    gcloud container clusters delete "${CLUSTER_NAME}" \
        --location="${REGION}" \
        --project="${PROJECT_ID}" \
        --quiet || true

Passaggi successivi