Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 30

Docker Compose Health Checks

Recommended resources

Olá! Bem-vindo de volta ao nosso curso.

Introdução

Na lição anterior, você aprendeu a usar a propriedade depends_on com condições como service_healthy para orquestrar a ordem de inicialização dos seus serviços. Vimos que simplesmente garantir que um contêiner de banco de dados inicie antes da sua API não é suficiente; precisamos garantir que ele esteja pronto para aceitar conexões.

Nesta lição, vamos nos aprofundar no mecanismo que torna isso possível: a diretiva healthcheck. Você aprenderá a implementar verificações de saúde personalizadas em seus serviços no Docker Compose. Isso lhe dará o poder de definir precisamente o que significa "saudável" para cada componente da sua aplicação, desde um banco de dados PostgreSQL até a sua API .NET. Ao final, você será capaz de construir sistemas multi-contêiner verdadeiramente robustos que iniciam de forma previsível e resiliente.


1. Anatomia de uma Verificação de Saúde (healthcheck)

A diretiva healthcheck é adicionada à definição de um serviço em seu arquivo docker-compose.yml. Ela instrui o Docker Engine a executar um comando específico periodicamente dentro do contêiner para verificar seu status. Se o comando retornar um código de saída 0, o serviço é considerado healthy. Se retornar um código diferente de 0, é uma falha.

Uma configuração completa de healthcheck possui várias opções que permitem um controle fino sobre seu comportamento.

Docker Compose Health Checks: An Easy-to-follow Guide

Para ter uma visão geral completa das opções de configuração do healthcheck, estude a seção correspondente neste guia do Last9.io.

Leia a seção "Set up Your First Docker Compose Health Check" e preste atenção especial à tabela "Health Check Options Explained". Isso lhe dará uma base sólida sobre o propósito de cada parâmetro.

Aqui está um resumo das opções que você acabou de ler:

Opção Propósito Exemplo
test O comando a ser executado para verificar a saúde do serviço. ["CMD", "curl", "-f", "http://localhost/health"]
interval O tempo de espera entre cada verificação de saúde. 30s
timeout O tempo máximo que o Docker esperará pela conclusão do comando. Se exceder, conta como falha. 10s
retries O número de falhas consecutivas necessárias para que o contêiner seja marcado como unhealthy. 3
start_period Um período de carência após o contêiner iniciar, durante o qual as falhas na verificação de saúde não contam para o número de retries. Essencial para serviços que demoram a inicializar. 60s

O status de saúde de um contêiner passa pelos seguintes estados:

  • starting: O contêiner iniciou e o start_period está ativo. Falhas são ignoradas.
  • healthy: O comando de test retornou um código de saída 0.
  • unhealthy: O número de retries de falhas consecutivas foi atingido.

2. Formatos do Comando test

A propriedade test pode ser definida em alguns formatos diferentes, o que é importante entender, pois afeta como o comando é executado.

How to Use Docker Compose depends_on with Health Checks

Para entender as nuances entre os formatos de teste, leia a seção "Test Formats" do artigo do OneUptime. A distinção entre CMD e CMD-SHELL é particularmente importante.

Concentre-se na subseção "Test Formats". Note como CMD-SHELL permite o uso de operadores de shell, como pipes (|) e &&.

Resumindo:

  • Formato de string: test: "curl -f http://localhost/health || exit 1". Este formato é um atalho para CMD-SHELL.
  • CMD-SHELL: test: ["CMD-SHELL", "curl -f http://localhost/health"]. O comando é executado dentro de um shell (/bin/sh -c), o que permite o uso de variáveis de ambiente do shell, pipes, etc.
  • CMD: test: ["CMD", "curl", "-f", "http://localhost"]. O comando é executado diretamente, sem um shell. É um pouco mais eficiente e seguro, mas não permite funcionalidades do shell.

Na maioria das vezes, CMD é suficiente, mas se você precisar de lógicas mais complexas, como encadear comandos, CMD-SHELL é a ferramenta certa.


3. Exemplos Práticos para Serviços Comuns

A teoria é importante, mas a implementação prática é fundamental. A beleza dos health checks é que existem padrões bem estabelecidos para a maioria dos serviços que você usará. Dado seu background e seus objetivos, vamos focar em exemplos para uma API web, PostgreSQL e RabbitMQ.

Pré-requisito: Ferramentas no Contêiner

Um ponto crucial e uma fonte comum de erros: o executável usado no comando test (por exemplo, curl, pg_isready) deve estar disponível dentro da imagem do contêiner. Imagens mínimas, como alpine, muitas vezes não incluem essas ferramentas por padrão.

Se você precisar de curl em uma imagem baseada em Alpine, adicione esta linha ao seu Dockerfile:

RUN apk add --no-cache curl

Agora, vamos aos exemplos.

How to Use Docker Compose depends_on with Health Checks

Este artigo contém uma excelente "receita de bolo" com comandos de health check para uma variedade de serviços. Recomendo fortemente que você o leia para ter uma referência prática.

Leia a seção "Common Service Health Checks". Preste atenção especial aos exemplos para PostgreSQL, RabbitMQ e HTTP Services, pois eles são diretamente relevantes para o tipo de aplicação que você está construindo.

