Docker Healthcheck: Não confie apenas no status "Up" do seu container
Ver um container com o status "Up" no terminal dá uma sensação de dever cumprido, certo? Nem sempre. No mundo real, um container ativo não significa necessariamente uma aplicação pronta para responder requisições. Neste post, vamos entender o que é o Docker Healthcheck, por que ele é indispensável para evitar "falsos positivos" e como utilizá-lo para sincronizar perfeitamente a inicialização entre o seu Front-end e o seu Back-end.
O perigo do "falso positivo" no terminal
Imagine o seguinte cenário: você acabou de rodar o seu docker compose up -d para testar o projeto localmente ou disparou o pipeline de CI/CD para produção. Você digita docker ps no terminal e vê lá o status: Up 5 seconds.
Confiante de que tudo deu certo, você tenta acessar a aplicação pelo navegador e se depara com um Erro 502 Bad Gateway ou a conexão simplesmente cai.
O que aconteceu? O Docker cumpriu perfeitamente o papel dele: ele iniciou o processo principal do container. Mas se o processo principal for uma JVM carregando o ecossistema do Spring Boot, subindo beans e abrindo conexões com o banco de dados, a aplicação ainda não está pronta para o usuário final, mesmo que o Docker diga que ela está "Up".
Esse intervalo invisível entre o container subir e a aplicação de fato inicializar é um perigo silencioso, especialmente quando temos serviços que dependem uns dos outros.
Como sincronizar a inicialização do Front e Back-end
Se você trabalha em projetos full-stack, com certeza já colocou a aplicação inteira para rodar em um único arquivo docker-compose.yml. E é aí que o problema do tempo de inicialização (warmup) fica evidente.
Você adiciona o Front-end e o Back-end no mesmo arquivo e pensa: "Quero que o Front só suba depois que o Back estiver pronto". Para resolver isso, a primeira reação de quase todo desenvolvedor é usar a propriedade depends_on.
# A armadilha comum:
services:
meu-frontend:
image: alexsousadev/meu-frontend:latest
depends_on:
# O Docker olha apenas o status do container, não da aplicação interna
- meu-backend
Por padrão, o depends_on tradicional apenas garante a ordem de inicialização dos containers. Ele vê que o container do Back-end mudou para o status "Up" e imediatamente liga o Front-end.
Como o Back-end ainda vai demorar alguns segundos (as vezes minutos) valiosos para estar pronto, o Front-end tenta fazer as primeiras requisições de inicialização, não encontra a API disponível e quebra. O Docker achou que estava tudo bem, mas o sistema falhou.
A Solução Definitiva: Docker Healthcheck + service_healthy
O Docker Healthcheck é uma instrução que inserimos na configuração para dizer ao Docker: "Não olhe apenas se o processo está vivo. Execute este comando específico de tempos em tempos para validar se a aplicação lá dentro está realmente rodando".
Combinando o Healthcheck no Back-end com uma condição avançada de depends_on no Front-end, o Docker segura o Front na fila até que o Back-end ok.
Veja como fica essa estrutura prática no seu docker-compose.yml:
version: '3.8'
services:
meu-backend:
image: alexsousadev/api-spring-boot:latest
ports:
- "8080:8080"
healthcheck:
# Comando que será executado dentro do container para testar a
# aplicação
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s # O tempo para o Java inicializar
# O frontend aguarda o backend inicializar
meu-frontend:
image: alexsousadev/front-nextjs:latest
ports:
- "3000:3000"
depends_on:
meu-backend:
condition: service_healthy # Aqui está o pulo do gato!
Entendendo os parâmetros do Healthcheck:
test: É o comando executado dentro do container. O curl -f faz a requisição falhar silenciosamente se o status HTTP não for de sucesso (família 200). Aqui, usamos o endpoint do Spring Boot Actuator (/actuator/health), que já monitora nativamente a saúde da nossa API e do banco de dados.
interval: O Docker vai repetir esse teste a cada 10 segundos.
timeout: Se a requisição demorar mais de 5 segundos para responder, o Docker considera como uma falha.
retries: Se o teste falhar 3 vezes seguidas, o status do container muda oficialmente para unhealthy.
start_period: Esse é o parâmetro mais importante para quem desenvolve em Java. Ele dá um intervalo inicial (neste caso, 20 segundos) para a aplicação inicializar antes que as falhas do Healthcheck comecem a contar para o limite de tentativas.
Enquanto o Back-end está subindo, o status do container no docker ps exibirá Up 10 seconds (health: starting) e o Front-end continuará aguardando. Assim que o Spring Boot responder ao curl, o status muda para healthy e o Docker finalmente inicializa o Front-end.
Conclusão
Implementar o Docker Healthcheck remove o misticismo do deploy e traz previsibilidade para a sua infraestrutura. Se você utiliza ferramentas de orquestração como o Docker Swarm ou Kubernetes (que usa os famosos Liveness e Readiness Probes), esse mapeamento é o que garante que o ecossistema saiba quando reiniciar um container travado ou quando direcionar o tráfego de usuários com segurança.
Desenvolver com mentalidade de produção significa garantir que seu software saiba reportar o próprio estado de saúde.
E você? Já passou pelo problema de ver o container ativo mas a aplicação fora do ar? Sabia que dava para sincronizar o Front e o Back de forma tão limpa usando o service_healthy? Deixe sua experiência aqui nos comentários!