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 KarpenterWhenEmptyOrUnderutilizedque 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_UTILcomo sinal de HPA: quebrado; use profundidade de fila ou utilização de KV.WhenEmptyOrUnderutilizeddo Karpenter: encerra trabalhos de GPU em andamento. UseWhenEmpty + consolidateAfter: 1hpara 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
- 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? - 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,consolidateAftere um taint que mantenha as cargas de trabalho que não usam GPU fora desses nós. - 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?
- Escolha um sinal para escalar automaticamente pods de prefill desmembrados e um sinal diferente para pods de decodificação. Justifique ambos.
- Calcule o custo da armadilha de consolidação
WhenEmptyOrUnderutilizedem 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
- KAI Scheduler GitHub — documentos de design e exemplos de configuração.
- Karpenter Disruption Controls — semântica da política de consolidação e padrões seguros para GPU.
- NVIDIA — Disaggregated LLM Inference on Kubernetes — sinais de escala do Dynamo Planner.
- Ray docs — KAI Scheduler for RayClusters — padrão de integração do Ray.
- AWS EKS Compute and Autoscaling Best Practices — orientações específicas para o Kubernetes gerenciado.
- llm-d GitHub — design do Workload Variant Autoscaler.