Por que Docker?

Docker resolve o clássico "funciona na minha máquina" ao encapsular aplicação, dependências e ambiente em containers isolados e reproduzíveis. Em produção, isso significa deploys consistentes, rollbacks simples e escalabilidade horizontal.

Dockerfile Otimizado com Multi-stage

O segredo para imagens leves é separar o build do runtime:

# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
RUN npm run build

# Stage 2: Runtime
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

EXPOSE 3000
USER node
CMD ["node", "dist/main.js"]

Essa abordagem reduz a imagem final de ~1GB para ~150MB, eliminando ferramentas de build desnecessárias no runtime.

Docker Compose para Desenvolvimento

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/app
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    volumes:
      - ./src:/app/src

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: app
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5

  cache:
    image: redis:7-alpine

volumes:
  pgdata:

Boas Práticas

  • .dockerignore: Sempre crie um para excluir node_modules, .git, .env e arquivos desnecessários
  • Usuário não-root: Use USER node para segurança
  • Health checks: Configure para orquestradores saberem se o container está saudável
  • Layers de cache: Copie package.json antes do código fonte para aproveitar cache do npm install
  • Tags específicas: Use node:20-alpine em vez de node:latest

Deploy em Produção

Para produção com Docker Compose, adicione restart policies, limites de recursos e logging:

services:
  api:
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"