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:
| Recurso | Ao ultrapassar o limit | Consequência |
|---|---|---|
| CPU | O container é estrangulado (throttling) | Fica lento; não morre |
| Memória | O container é morto | Reinicia 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.
| Classe | Como se obtém | O que significa na prática |
|---|---|---|
| Guaranteed | Request igual a limit, em CPU e memória, em todos os containers | Última a ser despejada quando o nó fica sem memória |
| Burstable | Tem request, e limit diferente ou ausente | Despejo intermediário |
| BestEffort | Sem request nem limit | Primeira 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:
- Suba sem limit e com request conservador, em homologação com carga representativa. O objetivo é observar, não acertar de primeira.
- 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.
- Defina request no percentil 95 de uso. É a reserva que a aplicação realmente precisa.
- 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
| Sintoma | Causa provável | Correção |
|---|---|---|
Pod reiniciando com OOMKilled | Limit de memória abaixo do pico real | Medir o pico e ajustar o limit |
| Latência alta com CPU baixa no gráfico | Throttling de CPU | Aumentar ou remover o limit de CPU |
Pod preso em Pending | Request maior do que cabe em qualquer nó | Reduzir o request ou aumentar o nó |
| Pod despejado sob pressão | QoS BestEffort ou Burstable com request baixo | Definir requests; LimitRange no namespace |
| Nós "cheios" com uso real baixo | Request inflado | Redimensionar pelo percentil 95 medido |
| Nó inteiro instável | Container sem limit de memória vazando | Limit 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
OOMKilledcomo 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ê.