Kubernetes requests vs limits: como isso afeta scheduling e throttling
Tabela de Conteúdo
Muitas vezes o pod esta sendo morto por OOM ou a aplicação fica lenta por conta de falta de recursos.
Isso geralmente acontece por conta de requests/limits mal configurados.
A ideia é simples:
requests= reserva que o scheduler usa pra decidir onde alocarlimits= teto que o container não pode passar
Classes de QoS (o que o K8s usa internamente)
Guaranteed (requests == limits)
- Garantia de recursos
- Não sofre limitação de CPU
- Se estourar memória, OOMKill
- Critérios: todos os containers devem ter requests == limits para CPU e memory
Burstable (requests < limits)
- Pode ser “preemptado” por pods Guaranteed
- CPU pode ser limitada se o nó estiver lotado
- Memória: OOM se passar do limit
- Critérios: pelo menos um container com request ou limit (mas não todos iguais)
BestEffort (sem requests/limits)
- Primeiro a ser “preemptado” se o nó precisar de recursos
- CPU pode ser quase zero se o nó estiver sob pressão
- OOMKill se o nó precisar de memória
- Critérios: nenhum container com requests ou limits
Troubleshooting do dia a dia
kubectl top pod -A
kubectl describe pod -n <ns> <pod>
kubectl describe node <node>
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -n 30
Procure por:
Killing container with id ...(OOM)Throttling(limitação de CPU)Insufficient cpu/memory(preemption)
Erros comuns que podem ser evitados
- Usar só limits e virar BestEffort sem querer
- Setar requests muito altos e desperdiçar recursos
- Não setar limits de memória e tomar OOMKill no nó
- CPU limits muito baixos e a aplicação ficar lenta
YAML de exemplo
Aqui temos um exemplo de deployment com requests e limits configurados, onde a reserva é de 128 MiB de memória e 0.1 vCPU, com teto de 256 MiB de memória e 0.5 vCPU.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: nginx:1.27
resources:
requests:
# reserva 128 MiB (o scheduler usa isso pra alocar)
memory: "128Mi"
# reserva 0.1 vCPU (100 millicores)
cpu: "100m"
limits:
# teto de 256 MiB
memory: "256Mi"
# teto de 0.5 vCPU (se passar, sofre limitação)
cpu: "500m"
Quando usar cada classe
Guaranteed
- Apps críticos que precisam de performance garantida
- Serviços com carga previsível
- Quando você pode pagar pelos recursos garantidos
Burstable
- Maioria das aplicações web
- Serviços com carga variável
- Quando você quer flexibilidade com algum controle
BestEffort
- Jobs batch que podem ser reiniciados
- Apps de baixa prioridade
- Quando recursos não são críticos
Comandos úteis
# Ver classe QoS dos pods
kubectl get pod -o custom-columns=NAME:.metadata.name,QOS:.status.qosClass
# Ver uso de recursos por nó
kubectl describe node <node> | grep -A 10 "Allocated resources"
# Ver pods que estão consumindo mais que o pedido
kubectl top pod -n <ns> --sort-by=cpu
kubectl top pod -n <ns> --sort-by=memory
# Ver eventos de OOM
kubectl get events -A --sort-by=.lastTimestamp | grep "Killing"
Boas práticas
- Se você setar limits, SEMPRE sete requests também
- Para apps críticos, considere Guaranteed (requests == limits)
- Para apps “batch” ou de baixa prioridade, Burstable costuma ser suficiente
- Monitore o uso real e ajuste conforme necessário
Se você quiser entender melhor como rollout e probes interagem com recursos, dá uma olhada neste post: Kubernetes probes: liveness, readiness e startup (sem mistério).
Simples assim! :)