Aprenda / Infraestrutura — Docker

IntermediárioAula 11 de 1615 min0% da trilha

Healthcheck e restart policy

Um container 'rodando' e um container funcionando não são a mesma coisa — e o que o Docker faz quando ele cai depende de uma configuração que não vem ligada por padrão.

docker ps mostra "Up 3 hours" pra um container travado, sem responder nenhuma requisição, do mesmo jeito que mostra pra um saudável — o processo continua vivo, só não faz mais nada útil. Docker, por padrão, não sabe diferenciar essas duas situações. HEALTHCHECK e restart policy juntos resolvem isso: um define como detectar que algo está errado, o outro define o que fazer a respeito.

HEALTHCHECK no Dockerfile

Dockerfile (trecho)
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1
anotado
--interval   de quanto em quanto tempo rodar a checagem (aqui: a cada 10s)
--timeout    quanto tempo esperar a checagem responder antes de considerar falha
--retries    quantas falhas seguidas até marcar o container como "unhealthy"
CMD          o comando que decide sucesso/falha pelo código de saída (0 = saudável, qualquer outro = falha)

Com isso, docker ps passa a mostrar (healthy), (unhealthy) ou (health: starting) ao lado do container — informação que antes simplesmente não existia.

Healthcheck no compose (não precisa estar só no Dockerfile)

docker-compose.yml (trecho)
services:
  api:
    build: .
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 3

Declarar no compose é útil quando a mesma imagem é reusada em contextos diferentes e você quer um healthcheck específico pra um deploy — o valor do compose sobrescreve o que estiver no Dockerfile.

restart policy: o que fazer quando o container cai

opções de restart
no              nunca reinicia sozinho (padrão quando "restart" não é declarado)
always          sempre reinicia, mesmo depois de "docker stop" seguido de reinício do daemon Docker
on-failure      só reinicia se o processo sair com código de erro (diferente de 0) — saída limpa não reinicia
unless-stopped  como "always", mas respeita um "docker stop" manual: se você parou de propósito, não volta sozinho
docker-compose.yml (trecho)
services:
  api:
    build: .
    restart: unless-stopped

Em produção, unless-stopped é o mais comum: se o container crashar, reinicia sozinho; se alguém (ou um script de deploy) parar o container de propósito, ele não fica ressuscitando no meio de uma manutenção. on-failure faz sentido pra jobs que devem rodar uma vez e terminar — reiniciar só importa se deram erro.

depends_on com condition: service_healthy

Um depends_on simples (o que a trilha de fundamentos usou) só espera o container do outro serviço iniciar — não que ele esteja pronto pra aceitar conexão. Um Postgres recém-iniciado ainda gasta um tempo inicializando antes de aceitar conexões; uma API que sobe rápido demais pode tentar conectar antes disso e cair. Combinando depends_on com o healthcheck do banco, dá pra esperar de verdade.

docker-compose.yml
services:
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy   # espera "db" ficar healthy, não só "started"

  db:
    image: postgres:16-alpine
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      retries: 5

Exercício

Um worker de fila (processa jobs em background) tem restart: on-failure e termina normalmente (código 0) depois de processar todos os jobs pendentes uma única vez, por design. Depois de um deploy, o time percebe que o worker não está mais rodando — docker ps -a mostra ele como "Exited (0)". Isso é um bug do restart policy?

Quiz rápido

Qual a diferença entre restart: on-failure e restart: unless-stopped?
Por que depends_on com condition: service_healthy é diferente do depends_on simples?