Guia Completo de Docker Compose para Aplicações de Microsserviços

Introdução

Em projetos reais de microsserviços, executar múltiplos contêineres manualmente usando comandos como docker run é inviável. Docker Compose resolve isso ao permitir definir todo o ambiente de aplicação em um único arquivo YAML e iniciar tudo com um único comando. Este guia explora os conceitos essenciais do Docker Compose através de um exemplo prático baseado em um projeto Go de microsserviços.

Passo Inicial: Clonar o Projeto e Configurar Variáveis de Ambiente

Para acompahnar, clone o repositório de exemplo:

git clone https://github.com/exemplo/backend-microservicos.git
cd backend-microservicos

Crie um arquivo .env na raiz do projeto com a variável abaixo. Isso é crucial, pois o Docker Compose utilizará esses valores para configurar serviços como o banco de dados.

DB_PWD=senha_segura_aqui

Sem este arquivo ou com valores vazios, o contêiner do MySQL falhará ao iniciar, impedindo a inicialização de todos os serviços dependentes.

Conceito Central: O que é Docker Compose?

Docker Compose é uma ferramenta para definir e aplicações de vários contêineres. Um único arquivo docker-compose.yaml descreve todos os serviços, suas configurações e dependências. Para iniciar o ambiente completo:

docker compose up -d

Para parar todos os serviços:

docker compose down

Diretivas Fundamentais no docker-compose.yaml

Diretiva services

Define cada contêiner como um serviço. Exemplo:

services:
  postgres-db:
    ...
  cache-redis:
    ...

image vs build

Use image para imagens pré-existentes (como do Docker Hub) ou build para construir a partir de um Dockerfile. É vital fixar versões de imagens para consistência.

# Usando imagem oficial com versão fixa
image: postgres:15.3

# Construindo a partir de um Dockerfile customizado
build:
  context: ./src
  dockerfile: dockerfiles/app.dockerfile

Mapeamento de Portas com ports

Formato: porta_host:porta_contêiner. A porta à direita deve corresponder à porta em que o serviço escuta internamente.

ports:
  - "5432:5432"   # Host 5432 -> Contêiner PostgreSQL 5432
  - "8080:80"     # Host 8080 -> Contêiner Nginx 80

Persistência de Dados com volumes

Use volumes para manter dados após a remoção do contêiner.

volumes:
  - db_storage:/var/lib/postgresql/data    # Volume nomeado
  - ./configs:/app/configs                 # Bind mount

Configuração via environment

Injete variáveis de ambiente. Serviços no mesmo Compose podem se comunicar usando nomes de serviço como hostnames, graças ao DNS interno.

environment:
  - DB_HOST=postgres-db
  - DB_PWD=${DB_PWD}   # Lido do arquivo .env

Controle de Inicailização com depends_on

Gerencia a ordem de inicialização. Use condition: service_healthy para garantir que um serviço esteja realmente pronto antes de iniciar dependências.

depends_on:
  postgres-db:
    condition: service_healthy

Verificação de Saúde com healthcheck

Define como o Docker Compose determina se um serviço está saudável.

healthcheck:
  test: ["CMD", "pg_isready", "-U", "admin"]
  interval: 5s
  timeout: 5s
  retries: 10

Política de Reinício com restart

Especifica como o contêiner deve se comportar após uma falha.

  • no: Não reinicia automaticamente (padrão).
  • on-failure: Reinicia apenas se o processo terminar com código de erro.
  • always: Reinicia independentemente do código de saída.
restart: on-failure

Melhores Práticas para Dockerfiles: Construção Multiestágio

A construção multiestágio resulta em imagens de produção menores e mais seguras. Exemplo para uma aplicação frontend:

# Estágio 1: Construção
FROM node:18-alpine AS build_stage
WORKDIR /code
COPY package.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build

# Estágio 2: Produção
FROM nginx:stable-alpine AS production_stage
COPY --from=build_stage /code/dist /usr/share/nginx/html
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Isso reduz drasticamente o tamanho da imagem final, incluindo apenas os artefatos de compilação e o servidor Nginx.

Configuração do Nginx para Servir a Aplicação Frontend

Um exemplo de configuração para roteamento de aplicação de página única (SPA):

server {
    listen 80;

    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;   # Suporte a rotas no lado do cliente
    }

    location /api/ {
        proxy_pass http://api-gateway:8080;  # Encaminha requisições de API para o gateway
    }
}

Exemplo Prático: docker-compose.yaml Completo

Este arquivo configura um ecossistema de microsserviços com infraestrutura de suporte. Observe as alterações nos nomes e variáveis para demonstrar personalização.

