FNSP Web Page

O contentor não arranca: ler o que ele diz antes de morrer

O docker ps não o mostra. O docker ps -a mostra, e diz Exited (127) 2 seconds ago. A partir daqui há uma ordem de perguntas que resolve quase todos os casos — e quase nenhuma delas começa por mexer no Dockerfile.

1. Primeiro, o que ele disse

docker ps -a
docker logs --tail 50 o-meu-contentor

Um pormenor que poupa tempo: os registos de um contentor morto continuam lá até alguém lhe fazer rm. Usar docker run --rm enquanto se depura é apagar a única prova que havia.

2. O código de saída diz mais do que parece

docker inspect --format '{{.State.ExitCode}}' o-meu-contentor
  • 0 — terminou bem. Num serviço, isso é o problema: o processo principal acabou e o contentor foi atrás dele.

  • 1 — erro da aplicação. A resposta está nos registos.

  • 125 — o docker run é que falhou, antes de arrancar seja o que for. Opção mal escrita.

  • 126 — o comando existe mas não é executável. Falta o chmod +x.

  • 127 — comando não encontrado. O clássico: um script com #!/bin/bash numa imagem Alpine, que só traz sh.

  • 137 — morreu com um sinal de morte imediata, quase sempre por falta de memória.

Nesse último caso há uma confirmação que não deixa dúvidas:

docker inspect --format '{{.State.OOMKilled}}' o-meu-contentor
# true

3. ENTRYPOINT e CMD não são a mesma coisa

É a confusão que mais custa a depurar. O CMD é o argumento do ENTRYPOINT, não uma alternativa a ele. Neste site:

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node", "server.js"]

O que corre é docker-entrypoint.sh node server.js. E é por isso que abrir uma consola não funciona como se espera:

docker run -it a-minha-imagem sh
# substitui o CMD, mas o entrypoint corre na mesma — e recebe "sh"

docker run -it --entrypoint sh a-minha-imagem
# assim sim

4. O exec que quase toda a gente esquece

Este é o erro silencioso desta história, porque não impede o arranque — estraga a paragem. O entrypoint deste site acaba assim:

set -e

node /app/cli/node_modules/prisma/build/index.js migrate deploy

exec "$@"

Sem aquele exec, o script continuaria a ser o processo número um e a aplicação seria um filho dele. Quando o Docker mandasse parar, o sinal ia para o script, que não faz nada com ele — e dez segundos depois o Docker matava tudo à força, a meio do que estivesse a acontecer.

Com o exec, a aplicação substitui o script e passa a receber os sinais em primeira mão.

O set -e da primeira linha é o par disto: uma migração falhada impede o arranque, em vez de deixar subir um servidor contra um esquema errado.

5. Entrar num contentor que não fica de pé

O docker exec não serve: precisa de um contentor vivo. A saída é arrancar outro com a mesma imagem e uma consola no lugar do programa:

docker run -it --rm --entrypoint sh a-minha-imagem

# lá dentro, correr à mão aquilo que o entrypoint corria
ls -l /app
node server.js

Quase sempre o erro aparece ali à primeira tentativa, com uma mensagem que os registos tinham cortado.

6. De pé não quer dizer pronto

Um contentor pode estar a correr e não servir nada. É para isso que existe o healthcheck — e o parâmetro que mais vezes falta é o do tempo de arranque:

HEALTHCHECK --interval=30s --timeout=3s --start-period=25s --retries=3 \
  CMD wget -qO- http://127.0.0.1:3000/healthz >/dev/null || exit 1

Sem o --start-period, um arranque que demore vinte segundos é declarado doente antes de chegar a responder, e o contentor entra num ciclo de reinícios que parece uma avaria e é só impaciência.

O resumo

Registos primeiro, código de saída a seguir. O 127 é um interpretador que não existe, o 126 é uma permissão, o 137 é memória, e o 0 num serviço quer dizer que ele achou que tinha acabado. Se nada disso explicar, arranca-se a imagem com uma consola e corre-se à mão aquilo que ela tentava fazer sozinha.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.