Deploy de aplicações com Docker Compose

1. Introdução ao Docker Compose para Deploy

Docker Compose é uma ferramenta que permite definir e gerenciar aplicações multi-contêiner através de um arquivo YAML declarativo. Embora seja amplamente utilizado em ambientes de desenvolvimento, o Docker Compose também é uma solução viável para deploy em produção, especialmente em cenários de pequena e média escala.

A principal diferença entre desenvolvimento local e deploy real está na configuração: em produção, é necessário considerar aspectos como persistência de dados, segurança, logs centralizados e estratégias de atualização sem downtime. Cenários ideais para deploy com Docker Compose incluem aplicações web com banco de dados, sistemas de microserviços simples, plataformas CMS e ferramentas internas de equipe.

2. Estrutura do Projeto e Arquivo docker-compose.yml

Um projeto bem estruturado facilita o deploy e a manutenção. A organização recomendada inclui:

meu-projeto/
├── docker-compose.yml
├── .env
├── app/
│   ├── Dockerfile
│   └── src/
├── db/
│   └── init.sql
└── nginx/
    └── default.conf

O arquivo docker-compose.yml define serviços, redes e volumes. Exemplo básico:

version: '3.8'

services:
  web:
    build: ./app
    ports:
      - "80:3000"
    env_file: .env
    depends_on:
      - db
    networks:
      - app-network

  db:
    image: postgres:15-alpine
    volumes:
      - postgres-data:/var/lib/postgresql/data
    env_file: .env
    networks:
      - app-network

volumes:
  postgres-data:

networks:
  app-network:
    driver: bridge

Boas práticas incluem o uso de arquivos .env para separar configurações sensíveis do código, evitando hardcoding de senhas e variáveis de ambiente.

3. Configuração de Serviços para Produção

Imagens otimizadas são essenciais para deploys eficientes. O uso de Dockerfile multi-stage reduz o tamanho da imagem final:

# Estágio de build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Estágio de produção
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

O mapeamento de portas deve ser controlado: apenas expor portas necessárias para o mundo externo. Serviços internos (como bancos de dados) não devem ser expostos publicamente.

Dependências entre serviços são gerenciadas com depends_on e healthchecks:

services:
  web:
    depends_on:
      db:
        condition: service_healthy

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

4. Gerenciamento de Dados e Persistência

Volumes nomeados são a forma recomendada para dados persistentes em produção. Diferente de bind mounts, volumes são gerenciados pelo Docker e não dependem da estrutura de diretórios do host:

volumes:
  postgres-data:
  redis-data:
  uploads:

Bind mounts podem ser úteis para configurações e logs que precisam ser acessados diretamente no host:

services:
  nginx:
    volumes:
      - ./nginx/logs:/var/log/nginx
      - ./nginx/conf.d:/etc/nginx/conf.d:ro

Para backup de volumes em produção, utilize comandos como:

docker run --rm -v postgres-data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz -C /data .

A restauração segue o processo inverso:

docker run --rm -v postgres-data:/data -v $(pwd):/backup alpine tar xzf /backup/postgres-backup.tar.gz -C /data

5. Redes e Segurança no Deploy

Redes isoladas garantem que apenas serviços autorizados se comuniquem entre si. Crie redes específicas para diferentes camadas da aplicação:

networks:
  frontend:
  backend:
  database:

A exposição de portas deve ser mínima. Apenas o serviço de entrada (geralmente nginx ou um API gateway) deve ter portas mapeadas para o host:

services:
  nginx:
    ports:
      - "80:80"
      - "443:443"
    networks:
      - frontend

  api:
    expose:
      - "3000"
    networks:
      - frontend
      - backend

  db:
    expose:
      - "5432"
    networks:
      - backend

Para variáveis sensíveis, utilize Docker Secrets (modo swarm) ou arquivos .env protegidos com permissões restritas:

services:
  app:
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

6. Estratégias de Deploy e Atualização

Comandos essenciais para gerenciamento em produção:

# Iniciar serviços em background
docker-compose up -d

# Parar e remover contêineres
docker-compose down

# Reiniciar serviços
docker-compose restart

# Visualizar status
docker-compose ps

Para atualizações sem downtime, utilize a estratégia de scale com versões:

# Subir nova versão sem parar a atual
docker-compose up -d --no-deps --scale web=2 --no-recreate

# Verificar se a nova versão está saudável
docker-compose ps

# Remover versão antiga
docker-compose up -d --no-deps --scale web=1

O versionamento de imagens facilita rollbacks:

services:
  web:
    image: registry.exemplo.com/app:v2.1.0

Para rollback, basta alterar a tag da imagem e executar docker-compose up -d.

7. Monitoramento e Logs em Produção

Logs centralizados são fundamentais para debugging. O Docker Compose oferece coleta nativa:

# Visualizar logs em tempo real
docker-compose logs -f

# Logs de serviço específico
docker-compose logs web

# Últimas 100 linhas
docker-compose logs --tail=100 web

Para monitoramento avançado, integre com Prometheus e Grafana:

services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana

Healthchecks automáticos garantem que serviços sejam reiniciados em caso de falha:

services:
  web:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

8. Considerações Finais e Próximos Passos

O Docker Compose é uma ferramenta poderosa para deploy de aplicações multi-serviço, mas possui limitações em ambientes de grande escala. Para clusters com múltiplos nós, alta disponibilidade e balanceamento de carga avançado, considere migrar para orquestradores como Kubernetes ou Docker Swarm.

Checklist de boas práticas para deploy seguro:
- [ ] Utilizar imagens oficiais e atualizadas
- [ ] Implementar healthchecks em todos os serviços
- [ ] Configurar restart policies (always, unless-stopped)
- [ ] Separar ambientes com arquivos docker-compose.override.yml
- [ ] Realizar backups periódicos dos volumes
- [ ] Utilizar redes isoladas e exposição mínima de portas
- [ ] Versionar imagens e manter histórico de releases
- [ ] Monitorar logs e métricas continuamente

Com essas práticas, o Docker Compose se torna uma solução robusta e confiável para deploy de aplicações em produção, oferecendo simplicidade sem abrir mão da segurança e da escalabilidade.

Referências