Com base no que você leu, aqui está um resumo para nossos serviços de interesse:

  • API Web (.NET): Para uma API que expõe um endpoint de saúde (por exemplo, /healthz, uma prática comum com ASP.NET Core Health Checks), curl é a ferramenta perfeita.

    services:
      webapi:
        # ...
        healthcheck:
          test: ["CMD", "curl", "-f", "http://localhost:80/healthz"]
          interval: 10s
          timeout: 5s
          retries: 3
          start_period: 30s
    
  • PostgreSQL: A imagem oficial do Postgres já vem com a ferramenta pg_isready, que foi projetada exatamente para este propósito.

    services:
      db:
        image: postgres:14
        environment:
          POSTGRES_USER: myuser
        # ...
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U myuser -d postgres"]
          interval: 5s
          timeout: 5s
          retries: 5
          start_period: 10s
    
  • RabbitMQ: A imagem de gerenciamento do RabbitMQ inclui ferramentas de diagnóstico que podemos usar.

    services:
      rabbitmq:
        image: rabbitmq:3-management
        healthcheck:
          test: ["CMD", "rabbitmq-diagnostics", "check_running", "-q"]
          interval: 10s
          timeout: 5s
          retries: 5
          start_period: 30s
    

4. Juntando Tudo e Solucionando Problemas

Agora, vamos ver como um docker-compose.yml se parece com tudo isso integrado. Uma API depende de um banco de dados que tem seu próprio health check.

version: '3.8'

services:
  webapi:
    build: ./my-dotnet-api
    ports:
      - "8080:80"
    depends_on:
      db:
        condition: service_healthy # Espera o banco de dados ficar saudável

  db:
    image: postgres:14-alpine
    environment:
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mysecretpassword
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myuser"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 10s

volumes:
  postgres-data:

Ao executar docker-compose up, você verá os logs do contêiner db iniciando e, após alguns segundos, o contêiner webapi começará sua inicialização, exatamente como esperado.

Docker Compose Up Output with Database and API Service Startup
Este é o tipo de saída que você verá no seu terminal. Note como o serviço do banco de dados (`nodejs-postgresql-db-1`) se torna "ready to accept connections" antes que o serviço da API (`quotes-api`) comece a iniciar.

Mas e se não funcionar?

Diagnosticando Falhas de Health Check

  1. Verifique o Status: O comando docker-compose ps (ou docker ps) mostrará o status de saúde dos seus contêineres na coluna STATUS. Você verá algo como (healthy) ou (unhealthy).

  2. Inspecione o Contêiner: O comando docker inspect fornece detalhes ricos sobre a falha.
    docker inspect <container_name_or_id>
    Procure pela seção State.Health. Ela mostrará um log das últimas verificações de saúde, incluindo o código de saída e a saída do comando (stdout/stderr).

Docker Container Health Check Failure Log
Este é um exemplo perfeito de uma falha de health check capturada pelo `docker inspect`. A saída (`Output`) mostra claramente que o erro foi "executable file not found in $PATH", indicando que `curl` não estava instalado na imagem.
  1. Execute o Comando Manualmente: A melhor maneira de depurar é tentar executar o comando de teste você mesmo dentro do contêiner.
    docker-compose exec <service_name> <your_test_command>
    Por exemplo: docker-compose exec db pg_isready -U myuser

Docker Compose Tutorial for Beginners (Networks - Volumes - Secrets - Postgres - Letsencrypt)

Para ver uma demonstração simples de um health check sendo adicionado a um serviço web, assista a este clipe.

Assista do timestamp 53:52 a 55:34. O vídeo mostra a adição de um healthcheck usando curl em uma aplicação Flask e como o depends_on do serviço Nginx espera que o Flask se torne saudável.

Teste seu entendimento!

Você está trabalhando com o seguinte arquivo docker-compose.yml. A api depende do cache (Redis), mas atualmente não há nenhuma garantia de que o Redis esteja pronto quando a API iniciar.

Sua tarefa é modificar o arquivo para que:

  1. O serviço cache tenha um health check apropriado usando o comando redis-cli ping. Use um interval de 5s, retries de 5, e um start_period de 10s.
  2. O serviço api espere até que o cache esteja saudável antes de iniciar.
version: '3.8'

services:
  api:
    build: .
    ports:
      - "5000:80"
    depends_on:
      - cache # <-- Precisa ser melhorado

  cache:
    image: redis:6-alpine
    # TODO: Adicionar healthcheck aqui
Show answer

A solução correta é:

version: '3.8'

services:
  api:
    build: .
    ports:
      - "5000:80"
    depends_on:
      cache:
        condition: service_healthy # Modificado para esperar a condição saudável

  cache:
    image: redis:6-alpine
    healthcheck: # Adicionado o bloco de healthcheck
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 5
      start_period: 10s

Explicação:

  1. Adicionamos o bloco healthcheck ao serviço cache. Ele usa o redis-cli ping, que é o método padrão para verificar a prontidão do Redis.
  2. Alteramos o depends_on no serviço api para o formato longo, especificando condition: service_healthy. Isso instrui o Docker Compose a esperar que o health check do cache passe antes de iniciar a api.

Conclusão

Excelente trabalho! Você agora tem o conhecimento prático para implementar verificações de saúde eficazes em suas aplicações Docker Compose. Isso eleva a robustez dos seus ambientes de desenvolvimento e teste a um novo patamar, eliminando condições de corrida e falhas de inicialização.

Principais Pontos:

  • A diretiva healthcheck permite definir um comando para validar a prontidão de um serviço.
  • Parâmetros como interval, timeout, retries, e especialmente start_period, oferecem controle fino sobre o comportamento da verificação.
  • A combinação de um healthcheck bem definido no serviço de dependência com depends_on: condition: service_healthy no serviço dependente é a maneira canônica de garantir uma ordem de inicialização robusta.
  • A depuração de falhas de health check é feita principalmente com docker-compose ps, docker inspect, e executando o comando de teste manualmente com docker-compose exec.

Na próxima lição, vamos mudar nosso foco de robustez para segurança. Iniciaremos o módulo de Segurança de Contêineres e Boas Práticas, começando com um princípio fundamental: aplicar o princípio do menor privilégio, criando e executando contêineres como usuários não-root.