Entrega Qwen3-Coder-480B en GKE con SGLang TorchTPU de varios hosts en aceleradores TPU7x

En este instructivo, se muestra cómo implementar y entregar Qwen3-Coder-480B (Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8), un modelo de codificación de mezcla de expertos, en Google Kubernetes Engine (GKE). La implementación usa aceleradores TPU7x (Ironwood) de varios hosts y el framework de entrega de SGLang con TorchTPU (torchtpu) y desagregación de decodificación de relleno previo (PD).

En la configuración de implementación que se describe en este instructivo, aprovisionas dos grupos de nodos tpu7x-standard-4t de varios hosts, cada uno organizado en una topología 2x2x2 (ocho chips TPU7x en dos nodos por grupo de nodos, para un total de 16 chips TPU7x). Usa la API de LeaderWorkerSet de Kubernetes para hacer lo siguiente:

  • Ejecuta servidores de decodificación y precompletado de varios hosts desagregados en porciones de TPU7x independientes con el paralelismo tensorial y el paralelismo de expertos habilitados.
  • Enruta las solicitudes compatibles con OpenAI a través de un router de desagregación de SGLang.

Objetivos

  • Configura tu clúster de GKE y los grupos de nodos TPU7x de varios hosts.
  • Configura las credenciales y, luego, instala el controlador de LeaderWorkerSet.
  • Implementa las cargas de trabajo de precompletado, decodificación y router de SGLang de varios hosts.
  • Envía una solicitud de inferencia al servicio de router de SGLang.
  • Ejecuta una carga de trabajo de comparativas de entrega.
  • Realizar una limpieza

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la [calculadora de precios](https://cloud.google.com/products/calculator).

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  • Instala y, luego, inicializa Google Cloud CLI, y, luego, instala kubectl. Si usas Cloud Shell, ambas herramientas están preinstaladas.

  • Necesitas un token de acceso de Hugging Face para usar este instructivo. Puedes registrarte para obtener una cuenta gratuita en Hugging Face. Después de tener una cuenta, genera un token de acceso:

    1. En la página Welcome to Hugging Face, haz clic en el avatar de tu cuenta y selecciona Tokens de acceso.
    2. En la página Tokens de acceso, haz clic en Crear token nuevo.
    3. Selecciona el tipo de token Lectura y, luego, ingresa un nombre para el token.
    4. Se mostrará tu token de acceso. Guarda el token en un lugar seguro.

  • Habilita la API de Kubernetes Engine, la API de Compute Engine, la API de Cloud TPU y la API de Artifact Registry en tu proyecto de Google Cloud :

    Habilita las API

  • Asegúrate de que tu proyecto tenga suficiente cuota de disco y de dirección IP externa en la región en la que se encuentra tu capacidad de TPU:

    1. En la consola de Google Cloud , ve a la página Cuotas y límites del sistema.

      Ir a Cuotas y límites del sistema

    2. Verifica que tengas al menos 2,800 GiB de cuota disponible de Google Cloud Hyperdisk Balanced en los cuatro volúmenes efímeros de 700 GiB que usan los Pods de precompletado y decodificación para el almacenamiento en caché de modelos.

    3. Filtra por Direcciones IPv4 externas regionales en uso y verifica que tengas suficiente cuota de direcciones IP en tu región de destino.

Si quieres obtener los permisos que necesitas para completar este instructivo, pídele a tu administrador que te otorgue los siguientes roles de IAM en tu proyecto:

Para obtener más información sobre cómo otorgar roles, consulta Administra el acceso a proyectos, carpetas y organizaciones.

También puedes obtener los permisos necesarios a través de roles personalizados o cualquier otro rol predefinido.

Asegúrate de que tu cuenta de servicio predeterminada de Compute Engine tenga los siguientes roles de Identity and Access Management (IAM) en tu proyecto:

Configura tus variables de entorno

Exporta las variables de entorno que usarás en este instructivo:

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"

Reemplaza lo siguiente:

  • YOUR_CLUSTER_NAME: el nombre del clúster de GKE.
  • YOUR_PROJECT_ID: Es el ID de tu proyecto de Google Cloud.
  • YOUR_REGION: Es la región en la que deseas implementar tu clúster, como us-central1.
  • YOUR_ZONE: Es la zona en la que deseas implementar tus grupos de nodos de TPU7x, como us-central1-c.
  • YOUR_RESERVATION_NAME: Si usas una reserva, el nombre de tu reserva de TPU7x.
  • YOUR_HF_TOKEN: Es tu token de acceso de Hugging Face.
  • YOUR_SGLANG_TPU_IMAGE: Si usas una imagen de contenedor de TorchTPU de SGLang personalizada, el URI de tu imagen; de lo contrario, la imagen de TorchTPU de SGLang predeterminada.

Crea el clúster de GKE

Para crear un clúster de GKE en modo estándar y conectar dos grupos de nodos TPU7x de varios hosts para la desagregación de decodificación y relleno previo, completa los siguientes pasos:

  1. Crea un clúster de GKE regional que use nodos e2-standard-4 para el grupo de nodos predeterminado, que proporciona suficientes recursos de CPU y memoria para el controlador LeaderWorkerSet y el router de desagregación de SGLang:

    gcloud container clusters create $CLUSTER_NAME \
      --project=$PROJECT_ID \
      --location=$REGION \
      --machine-type=e2-standard-4
  2. Crea una política de ubicación de cargas de trabajo de alta capacidad de procesamiento con una topología de acelerador 2x2x2, que es necesaria para los grupos de nodos TPU7x de varios hosts:

    gcloud compute resource-policies create workload-policy tpu7x-2x2x2-workload-policy \
        --type=HIGH_THROUGHPUT \
        --accelerator-topology=2x2x2 \
        --project=${PROJECT_ID} \
        --region=${REGION}
  3. Crea dos grupos de nodos TPU7x de varios hosts (tpu7x-pool-0 y tpu7x-pool-1), cada uno con dos nodos tpu7x-standard-4t (ocho chips TPU7x en una topología 2x2x2 por grupo de nodos, para un total de 16 chips 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

    En lugar de usar una reserva específica, también puedes crear los grupos de nodos con capacidad Spot o VMs de inicio flexible pasando las siguientes marcas:

    • VMs Spot: Reemplaza --reservation-affinity=specific y --reservation=${RESERVATION} por --spot para usar la capacidad de Spot. Las instancias que usan la capacidad de Spot pueden tardar en crearse después de que se crea el grupo de nodos y se pueden interrumpir durante las ejecuciones de las cargas de trabajo.
    • VMs de inicio flexible: Reemplaza --num-nodes=2, --reservation-affinity=specific y --reservation=${RESERVATION} por --flex-start, --enable-autoscaling, --num-nodes=0, --min-nodes=0 y --max-nodes=2 para aprovisionar el grupo de nodos con la opción de consumo de inicio flexible. Para obtener más información, consulta Ejecuta una carga de trabajo por lotes pequeña con TPU y VMs de inicio flexible.
  4. Configura las credenciales de kubectl para comunicarte con tu clúster:

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

Configura las credenciales e instala LeaderWorkerSet

Para preparar tu clúster para la implementación de SGLang de varios hosts, completa los siguientes pasos:

  1. Crea un Secret de Kubernetes llamado hf-secret para almacenar tu token de acceso de Hugging Face:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN}
  2. Instala la CustomResourceDefinition y el controlador de LeaderWorkerSet en tu clúster:

    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

Implementa el servidor de SGLang de varios hosts

El manifiesto de la carga de trabajo (qwen3-coder-480b-multihost-disagg-v7x.yaml) define los siguientes recursos para coordinar la entrega de precompletado y decodificación desagregados en los dos grupos de nodos TPU7x de 2x2x2:

  • StorageClass (hyperdisk-balanced-tpu): Aprovisiona volúmenes efímeros de 700 GiB ReadWriteOnce respaldados por el almacenamiento de Google Cloud Hyperdisk Balanced. Cada uno de los cuatro Pods de precompletado y decodificación activa su propio volumen en /data para almacenar el punto de control de Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 descargado de forma local.
  • Grupo de relleno previo (sglang-prefill-service y sglang-tpu-prefill): Ejecuta un LeaderWorkerSet de size: 2 (un Pod líder y un Pod trabajador) fijado exclusivamente a un grupo de nodos TPU7x 2x2x2 (ocho chips TPU, que proporcionan 16 TensorCores). LeaderWorkerSet usa la anotación leaderworkerset.sigs.k8s.io/exclusive-topology establecida en "cloud.google.com/gke-nodepool". Los Pods inician sglang.launch_server con las marcas --disaggregation-mode=prefill, --tensor-parallel-size=16 y --ep-size=16, lo que expone el puerto HTTP 8000 y el puerto de arranque 8998.
  • Grupo de decodificación (sglang-decode-service y sglang-tpu-decode): Ejecuta un LeaderWorkerSet de size: 2 fijado al segundo grupo de nodos TPU7x 2x2x2 (ocho chips TPU, que proporcionan 16 TensorCores). LeaderWorkerSet inicia sglang.launch_server con las marcas --disaggregation-mode=decode, --tensor-parallel-size=16 y --ep-size=16.
  • Router de desagregación (sglang-router y sglang-service): Ejecuta un Deployment de CPU que ejecuta el módulo sglang_router.launch_router con la marca --pd-disaggregation y expone un Service ClusterIP en el puerto 8000. El router dirige las instrucciones y coordina la transferencia de la caché de KV entre los servicios sglang-prefill-service y sglang-decode-service.

Para implementar y supervisar los servidores de SGLang de varios hosts, completa los siguientes pasos:

  1. Descarga el manifiesto de la carga de trabajo:

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

    Como alternativa, crea un archivo llamado qwen3-coder-480b-multihost-disagg-v7x.yaml con el siguiente contenido:

    # ==============================================================================
    # 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. Aplica el manifiesto de la carga de trabajo a tu clúster:

    envsubst '$SGLANG_IMAGE' < qwen3-coder-480b-multihost-disagg-v7x.yaml | kubectl apply -f -
  3. Verifica que se hayan creado los Pods de router, prefill y decode:

    kubectl get pods

    Cuando todos los Pods están listos, el resultado es similar al siguiente:

    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. Supervisa los registros del Pod líder de prefill y decode hasta que se complete el inicio:

    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

    La descarga inicial del modelo, la carga de pesos y la compilación tardan aproximadamente entre 15 y 30 minutos en la primera ejecución.

    Si usas capacidad interrumpible, como la capacidad de Spot o las VMs de inicio flexible, es posible que los nodos de porción de TPU tarden más en crearse después de la creación del grupo de nodos y que se interrumpan durante las ejecuciones de las cargas de trabajo. Para obtener más información, consulta Instancias de VMs interrumpibles. Para verificar si se crearon los nodos de porción de TPU, consulta Verifica el estado y las condiciones del nodo.

    Espera hasta que los registros de los Pods de líder de decodificación y de precompletado muestren el siguiente mensaje y kubectl get pods muestre 1/1 en la columna READY para todos los Pods:

    The server is fired up and ready to roll!
    
  5. Abre una segunda sesión de terminal y reenvía el puerto local 8000 a sglang-service:

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

    Deja la redirección de puertos en ejecución en esta sesión de terminal.

Envía solicitudes de inferencia

Regresa a tu primera sesión de terminal y envía una solicitud de finalización de chat al extremo /v1/chat/completions expuesto por el router de 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
  }'

Ejecuta una carga de trabajo de comparativas

Para comparar el modelo Qwen3-Coder implementado con una carga de trabajo de entrada de 1,024 tokens y salida de 1,024 tokens (1k/1k) desde el Pod sglang-router, ejecuta el siguiente 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

Realiza una limpieza

Para evitar que se apliquen cargos adicionales continuos a tu cuenta de Google Cloud por los recursos que usaste en este instructivo, borra los siguientes recursos:

  1. Borra las cargas de trabajo de precompletado, decodificación y router de SGLang:

    kubectl delete -f qwen3-coder-480b-multihost-disagg-v7x.yaml --ignore-not-found
  2. Borra los grupos de nodos TPU7x, la política de posición de las cargas de trabajo y el clúster de 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

¿Qué sigue?