Marcio Cunha

Container reiniciando sozinho? Como debugar o Exit Code no Docker

Descubra o significado real dos códigos de saída do Docker como 137, 139 e 111. Aprenda a rastrear o comportamento do PID 1 e falhas de memória para impedir quedas.

Marcio Cunha•5 min
Também disponível em:EnglishEspañol
Resumo
  • O código de saída 137 indica que o processo principal estourou o limite de memória e foi finalizado pelo kernel do Linux.
  • O código 139 aponta uma falha de segmentação, ocorrendo quando o programa tenta acessar um espaço de memória inválido.
  • O PID 1 dentro do container tem a responsabilidade de gerenciar sinais do sistema operacional e processos filhos.
  • Ferramentas de observabilidade como o rastreio do OOM Killer ajudam a diagnosticar falhas de hardware e software.
  • O uso correto de comandos de parada e health checks evita loops de reinicialização indesejados em produção.

Por que seu container Docker está reiniciando sozinho?

Quando um container Docker fecha de repente e entra em um loop infinito de reinicializações, a primeira reação costuma ser o desespero. Na prática, o Docker não desliga um sistema por capricho; ele sempre deixa uma pista valiosa sobre o que aconteceu logo antes do encerramento. Essa pista é o chamado Exit Code, ou código de saída, um número retornado pelo processo quando ele para de funcionar. Compreender esses números transforma um erro misterioso em um diagnóstico claro, permitindo que você resolva a raiz do problema em vez de apenas reiniciar o serviço rezando para que ele dure mais tempo.

Para investigar a fundo, precisamos olhar além do comando básico 'docker ps'. O motor do Docker conversa diretamente com o núcleo do sistema operacional (o kernel) para gerenciar o isolamento dos processos. Quando algo sai do controle — seja falta de memória RAM, um erro de programação de baixo nível ou uma porta que recusou conexões —, o kernel intervém e encerra a aplicação abruptamente. Antes de aprender a analisar os detalhes, vale lembrar que gerenciar falhas de infraestrutura exige paciência e o uso de ferramentas adequadas, assim como quando investigamos falhas físicas de hardware, conforme discutido em nosso guia sobre como testar circuito integrado na placa com osciloscópio.

Desvendando os códigos de saída mais comuns: 137, 139 e 111

O código de saída é um número inteiro que o programa entrega ao sistema operacional ao terminar sua execução. No universo Docker, alguns números aparecem com tanta frequência que merecem um lugar cativo na memória de qualquer engenheiro. O famigerado código 137, por exemplo, é o clássico sinal de que o processo foi sumariamente executado por falta de memória. Na prática, isso significa que a aplicação pediu mais RAM do que o container tinha direito, e o OOM Killer (mecanismo do Linux que mata processos para salvar o sistema) cortou o mal pela raiz. Outro código frequente é o 139, que representa uma falha de segmentação, ocorrendo quando um software tenta ler ou escrever em um endereço de memória proibido ou corrompido.

Já o código 111 costuma aparecer em cenários de conexão recusada, indicando que a aplicação tentou conversar com um banco de dados ou API externa que ainda não estava pronta ou aceitando conexões naquela porta específica. Para inspecionar o código de saída exato de um container que acabou de morrer, você pode executar o comando abaixo no terminal:

docker inspect --format='{{.State.ExitCode}}' nome_do_container

Esse comando simples lê diretamente a estrutura de metadados do Docker e devolve o número exato, poupando tempo na investigação e direcionando você diretamente para a categoria correta do erro.

O papel crítico do PID 1 e o gerenciamento de sinais

Dentro de um container, o primeiro processo iniciado recebe o identificador PID 1. Na teoria dos sistemas Unix, o PID 1 carrega uma responsabilidade imensa: ele funciona como o 'chefe' de todos os outros processos gerados ali dentro, sendo o encarregado de recolher processos órfãos e repassar sinais vitais, como o comando de encerramento (SIGTERM). Quando criamos arquivos Dockerfile incorretamente, muitas vezes colocamos um script simples ou um interpretador no PID 1 que não sabe lidar com sinais do sistema operacional. O resultado prático disso é que, quando você tenta parar o container, ele simplesmente ignora o aviso educado e precisa ser eliminado à força após dez segundos de espera.

Para evitar esse comportamento indesejado, é fundamental utilizar o formato de instrução 'exec' no seu Dockerfile ou contar com gerenciadores de processos leves que saibam repassar os sinais corretamente. Quando um container trava por completo e não responde nem aos comandos normais, o operador precisa recorrer a ferramentas de força bruta na interface de linha de comando, um cenário que abordamos detalhadamente em como forçar a parada de um container travado via CLI. Garantir que o PID 1 seja um processo consciente do seu papel evita corrupção de dados e quedas abruptas de serviços críticos.

Rastreando logs do kernel e identificando o OOM Killer

Muitas vezes, os logs tradicionais da sua aplicação (obtidos com 'docker logs') param de repente e não mostram nenhum erro explícito antes da queda. Isso acontece porque a falha ocorreu fora do alcance da linguagem de programação, direto no nível do sistema operacional. Quando o consumo de memória ultrapassa o teto permitido pelo cgroup do container, o kernel do Linux age em silêncio. Para confirmar se o seu container foi vítima dessa execução sumária, você precisa consultar o registro geral de eventos do sistema hospedeiro usando o comando dmesg no terminal do servidor:

dmesg -T | grep -i oom

Esse comando filtra o histórico do kernel exibindo os eventos de falta de memória acompanhados de carimbos de data e hora legíveis. Ao analisar a saída, você verá exatamente qual processo foi sacrificado e qual era o consumo de memória naquele instante exato. Identificar esse padrão permite ajustar os limites de recursos no seu arquivo Docker Compose, evitando novas interrupções indesejadas no ambiente de produção.

Como evitar loops de reinicialização e garantir resiliência

Descobrir o motivo da queda é apenas metade do caminho; a outra metade é garantir que o sistema se mantenha estável e previsível. Políticas de reinicialização automáticas como 'restart: always' são ótimas para manter serviços no ar após uma falha repentina, mas podem mascarar problemas estruturais graves, criando um ciclo vicioso de consumo de CPU e reincidência de erros. O ideal é combinar limites de memória realistas, estratégias de monitoramento adequadas e testes de carga para validar o comportamento da aplicação antes de levá-la para o ambiente de produção.

Em suma, dominar a leitura dos códigos de saída e entender a dinâmica entre o container e o kernel do Linux elimina a adivinhação na hora de debugar problemas de infraestrutura. Ao monitorar o PID 1, interpretar corretamente códigos como 137 e 139, e consultar o dmesg quando necessário, você ganha autonomia total para diagnosticar e corrigir qualquer falha em seus ambientes conteinerizados com rapidez e precisão.