Requests e limits no Kubernetes: o que acontece quando você chuta

Requests e limits decidem agendamento, despejo e throttling no Kubernetes. O que cada um faz de verdade, por que limit de CPU costuma atrapalhar e como medir.

Equipe Solvefy 7 min de leitura

Dois campos de YAML, dois números cada, e uma quantidade desproporcional de incidentes. Requests e limits são a parte do Kubernetes em que o palpite tem consequência direta: aplicação morta sem explicação, latência que aparece sem carga, nó lotado com CPU ociosa, fatura de cloud com o dobro do necessário.

O problema raramente é falta de conhecimento do que os campos significam. É que o comportamento real deles é assimétrico — CPU e memória se comportam de formas completamente diferentes quando o limite é atingido —, e essa assimetria não é óbvia.

O que cada um faz, sem rodeio

Request é uma reserva. É o que o agendador usa para decidir em qual nó o pod cabe. Ele soma os requests de tudo que já está no nó e verifica se o novo pod cabe na capacidade. Request não limita o que o container consome — é uma promessa de disponibilidade, não um teto.

Limit é um teto. É o máximo que o container pode consumir. E é aqui que a assimetria aparece:

RecursoAo ultrapassar o limitConsequência
CPUO container é estrangulado (throttling)Fica lento; não morre
MemóriaO container é mortoReinicia com OOMKilled

CPU é compressível: o sistema simplesmente dá menos fatias de tempo. Memória não é: não existe "um pouco menos de memória" — ou o processo tem, ou o kernel mata alguém.

Guardar essa tabela resolve metade dos problemas de dimensionamento.

Classes de QoS: a consequência que ninguém configura de propósito

O Kubernetes classifica cada pod em uma de três classes, derivadas do que você escreveu. Você não escolhe a classe; você a recebe.

ClasseComo se obtémO que significa na prática
GuaranteedRequest igual a limit, em CPU e memória, em todos os containersÚltima a ser despejada quando o nó fica sem memória
BurstableTem request, e limit diferente ou ausenteDespejo intermediário
BestEffortSem request nem limitPrimeira a morrer

A consequência prática: BestEffort em produção é uma escolha ruim tomada por omissão. Quando o nó fica sob pressão de memória, esses pods são os primeiros a ser despejados — e eles são justamente os que ninguém configurou, ou seja, quase sempre os que ninguém está monitorando.

Para cargas realmente críticas — um banco de dados, um componente de infraestrutura —, Guaranteed vale o desperdício. Para a maioria das aplicações, Burstable bem dimensionado é o equilíbrio certo.

A polêmica do limit de CPU

Esta é a recomendação que mais gera discussão, e vale explicar o raciocínio em vez de só afirmar.

O throttling de CPU no Linux funciona por janelas curtas de tempo. Se o container atinge a cota dele dentro da janela, ele para até a janela seguinte — mesmo que a máquina inteira esteja ociosa. Para uma aplicação que faz picos curtos de processamento (praticamente toda API que serve requisição HTTP), isso produz latência de cauda ruim sem nenhuma causa visível: o gráfico de uso de CPU mostra consumo baixo, e o percentil 99 de latência está péssimo.

Por isso a orientação que funciona na maioria dos casos:

  • Sempre defina request de CPU. É o que garante a fatia mínima e o agendamento correto.
  • Considere não definir limit de CPU para cargas latência-sensíveis, deixando o container aproveitar a capacidade ociosa do nó.
  • Sempre defina request e limit de memória, e iguais. Memória não é compressível; deixar sem teto é aceitar que um vazamento derrube o nó inteiro.

A ressalva honesta: sem limit de CPU, um container com defeito pode consumir a sobra do nó e prejudicar os vizinhos. Se o ambiente é compartilhado entre times que não se conhecem, ou entre clientes, o limit passa a ser necessário — e aí o dimensionamento precisa ser generoso e a métrica de throttling precisa estar no painel.

A métrica que resolve a discussão no seu ambiente é uma só: percentual de tempo em throttling por container. Se está alto, o limit está apertado, e o gráfico de uso médio de CPU não vai te contar isso.

Como medir em vez de chutar

Quatro passos, nessa ordem:

  1. Suba sem limit e com request conservador, em homologação com carga representativa. O objetivo é observar, não acertar de primeira.
  2. Colete o percentil 95 de uso de CPU e memória ao longo de um ciclo completo — incluindo o pico do dia, o fechamento do mês, o processamento noturno. Média engana; o que dimensiona é o pico sustentado.
  3. Defina request no percentil 95 de uso. É a reserva que a aplicação realmente precisa.
  4. Defina limit de memória com folga sobre o pico observado — o suficiente para absorver variação, pequeno o bastante para que um vazamento seja contido.

