Phase 17 - Lesson 03

Autoscaling de GPU no Kubernetes — Karpenter, KAI Scheduler, Gang Scheduling

Três camadas, não uma. O Karpenter provisiona nós dinamicamente (menos de um minuto, 40% mais rápido que o Cluster Autoscaler). O KAI Scheduler lida com gang scheduling, reconhecimento de topologia (topology awareness) e filas hierárquicas — ele evita a armadilha de alocação parcial de "7 de 8", na qual sete nós esperam e gastam recursos ociosos aguardando por uma GPU ausente. Os escaladores automáticos no nível de aplicação (NVIDIA Dynamo Planner, llm-d Workload Variant Autoscaler) escalam com base em sinais específicos de inferência — profundidade da fila, utilização do KV cache — não no ciclo de trabalho de CPU/DCGM. A armadilha clássica do HPA é que DCGM_FI_DEV_GPU_UTIL é uma medição do ciclo de trabalho (duty cycle): 100% de utilização pode significar 10 requisições ou 100. O vLLM pré-aloca memória do KV cache, então a memória nunca dispara uma redução de escala (scale-down). Esta lição ensina você a compor essas três camadas e evitar a política padrão do Karpenter WhenEmptyOrUnderutilized que encerra trabalhos de GPU em andamento no meio da inferência.

Tipo: Aprender Linguagens: Python (stdlib, simulador simplificado de autoscaler por profundidade de fila) Pré-requisitos: Phase 17 · 02 (Inference Platform Economics), Phase 17 · 04 (vLLM Serving Internals) Tempo: ~75 minutos

Objetivos de Aprendizagem

  • Diagramar as três camadas de autoscaling (provisionamento de nós, gang scheduling, nível de aplicação) e nomear a ferramenta utilizada em cada camada.
  • Explicar por que DCGM_FI_DEV_GPU_UTIL é o sinal de HPA errado para o vLLM e nomear dois substitutos (profundidade de fila, utilização do KV cache).
  • Descrever o gang scheduling e o modo de falha de alocação parcial que o KAI Scheduler previne (7 de 8 GPUs ociosas).
  • Nomear a política de consolidação do Karpenter (WhenEmptyOrUnderutilized) que encerra trabalhos de GPU em execução e apresentar a alternativa segura para 2026.

O Problema

Sua equipe publica um serviço de atendimento de LLM no Kubernetes. Você configura o HPA com DCGM_FI_DEV_GPU_UTIL como o sinal. O serviço se mantém fixo em 100% de utilização durante o horário comercial. O HPA nunca escala para cima (scale-up) — ele já acha que você está no limite de capacidade. Você adiciona uma réplica manualmente e o TTFT cai. Mesmo assim, o HPA não escala. O sinal está mentindo para você.

Separadamente, você usa o Cluster Autoscaler para os nós. Um prompt de 1M de tokens chega às 2h; o clúster passa 3 minutos provisionando um nó, e a requisição expira (timeout).

Além disso, você faz o deploy de um modelo de 70B que exige 8 GPUs distribuídas em 2 nós. O clúster tem 7 GPUs livres e 1 espalhada por 3 nós. O Cluster Autoscaler provisiona um nó para a 1 GPU ausente. Sete nós esperam 4 minutos queimando dinheiro enquanto o Kubernetes coloca a última GPU em funcionamento.

Três camadas, três modos de falha diferentes. Em 2026, o autoscaling focado em GPU não é simplesmente "ligar o HPA". É compor o provisionamento de nós, o gang scheduling e o autoscaling baseado em sinais da aplicação.

O Conceito

Camada 1 — provisionamento de nós (Karpenter)

O Karpenter monitora pods pendentes e provisiona nós em cerca de 45 a 60 segundos (o Cluster Autoscaler normalmente leva de 90 a 120 segundos para nós de GPU). Ele escolhe os tipos de instância dinamicamente de acordo com a restrição do NodePool — se o seu pod precisa de 8 H100s e o clúster não tem um nó compatível, o Karpenter provisiona um diretamente em vez de escalar um grupo existente.

