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