E depois: revise. Aplicação muda, carga muda, e o dimensionamento de seis meses atrás não descreve mais a realidade. Uma revisão trimestral dos pods que mais consomem paga o tempo investido.

O custo do request inflado

Request alto demais não causa incidente — causa fatura. E é o desperdício mais comum que encontramos.

Como o agendador reserva pelo request, um pod que pede 2 CPUs e usa 0,2 ocupa 2 CPUs de capacidade do nó. Repita isso por cinquenta pods e você tem um cluster que parece cheio, com nós em 15% de uso real, e um autoscaler comprando máquina nova.

O sintoma é inconfundível: nós "sem capacidade" para agendar novos pods, com uso real baixo nos gráficos. Quando isso aparece, o problema não é falta de nó — é request inflado, e a correção devolve capacidade imediatamente sem comprar nada.

O número que vale acompanhar é a relação entre o que foi reservado e o que é usado, por namespace. Ele aponta o desperdício direto.

Governança: LimitRange e ResourceQuota

Confiar que todo time vai preencher os campos corretamente não escala. Duas ferramentas do próprio Kubernetes resolvem:

  • LimitRange, por namespace, define valores padrão para quem não declarou nada e estabelece mínimo e máximo permitidos. Ele elimina o pod BestEffort acidental: sem declaração, o padrão é aplicado.
  • ResourceQuota, por namespace, limita o total de CPU, memória e número de objetos que aquele time pode consumir. É o que impede um namespace de engolir o cluster.

Em cluster compartilhado, os dois são obrigatórios. Sem eles, o primeiro time que escrever um número errado afeta todos os outros.

Diagnóstico rápido dos sintomas mais comuns

SintomaCausa provávelCorreção
Pod reiniciando com OOMKilledLimit de memória abaixo do pico realMedir o pico e ajustar o limit
Latência alta com CPU baixa no gráficoThrottling de CPUAumentar ou remover o limit de CPU
Pod preso em PendingRequest maior do que cabe em qualquer nóReduzir o request ou aumentar o nó
Pod despejado sob pressãoQoS BestEffort ou Burstable com request baixoDefinir requests; LimitRange no namespace
Nós "cheios" com uso real baixoRequest infladoRedimensionar pelo percentil 95 medido
Nó inteiro instávelContainer sem limit de memória vazandoLimit de memória em tudo

Erros comuns

  • Subir em produção sem request nem limit, caindo em BestEffort por omissão.
  • Copiar valores de um exemplo da internet sem medir a própria carga.
  • Dimensionar pela média em vez do percentil de pico.
  • Limit de CPU apertado em API latência-sensível.
  • Container sem limit de memória, com vazamento derrubando o nó.
  • Request inflado "por segurança", pagando por capacidade ociosa.
  • Namespace compartilhado sem LimitRange nem ResourceQuota.
  • Definir uma vez e nunca revisar.
  • Não monitorar throttling, e diagnosticar latência olhando só uso médio de CPU.
  • Tratar OOMKilled como problema de aplicação sem verificar o limit.

O resumo que cabe num post-it

Request de CPU sempre; limit de CPU com cautela e monitorando throttling. Request e limit de memória sempre, e iguais. Dimensione pelo percentil 95 medido, não pela média nem pelo palpite. LimitRange e ResourceQuota em todo namespace compartilhado. Revise a cada trimestre.

Onde a Solvefy/Cloud entra

Dimensionamento de recursos é uma das primeiras coisas que revisamos em consultoria de Kubernetes, porque costuma render ganho imediato dos dois lados: estabilidade, cortando OOMKilled e throttling escondido, e custo, devolvendo a capacidade travada por request inflado. Implantamos a medição, ajustamos os valores com dados do seu ambiente, colocamos LimitRange e ResourceQuota por namespace e deixamos o painel que mostra reservado contra usado — para a revisão seguinte ser sua.

Seu cluster parece cheio com os nós ociosos? Diagnóstico gratuito, sem compromisso, em solvefy.cloud.

Solvefy/Cloud — Infraestrutura que cresce com você.

Faça agora o seu diagnóstico e orçamento

Inicie com um diagnóstico gratuito da sua infraestrutura e receba uma proposta personalizada — com escopo, cronograma e valores.