A armadilha da consolidação: a política padrão de consolidação do Karpenter (consolidationPolicy: WhenEmptyOrUnderutilized) é perigosa para pools de GPU. Ela encerrará um nó de GPU em execução para migrar pods para uma instância menor de tamanho adequado e mais barata. Para cargas de trabalho de inferência, isso significa despejar requisições em andamento e recarregar um modelo de 70B no novo nó. A perda se traduz em minutos de capacidade e falhas nas requisições.

Configuração segura para pools de GPU:

disruption:
  consolidationPolicy: WhenEmpty
  consolidateAfter: 1h

Permite que o Karpenter consolide nós verdadeiramente vazios após uma hora, mas nunca despeje um trabalho em execução.

Camada 2 — gang scheduling (KAI Scheduler)

O KAI Scheduler (conhecido originalmente como projeto "Karp" e depois renomeado) lida com o que o kube-scheduler padrão não consegue:

Gang scheduling — agendamento do tipo tudo-ou-nada. Um pod de inferência distribuída que exige 8 GPUs exige que todas as 8 comecem juntas ou nenhuma começa. Sem isso, você cai na armadilha da alocação parcial: 7 dos 8 pods iniciam, esperam indefinidamente e queimam dinheiro.

Reconhecimento de topologia (topology awareness) — saber quais GPUs compartilham o NVLink, quais estão no mesmo rack e quais têm InfiniBand entre elas. Coloca os pods de acordo com essas informações. Uma carga de trabalho de paralelismo de tensores do DeepSeek-V3 de 67B precisa permanecer em um único domínio NVLink; o KAI Scheduler respeita essa exigência.

Filas hierárquicas — várias equipes competem pelo mesmo pool de GPUs com prioridade e cota. O aperto de produção da Equipe A só é interrompido pelo trabalho de treinamento da Equipe B se as regras de prioridade permitirem.

O KAI é implantado ao lado do kube-scheduler como um agendador secundário; você anota as cargas de trabalho para usá-lo. As pilhas de produção do Ray e do vLLM são integradas ao KAI.

Camada 3 — sinais no nível de aplicação

A armadilha do HPA: DCGM_FI_DEV_GPU_UTIL é uma métrica de ciclo de trabalho — ela mede se a GPU estava realizando trabalho a cada intervalo de amostragem. A utilização em 100% pode significar 10 requisições simultâneas ou 100; a GPU estava ocupada de qualquer forma. Escalar com base no ciclo de trabalho é escalar às cegas.

Pior ainda, motores como o vLLM pré-alocam a memória do KV cache (até o limite de --gpu-memory-utilization). O uso da memória permanece próximo a 90% mesmo com apenas uma requisição ativa. O HPA baseado em memória nunca realiza a redução de escala (scale-down).

Sinais de substituição para 2026:

  • Profundidade da fila (número de requisições esperando pelo prefill).
  • Utilização do KV cache (qual fração de blocos está alocada para sequências ativas).
  • TTFT P99 por réplica (seu sinal de SLA).
  • Goodput (requisições que atendem a todos os SLOs por segundo).

O NVIDIA Dynamo Planner e o llm-d Workload Variant Autoscaler consomem esses sinais e escalam as réplicas. Eles substituem totalmente o HPA no serviço de LLMs.

Quando usar o quê

Decisão de escala Ferramenta
Adicionar/remover nós Karpenter
Agendar tarefas multi-GPU KAI Scheduler
Adicionar/remover réplicas Dynamo Planner / llm-d WVA (ou HPA personalizado na profundidade da fila)
Escolher o tipo de GPU Karpenter NodePool
Preemptar baixa prioridade Filas do KAI Scheduler

A separação (disaggregation) de prefill/decode complica tudo

Se você executa prefill e decodificação separados (Phase 17 · 17), você tem duas classes de pods com diferentes gatilhos de escala: os pods de prefill escalam com base na profundidade da fila, enquanto os pods de decodificação escalam sob pressão do KV cache. O llm-d expõe esses componentes como Services separados com HPA por função. Não tente colocar um único HPA na frente de ambos.