services:

  # ===== INFRAESTRUTURA =====
  postgres-db:
    image: postgres:15.3
    environment:
      POSTGRES_PASSWORD: ${DB_PWD}
      POSTGRES_DB: app_database
    ports:
      - "5433:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 5s
      timeout: 5s
      retries: 10

  cache-redis:
    image: redis:7.0-alpine
    ports:
      - "6380:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 10

  message-queue:
    image: rabbitmq:3.12-management-alpine
    ports:
      - "5673:5672"
      - "15673:15672"
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "check_running"]
      interval: 10s
      timeout: 5s
      retries: 10

  service-registry:
    image: consul:1.15
    command: "agent -dev -client=0.0.0.0"
    ports:
      - "8501:8500"

  tracing-collector:
    image: jaegertracing/all-in-one:1.47
    ports:
      - "16687:16686"
      - "6832:6831/udp"
    environment:
      - COLLECTOR_OTLP_ENABLED=true

  metrics-server:
    image: prom/prometheus:v2.46.0
    ports:
      - "9091:9090"
    volumes:
      - ./config/prometheus.yml:/etc/prometheus/prometheus.yml
      - prom_storage:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'

  dashboard-grafana:
    image: grafana/grafana:10.0.3
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - metrics-server

  # ===== SERVIÇOS DE NEGÓCIO =====
  auth-service:
    build:
      context: .
      dockerfile: dockerfiles/auth.dockerfile
    environment:
      - DATABASE_URL=postgres://postgres:${DB_PWD}@postgres-db:5432/app_database
      - REDIS_URL=redis://cache-redis:6379
      - CONSUL_HTTP_ADDR=service-registry:8500
      - JAEGER_ENDPOINT=http://tracing-collector:14268/api/traces
    restart: on-failure
    depends_on:
      postgres-db:
        condition: service_healthy
      cache-redis:
        condition: service_healthy

  catalog-service:
    build:
      context: .
      dockerfile: dockerfiles/catalog.dockerfile
    environment:
      - DATABASE_URL=postgres://postgres:${DB_PWD}@postgres-db:5432/app_database
      - AMQP_URL=amqp://guest:guest@message-queue:5672
      - CONSUL_HTTP_ADDR=service-registry:8500
      - JAEGER_ENDPOINT=http://tracing-collector:14268/api/traces
    restart: on-failure
    depends_on:
      postgres-db:
        condition: service_healthy
      cache-redis:
        condition: service_healthy
      message-queue:
        condition: service_healthy

  worker-process:
    build:
      context: .
      dockerfile: dockerfiles/worker.dockerfile
    environment:
      - DATABASE_URL=postgres://postgres:${DB_PWD}@postgres-db:5432/app_database
      - AMQP_URL=amqp://guest:guest@message-queue:5672
      - JAEGER_ENDPOINT=http://tracing-collector:14268/api/traces
    restart: on-failure
    depends_on:
      postgres-db:
        condition: service_healthy
      message-queue:
        condition: service_healthy

  # ===== API GATEWAY =====
  api-gateway:
    build:
      context: .
      dockerfile: dockerfiles/gateway.dockerfile
    environment:
      - AUTH_SERVICE_URL=auth-service:8081
      - CATALOG_SERVICE_URL=catalog-service:8082
      - REDIS_URL=redis://cache-redis:6379
      - CONSUL_HTTP_ADDR=service-registry:8500
      - JAEGER_ENDPOINT=http://tracing-collector:14268/api/traces
    ports:
      - "8080:8080"
    restart: on-failure
    depends_on:
      - auth-service
      - catalog-service
    volumes:
      - ./uploads:/data/uploads

  # ===== FRONTEND =====
  web-app:
    build:
      context: ./web
      dockerfile: Dockerfile
    ports:
      - "3000:80"
    depends_on:
      - api-gateway

volumes:
  pgdata:
  prom_storage:

Inicializando o Ambiente

Após criar o arquivo .env, execute o seguinte comando no diretório do projeto para construir e iniciar todos os serviços:

docker compose up --build -d

Para inicializações subsequentes, se nenhum código mudou:

docker compose up -d

Serviços acessíveis após a inicialização:

  • Aplicação Web: http://localhost:3000
  • API Gateway: http://localhost:8080
  • Gerenciamento do RabbitMQ: http://localhost:15673
  • Prometheus: http://localhost:9091
  • Grafana: http://localhost:3001
  • Jaeger UI: http://localhost:16687
  • Consul UI: http://localhost:8501

Tags: docker-compose Docker microsservicos go postgresql

Publicado em 6-18 18:39