Disponibilizar o Qwen3-Coder-480B no GKE com o SGLang TorchTPU de vários hosts em aceleradores TPU7x

Neste tutorial, mostramos como implantar e disponibilizar o Qwen3-Coder-480B (Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8), um modelo de codificação de Mix de Especialistas, no Google Kubernetes Engine (GKE). A implantação usa aceleradores TPU7x (Ironwood) de vários hosts e o framework de disponibilização SGLang com TorchTPU (torchtpu) e desagregação de pré-preenchimento e decodificação (DP).

Na configuração de implantação descrita neste tutorial, você provisiona dois pools de nós tpu7x-standard-4t de vários hosts, cada um organizado em uma topologia 2x2x2 (oito chips TPU7x em dois nós por pool de nós, totalizando 16 chips TPU7x). Use a API LeaderWorkerSet do Kubernetes para fazer o seguinte:

  • Execute servidores de pré-preenchimento e decodificação desagregados de vários hosts em frações de TPU7x separadas com paralelismo de tensor e paralelismo de especialista ativados.
  • Encaminhe solicitações compatíveis com a OpenAI por um roteador de desagregação do SGLang.

Objetivos

  • Configure o cluster do GKE e os pools de nós de TPU7x com vários hosts.
  • Configurar credenciais e instalar o controlador LeaderWorkerSet.
  • Implante as cargas de trabalho de pré-preenchimento, decodificação e roteador do SGLang de vários hosts.
  • Envie uma solicitação de inferência ao serviço de roteador do SGLang.
  • Executar uma carga de trabalho de comparativo de mercado de exibição.
  • Fazer a limpeza.

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a [calculadora de preços](https://cloud.google.com/products/calculator).

Ao concluir as tarefas descritas neste documento, é possível evitar o faturamento contínuo excluindo os recursos criados. Para mais informações, consulte Limpeza.

Antes de começar

  • Instale e inicialize a Google Cloud CLI e instale kubectl. Se você usa o Cloud Shell, as duas ferramentas já estão pré-instaladas.

  • Você precisa de um token de acesso do Hugging Face para usar este tutorial. Você pode se inscrever em uma conta sem custo financeiro no Hugging Face. Depois de ter uma conta, gere um token de acesso:

    1. Na página Welcome to Hugging Face, clique no avatar da sua conta e selecione token de acesso.
    2. Na página Tokens de acesso, clique em Criar novo token.
    3. Selecione o tipo de token Read e insira um nome para ele.
    4. O token de acesso será exibido. Salve o token em um local seguro.

  • Ative as API Kubernetes Engine, API Compute Engine, Cloud TPU e Artifact Registry no projeto do Google Cloud :

    Ativar APIs

  • Verifique se o projeto tem cota de disco e cota de endereço IP externo suficientes na região em que a capacidade da TPU está localizada:

    1. No console do Google Cloud , acesse a página Cotas e limites do sistema.

      Acesse Cotas e limites do sistema

    2. Verifique se você tem pelo menos 2.800 GiB de cota disponível do Google Cloud Hyperdisk Balanced nos quatro volumes temporários de 700 GiB usados para armazenamento em cache de modelos pelos pods de preenchimento prévio e decodificação.

    3. Filtre por Endereços IPv4 externos regionais em uso e verifique se você tem cota de endereço IP suficiente na região de destino.

Para conseguir as permissões necessárias para concluir o tutorial, peça ao administrador para conceder a você os seguintes papéis do IAM no seu projeto:

Para mais informações sobre a concessão de papéis, consulte Gerenciar o acesso a projetos, pastas e organizações.

Também é possível conseguir as permissões necessárias usando papéis personalizados ou outros papéis predefinidos.

Verifique se a conta de serviço padrão do Compute Engine tem os seguintes papéis do Identity and Access Management (IAM) no projeto:

Configurar as variáveis de ambiente

Exporte as variáveis de ambiente que você vai usar neste 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"

Substitua:

  • YOUR_CLUSTER_NAME: o nome do cluster do GKE.
  • YOUR_PROJECT_ID: ID do seu projeto do Google Cloud.
  • YOUR_REGION: a região em que você quer implantar o cluster, como us-central1.
  • YOUR_ZONE: a zona em que você quer implantar os pools de nós da TPU7x, como us-central1-c.
  • YOUR_RESERVATION_NAME: se você estiver usando uma reserva, o nome da sua reserva de TPU7x.
  • YOUR_HF_TOKEN: seu token de acesso do Hugging Face.
  • YOUR_SGLANG_TPU_IMAGE: se você estiver usando uma imagem de contêiner personalizada do SGLang TorchTPU, o URI da imagem. Caso contrário, a imagem padrão do SGLang TorchTPU.

Criar o cluster do GKE

Para criar um cluster do GKE no modo Standard e anexar dois pools de nós de TPU7x de vários hosts para desagregação de preenchimento prévio e decodificação, siga estas etapas:

  1. Crie um cluster regional do GKE que use nós e2-standard-4 para o pool de nós padrão, que fornece recursos suficientes de CPU e memória para o controlador LeaderWorkerSet e o roteador de desagrupamento SGLang:

    gcloud container clusters create $CLUSTER_NAME \
      --project=$PROJECT_ID \
      --location=$REGION \
      --machine-type=e2-standard-4
  2. Crie uma política de posicionamento de carga de trabalho de alta capacidade de processamento com uma topologia de acelerador 2x2x2, que é necessária para pools de nós de TPU7x de vários hosts:

    gcloud compute resource-policies create workload-policy tpu7x-2x2x2-workload-policy \
        --type=HIGH_THROUGHPUT \
        --accelerator-topology=2x2x2 \
        --project=${PROJECT_ID} \
        --region=${REGION}
  3. Crie dois pools de nós de TPU7x de vários hosts (tpu7x-pool-0 e tpu7x-pool-1), cada um contendo dois nós tpu7x-standard-4t (oito chips de TPU7x em uma topologia 2x2x2 por pool de nós, totalizando 16 chips de 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

    Em vez de usar uma reserva específica, também é possível criar os pools de nós usando a capacidade spot ou as VMs flex-start transmitindo as seguintes flags:

    • VMs spot: substitua --reservation-affinity=specific e --reservation=${RESERVATION} por --spot para usar a capacidade spot. As instâncias que usam a capacidade spot podem levar algum tempo para serem criadas após a criação do pool de nós e podem ser interrompidas durante as execuções da carga de trabalho.
    • VMs flex-start: substitua --num-nodes=2, --reservation-affinity=specific e --reservation=${RESERVATION} por --flex-start, --enable-autoscaling, --num-nodes=0, --min-nodes=0 e --max-nodes=2 para provisionar o pool de nós usando a opção de consumo flex-start. Para mais informações, consulte Executar uma carga de trabalho em lote pequena com TPUs e VMs de início flexível.
  4. Configure as credenciais de kubectl para se comunicar com o cluster:

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

Configurar credenciais e instalar o LeaderWorkerSet

Para preparar o cluster para a implantação do SGLang em vários hosts, siga estas etapas:

  1. Crie um Secret do Kubernetes chamado hf-secret para armazenar o token de acesso do Hugging Face:

    kubectl create secret generic hf-secret \
        --from-literal=hf_api_token=${HF_TOKEN}
  2. Instale o CustomResourceDefinition e o controlador do LeaderWorkerSet no 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

Implantar o servidor SGLang de vários hosts

O manifesto da carga de trabalho (qwen3-coder-480b-multihost-disagg-v7x.yaml) define os seguintes recursos para coordenar a disponibilização de pré-preenchimento e decodificação desagregados nos dois pools de nós TPU7x 2x2x2:

  • StorageClass (hyperdisk-balanced-tpu): provisiona volumes efêmeros de 700 GiB ReadWriteOnce com suporte do armazenamento do Google Cloud Hyperdisk Balanced. Cada um dos quatro pods de preenchimento prévio e decodificação monta o próprio volume em /data para armazenar localmente o checkpoint Qwen/Qwen3-Coder-480B-A35B-Instruct-FP8 transferido por download.
  • Pool de preenchimento prévio (sglang-prefill-service e sglang-tpu-prefill): executa um LeaderWorkerSet de size: 2 (um pod líder e um pod de worker) fixado exclusivamente a um pool de nós TPU7x 2x2x2 (oito chips de TPU, que fornecem 16 TensorCores). O LeaderWorkerSet usa o conjunto de anotações leaderworkerset.sigs.k8s.io/exclusive-topology definido como "cloud.google.com/gke-nodepool". Os pods iniciam sglang.launch_server com as flags --disaggregation-mode=prefill, --tensor-parallel-size=16 e --ep-size=16, expondo a porta HTTP 8000 e a porta de inicialização 8998.
  • Pool de decodificação (sglang-decode-service e sglang-tpu-decode): executa um LeaderWorkerSet de size: 2 fixado ao segundo pool de nós da TPU v5e 2x2x2 (oito chips de TPU, que fornecem 16 TensorCores). O LeaderWorkerSet inicia sglang.launch_server com as sinalizações --disaggregation-mode=decode, --tensor-parallel-size=16 e --ep-size=16.
  • Roteador de desagregação (sglang-router e sglang-service): executa uma implantação de CPU que executa o módulo sglang_router.launch_router com a flag --pd-disaggregation e expõe um serviço ClusterIP na porta 8000. O roteador direciona os comandos e coordena a transferência de cache de chave-valor entre os serviços sglang-prefill-service e sglang-decode-service.

Para implantar e monitorar os servidores SGLang de vários hosts, siga estas etapas:

  1. Faça o download do manifesto da carga de trabalho:

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

    Como alternativa, crie um arquivo chamado qwen3-coder-480b-multihost-disagg-v7x.yaml com o seguinte conteúdo:

    # ==============================================================================
    # 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. Aplique o manifesto da carga de trabalho ao cluster:

    envsubst '$SGLANG_IMAGE' < qwen3-coder-480b-multihost-disagg-v7x.yaml | kubectl apply -f -
  3. Verifique se os pods de roteador, preenchimento prévio e decodificação foram criados:

    kubectl get pods

    Quando todos os pods estiverem prontos, a saída será semelhante a esta:

    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. Monitore os registros do pod líder de preenchimento prévio e decodificação até que a inicialização seja concluída:

    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

    O download do modelo inicial, o carregamento de peso e a compilação levam aproximadamente de 15 a 30 minutos na primeira execução.

    Se você estiver usando capacidade preemptiva, como capacidade spot ou VMs flex-start, pode levar mais tempo após a criação do pool de nós para que os nós de fração de TPU sejam criados, e eles podem ser interrompidos durante as execuções de carga de trabalho. Para mais informações, consulte Instâncias de VM preemptivas. Para verificar se os nós de fração de TPU foram criados, consulte Verificar o status e as condições do nó.

    Aguarde até que os registros do pod líder de preenchimento prévio e decodificação mostrem a seguinte mensagem e kubectl get pods mostre 1/1 na coluna READY para todos os pods:

    The server is fired up and ready to roll!
    
  5. Abra uma segunda sessão de terminal e encaminhe a porta local 8000 para sglang-service:

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

    Deixe o encaminhamento de portas em execução nessa sessão do terminal.

Enviar solicitações de inferência

Volte para a primeira sessão do terminal e envie uma solicitação de conclusão de chat para o endpoint /v1/chat/completions exposto pelo roteador do 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
  }'

Executar uma carga de trabalho de comparativo de mercado

Para comparar o modelo Qwen3-Coder implantado usando uma carga de trabalho de entrada de 1.024 tokens e saída de 1.024 tokens (1k/1k) do pod sglang-router, execute o seguinte 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

Limpar

Para evitar cobranças contínuas extras na sua conta do Google Cloud pelos recursos usados neste tutorial, exclua os seguintes recursos:

  1. Exclua as cargas de trabalho de preenchimento, decodificação e roteador do SGLang:

    kubectl delete -f qwen3-coder-480b-multihost-disagg-v7x.yaml --ignore-not-found
  2. Exclua os pools de nós da TPU7x, a política de posicionamento de cargas de trabalho e o cluster do 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

A seguir