O cold start também importa aqui

A mitigação de cold start (Phase 17 · 10) é onde o tempo de provisionamento de nós se torna visível ao usuário. O tempo de aquecimento do Karpenter de 45 a 60 segundos, somado à carga de um modelo de 20GB e à inicialização do motor, significa que uma requisição partindo do zero leva de 2 a 5 minutos. Mantenha um pool ativo (min_workers=1) para caminhos críticos de SLO ou use checkpoints no estilo da Modal na camada da aplicação.

Números que você deve lembrar

  • Provisionamento de nós com Karpenter: ~45-60s vs Cluster Autoscaler ~90-120s (nós de GPU).
  • O KAI Scheduler evita o desperdício de alocação parcial — a armadilha de 7 de 8.
  • DCGM_FI_DEV_GPU_UTIL como sinal de HPA: quebrado; use profundidade de fila ou utilização de KV.
  • WhenEmptyOrUnderutilized do Karpenter: encerra trabalhos de GPU em andamento. Use WhenEmpty + consolidateAfter: 1h para inferência.

Use It

code/main.py simula um autoscaler de três camadas em uma carga de trabalho de GPU intermitente. Compara o HPA ingênuo (ciclo de trabalho), o HPA por profundidade de fila e a escala agendada por gang do KAI. Relata requisições não atendidas, minutos de GPU ociosa e uma pontuação composta.

Ship It

Esta lição produz outputs/skill-gpu-autoscaler-plan.md. Com base na topologia do clúster, perfil da carga de trabalho e SLO, ela projeta um plano de autoscaling de três camadas.

Exercises

  1. Execute code/main.py. Sob uma carga de trabalho com picos (bursty), quantas requisições o HPA de ciclo de trabalho ingênuo descarta que o HPA por profundidade de fila consegue processar? De onde vem essa diferença?
  2. Projete um Karpenter NodePool para um clúster que atende ao Llama 3.3 70B FP8 em H100 SXM5. Especifique o capacity-type, disruption.consolidationPolicy, consolidateAfter e um taint que mantenha as cargas de trabalho que não usam GPU fora desses nós.
  3. Sua equipe relata que os deploys estão travados em Pending porque "GPUs estão disponíveis, mas o pod não agenda". Diagnostique: o problema é do Karpenter, do kube-scheduler ou do KAI Scheduler? Quais métricas confirmam isso?
  4. Escolha um sinal para escalar automaticamente pods de prefill desmembrados e um sinal diferente para pods de decodificação. Justifique ambos.
  5. Calcule o custo da armadilha de consolidação WhenEmptyOrUnderutilized em um serviço de produção 24/7 que faz uma média de 60 eventos de queda de requisições/dia com TTFT P99 > 10s.

Key Terms

Term What people say What it actually means
Karpenter "o provisionador de nós" Autoscaler de nós do Kubernetes; provisionamento em menos de um minuto
Cluster Autoscaler "o antigo escalador" Predecessor do autoscaler de nós do Kubernetes; mais lento, baseado em grupos
KAI Scheduler "o GPU scheduler" Agendador secundário para gang + topologia + filas
Gang scheduling "tudo ou nada" Agenda N pods de forma atômica ou adia todos eles
Topology awareness "reconhecimento de rack" Posiciona pods com base no NVLink/IB/posicionamento físico do rack
DCGM_FI_DEV_GPU_UTIL "utilização da GPU" Métrica de ciclo de trabalho; NÃO é um sinal de escala para LLMs
Queue depth "requisições esperando" Sinal correto de HPA para escala limitada pelo prefill
KV cache utilization "pressão de memória" Sinal correto de HPA para escala limitada pelo decode
Consolidation "consolidação do Karpenter" Encerramento de nós para migração para tipos de instâncias mais baratas
WhenEmpty + 1h "consolidação segura" Política que não despeja tarefas de GPU em execução

Further Reading

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).