PerfectScale

Escalonamento granular de pods no Kubernetes: muito além do HPA e VPA

Esta página também está disponível em English, Deutsch, Español, Français, Italiano e 日本語.

Jun 25, 20264 min read
Joshua Fox

About Joshua Fox

Joshua Fox has been a software architect in innovative technology companies for 20 years. Now, he advises tech startups and growth companies on architecture and cost optimization for Google Cloud Platform and Amazon Web Services; also publishing articles, and speaking to cloud engineers.

He has a PhD from Harvard University and a BA in math from Brandeis.

My personal page

1. Introdução

O autoscaling do Kubernetes foi pensado para manter a robustez sem estourar o orçamento. Os autoscalers conseguem ajustar o número de nós com o Cluster Autoscaler, ou o número e o tamanho dos pods com um Vertical ou Horizontal Pod Autoscaler.

Mas há limitações: os autoscalers padrão dependem de métricas genéricas, como o uso médio de CPU. Isso é impreciso demais, e o resultado é que ou se mantém recursos desperdiçados, ou o sistema fica instável sob carga. Neste artigo, vou focar nos pod autoscalers e mostrar como a telemetria granular do cAdvisor, do Kube-State-Metrics e do eBPF pode tornar as decisões de escalonamento mais precisas e conectadas à realidade dos workloads. Essas decisões são então aplicadas a pods em execução por meio da API InPlacePodVerticalScaling do Kubernetes.

2. As falhas do autoscaling atual

O autoscaling padrão tem três limitações centrais:

  • Granularidade insuficiente do sinal: médias simples de CPU não refletem todo o perfil de desempenho de aplicações complexas, levando a decisões de escalonamento atrasadas ou equivocadas.
  • Conflito entre HPA e VPA: o HPA escala com base nos recursos solicitados, enquanto o VPA modifica essas solicitações. Quando os dois atuam de forma independente, podem emitir instruções contraditórias e provocar oscilação.
  • Redimensionamento por eviction: o VPA legado exige eviction de pods para aplicar mudanças de recursos, o que prejudica workloads stateful ou sensíveis a cache, que não toleram a latência de cold start.

3. O pipeline de telemetria em múltiplas camadas

Partimos de métricas em nível de nó como linha de base e, em seguida, somamos mais quatro camadas de telemetria para obter sinais mais granulares:

  • Camada 0 (linha de base): utilização de CPU e memória em nível de nó, vinda do Kubernetes Metrics Server, que fornece a base de granularidade mais grossa.
  • Camada 1: telemetria em nível de container via cAdvisor. Essa camada captura a latência de micro-bursts — picos curtos de CPU que inflam as médias sem refletir carga sustentada — e memory working sets, evitando decisões de escalonamento motivadas pelo crescimento inofensivo do page cache do kernel.
  • Camada 2: o Kube-State-Metrics (KSM) fornece contexto sobre a saúde do workload, como réplicas pendentes e sinais de saturação do HPA. Isso permite ao controller verificar se o escalonamento é mesmo necessário ou se já está em andamento antes de disparar novas ações.
  • Camada 3: o eBPF sonda o kernel para expor sinais profundos da aplicação, como taxas de alocação no heap da JVM e pressão do Garbage Collection (GC). Isso viabiliza ajustes preemptivos de recursos antes que ocorram eventos de OOM. Por exemplo, uma probe bpftrace usando marcadores USDT do HotSpot, como usdt:/path/to/libjvm.so:hotspot:mem__pool__gc__begin, consegue detectar picos na frequência de GC segundos antes que eles afetem a latência, dando ao controller uma vantagem inicial.
  • Camada 4: GPUs hoje são um recurso crítico para o custo e o desempenho de AI/ML. Métricas específicas de GPU via NVIDIA DCGM permitem escalar com base em gargalos de GPU, como utilização de SM ou saturação da largura de banda de memória.

4. Construindo o controller unificado de escalonamento

Um controller unificado harmoniza esses sinais como uma máquina de estados: mantém estados explícitos (stable, scaling-horizontal, scaling-vertical, cooldown) e só faz transições entre eles sob condições bem definidas. Isso é diferente dos scalers atuais, que em geral funcionam como motores de regras, avaliando cada sinal isoladamente — com o risco de emitir instruções conflitantes.

O controller opera em três fases:

Fase 1: agregação de sinais

Cada camada de telemetria contribui com uma pontuação ponderada para uma única métrica de pressão por workload. Sinais da camada de aplicação, como os da Camada 3 (eBPF/pressão de GC), recebem peso maior.

Fase 2: arbitragem de eixo

O controller evita conflitos ao identificar que um escalonamento horizontal já está em andamento; isso é indicado por AbleToScale=True somado a uma diferença diferente de zero entre currentReplicas e desiredReplicas.

Já quando um workload é declarado singleton ou stateful (via anotação ou por pertencer a um StatefulSet), o controller direciona sempre para escalonamento vertical, nunca horizontal.

Fase 3: reconciliação e cooldown

Após qualquer ação de escalonamento, um período de cooldown evita thrashing. O controller só reavalia depois de observar pelo menos duas janelas consecutivas de métricas acima ou abaixo do limite, garantindo que picos transitórios não disparem eventos de escalonamento em sequência.

5. In-Place Pod Vertical Scaling

A API InPlacePodVerticalScaling, que chegou ao General Availability em dezembro de 2025 com o Kubernetes v1.35, permite alterar recursos sem precisar de eviction do pod. O controller aplica um patch na especificação do pod, e o kubelet atualiza dinamicamente os cgroups do container — sem queda de conexões e sem reinício de processo.

Um exemplo concreto: se a camada eBPF detecta pressão sustentada do GC do Java indicando que a exaustão de heap deve ocorrer em até 30 segundos, o controller dispara um PATCH /api/v1/namespaces/{ns}/pods/{name} com resources.requests.memory e resources.limits.memory atualizados. O kubelet aplica a mudança ajustando o limite de memória do cgroup do container in-place.

Isso é especialmente valioso para serviços com cold start longo, como aplicações JVM, sidecars de banco de dados e caches em memória, nos quais o redimensionamento via eviction introduziria picos de latência de dezenas de segundos.

6. Conclusão

Configurações padrão de HPA e VPA muitas vezes deixam a desejar quando o objetivo é manter, ao mesmo tempo, alta disponibilidade e eficiência de custo. Ao combinar a observabilidade granular do eBPF e do cAdvisor com um escalonamento horizontal/vertical coordenado por uma máquina de estados unificada — e aplicar as mudanças com redimensionamento in-place de pods —, as organizações conseguem construir clusters autorrecuperáveis e bem dimensionados, com o mínimo de disrupção.

O PerfectScale é uma implementação inspirada nos padrões arquiteturais descritos aqui. Eu atuo em Forward Deployed Engineering na DoiT, assessorando clientes em AWS e Google Cloud Platform. Fique à vontade para entrar em contato em caso de dúvidas.