Resolver um Pod em CrashLoopBackOff
CrashLoopBackOff não é um erro: é o kubelet informando que o container morreu várias
vezes e que ele está esperando cada vez mais entre as tentativas (10s, 20s, 40s… até 5
minutos). A causa real está no que aconteceu antes do crash.
Siga os passos na ordem. Cada um elimina uma classe de causa.
1. Ler o log da execução anterior
Seção intitulada “1. Ler o log da execução anterior”O container atual pode ter acabado de nascer e ainda não ter escrito nada. O que você quer é o log de quem morreu:
kubectl logs <pod> -n <ns> --previousCom mais de um container no Pod, aponte qual:
kubectl logs <pod> -n <ns> -c <container> --previousSe aparecer stack trace ou mensagem de erro de configuração, você já tem a resposta. Vá direto para o passo 6.
Se o log vier vazio, o processo morreu antes de conseguir escrever — o que aponta para os passos 2 e 3.
2. Ler o exit code
Seção intitulada “2. Ler o exit code”kubectl get pod <pod> -n <ns> \ -o jsonpath='{.status.containerStatuses[0].lastState.terminated}' | jq| Exit code | O que significa | Próximo passo |
|---|---|---|
0 |
O processo terminou com sucesso e não voltou | Passo 5 |
1 |
Erro da aplicação | Passo 1, com mais atenção |
126 |
Comando existe mas não é executável | Passo 4 |
127 |
Comando não encontrado | Passo 4 |
137 |
SIGKILL — quase sempre OOMKilled |
Passo 3 |
139 |
SIGSEGV — falha de segmentação |
Bug na aplicação ou biblioteca nativa |
143 |
SIGTERM — encerramento solicitado |
Passo 5 |
O campo reason no mesmo objeto costuma dizer OOMKilled explicitamente quando é o caso.
3. Descartar falta de memória
Seção intitulada “3. Descartar falta de memória”kubectl get pod <pod> -n <ns> \ -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'Se voltar OOMKilled, veja o consumo real antes de mexer no limite:
kubectl top pod <pod> -n <ns> --containersDuas verificações antes de simplesmente aumentar o número:
- A aplicação enxerga o limite do container? Runtimes com heap gerenciado (JVM, .NET,
Node com
--max-old-space-size) dimensionam o heap pela memória visível. Sem configuração explícita, muitos olham para a memória do nó e estouram o limite do cgroup. Configure-XX:MaxRAMPercentage=75ou o equivalente. - É pico ou vazamento? Se o consumo cresce sempre até morrer, aumentar o limite só aumenta o intervalo entre os crashes.
4. Descartar erro de comando ou imagem
Seção intitulada “4. Descartar erro de comando ou imagem”Exit 126 ou 127 significa que o container nem chegou a executar sua aplicação.
# O que a imagem realmente define como entrypoint e cmd?kubectl get pod <pod> -n <ns> -o jsonpath='{.spec.containers[0].command} {.spec.containers[0].args}'docker inspect <imagem> --format '{{.Config.Entrypoint}} {{.Config.Cmd}}'Causas típicas:
commandno manifest sobrescrevendo o entrypoint com um caminho que não existe naquela imagem.- Imagem distroless ou
scratch: não existesh, então qualquercommand: ["sh", "-c", ...]falha com 127. - Arquitetura errada: imagem
amd64num nóarm64. A mensagem costuma serexec format error. - Script copiado sem bit de execução — 126.
5. Descartar probe mal configurada
Seção intitulada “5. Descartar probe mal configurada”Se o exit code for 0 ou 143, provavelmente não foi a aplicação que decidiu morrer:
ela foi encerrada.
kubectl describe pod <pod> -n <ns> | grep -A20 "Liveness\|Events"Procure eventos como Liveness probe failed seguidos de Killing container.
O padrão clássico: a aplicação leva 40 segundos para subir, a livenessProbe começa a
verificar aos 10 segundos e reprova três vezes. O kubelet reinicia. O ciclo se repete
para sempre, e a aplicação nunca chega a ficar pronta.
A correção certa é uma startupProbe, que suspende a liveness até a aplicação subir:
startupProbe: httpGet: { path: /healthz, port: http } periodSeconds: 5 failureThreshold: 30 # tolera até 150s de partida
livenessProbe: httpGet: { path: /healthz, port: http } periodSeconds: 10 failureThreshold: 36. Confirmar a hipótese fora do ciclo
Seção intitulada “6. Confirmar a hipótese fora do ciclo”Antes de corrigir, valide. Rode a mesma imagem sem o ciclo de reinício atrapalhando:
kubectl run diagnostico -n <ns> --rm -it --restart=Never \ --image=<a-mesma-imagem> \ --overrides='{"spec":{"containers":[{"name":"diagnostico","image":"<a-mesma-imagem>","command":["sh"],"stdin":true,"tty":true}]}}' \ -- shPara imagem sem shell, use um container efêmero no Pod que está falhando — ele compartilha o namespace de rede e de processos:
kubectl debug -it <pod> -n <ns> --image=nicolaka/netshoot --target=<container> -- bashDentro dele, env mostra as variáveis reais, ls -la mostra o que foi montado e
cat /proc/1/cmdline mostra o comando que de fato executou.
7. Parar o ciclo enquanto investiga
Seção intitulada “7. Parar o ciclo enquanto investiga”Se o crash constante está poluindo logs e alertas, congele o Pod substituindo o comando por algo que não morre:
kubectl patch deployment <deploy> -n <ns> --type=json -p='[ {"op":"replace","path":"/spec/template/spec/containers/0/command","value":["sleep","infinity"]}]'O Pod sobe, você entra nele e investiga com calma. Reverta antes de sair — um
Deployment esquecido em sleep infinity é um serviço fora do ar que parece saudável.
As causas por frequência
Seção intitulada “As causas por frequência”Depois de muitos casos, a distribuição costuma ser esta:
- Configuração ausente ou errada — variável de ambiente, chave de Secret com nome diferente, ConfigMap não montado. De longe a maior fatia.
- Dependência indisponível na partida — a aplicação tenta conectar, falha e sai.
Trate com retry e backoff, não com
sleepno entrypoint. - OOMKilled — limite baixo, ou runtime que não enxerga o cgroup.
- Probe mal configurada — liveness sem startup em aplicação de partida lenta.
- Imagem errada — arquitetura, tag ou entrypoint.
Leituras relacionadas
Seção intitulada “Leituras relacionadas”- Diagnóstico em Kubernetes — o roteiro completo, incluindo Service e nó
- Workloads — probes, requests e limits em detalhe
- Sinais e exit codes
- Deployment de produção — as três probes configuradas corretamente