Skip to content

Latest commit

 

History

History
916 lines (770 loc) · 20.3 KB

File metadata and controls

916 lines (770 loc) · 20.3 KB

Configuração Docker - Varion

🐳 Visão Geral

Este documento aborda todos os aspectos da configuração Docker no projeto Varion, incluindo desenvolvimento, produção, otimizações e troubleshooting.

📁 Estrutura Docker

Varion/
├── docker-compose.yml           # Desenvolvimento
├── docker-compose.dev.yml       # Desenvolvimento com hot reload
├── docker-compose.prod.yml      # Produção
├── .dockerignore               # Arquivos ignorados
├── backend/
│   ├── Dockerfile              # Backend container
│   └── .dockerignore
├── frontend/
│   ├── Dockerfile              # Frontend container
│   └── .dockerignore
└── nginx/
    ├── Dockerfile              # Nginx reverse proxy
    └── nginx.conf

🔧 Dockerfiles

Backend Dockerfile

# backend/Dockerfile
# Dockerfile multi-stage para otimização

# Stage 1: Base
FROM node:18-alpine AS base
RUN corepack enable pnpm
WORKDIR /app

# Stage 2: Dependencies
FROM base AS deps
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Stage 3: Build
FROM base AS build
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

# Stage 4: Development
FROM base AS development
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
EXPOSE 3001
CMD ["pnpm", "start:dev"]

# Stage 5: Production
FROM base AS production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs

# Copiar dependências de produção
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build --chown=nodejs:nodejs /app/dist ./dist
COPY --from=build --chown=nodejs:nodejs /app/package.json ./package.json

# Configurar usuário não-root
USER nodejs

EXPOSE 3001
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3001/api/health || exit 1

CMD ["node", "dist/server.js"]

Frontend Dockerfile

# frontend/Dockerfile
# Dockerfile multi-stage para Next.js

# Stage 1: Base
FROM node:18-alpine AS base
RUN corepack enable pnpm
WORKDIR /app

# Stage 2: Dependencies
FROM base AS deps
RUN apk add --no-cache libc6-compat
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Stage 3: Build
FROM base AS build
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Configurar variáveis de build
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

RUN pnpm build

# Stage 4: Development
FROM base AS development
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
EXPOSE 3000
CMD ["pnpm", "dev"]

# Stage 5: Production
FROM base AS production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs

# Copiar arquivos necessários
COPY --from=build --chown=nodejs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nodejs:nodejs /app/.next/static ./.next/static
COPY --from=build --chown=nodejs:nodejs /app/public ./public

USER nodejs

EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

Nginx Dockerfile

# nginx/Dockerfile
FROM nginx:alpine

# Instalar certbot para SSL
RUN apk add --no-cache certbot certbot-nginx

# Copiar configuração customizada
COPY nginx.conf /etc/nginx/nginx.conf
COPY ssl/ /etc/nginx/ssl/

# Criar diretórios necessários
RUN mkdir -p /var/log/nginx /var/cache/nginx

# Configurar permissões
RUN chown -R nginx:nginx /var/log/nginx /var/cache/nginx

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

🚀 Docker Compose Configurations

Desenvolvimento (docker-compose.yml)

version: '3.8'

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      target: development
    container_name: varion-frontend-dev
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - NEXT_PUBLIC_API_URL=http://localhost:3001
    volumes:
      - ./frontend:/app
      - /app/node_modules
      - /app/.next
    depends_on:
      - backend
    networks:
      - varion-network

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
      target: development
    container_name: varion-backend-dev
    ports:
      - "3001:3001"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/varion_dev
      - JWT_SECRET=development_jwt_secret_key
      - CORS_ORIGIN=http://localhost:3000
    volumes:
      - ./backend:/app
      - /app/node_modules
      - /app/dist
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - varion-network

  postgres:
    image: postgres:15-alpine
    container_name: varion-postgres-dev
    environment:
      - POSTGRES_DB=varion_dev
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - "5432:5432"
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
      - ./backend/scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - varion-network

volumes:
  postgres_dev_data:

networks:
  varion-network:
    driver: bridge

Desenvolvimento com Hot Reload (docker-compose.dev.yml)

version: '3.8'

services:
  frontend:
    extends:
      file: docker-compose.yml
      service: frontend
    environment:
      - NODE_ENV=development
      - NEXT_PUBLIC_API_URL=http://localhost:3001
      - WATCHPACK_POLLING=true
    volumes:
      - ./frontend:/app
      - /app/node_modules
      - /app/.next
    command: ["pnpm", "dev"]

  backend:
    extends:
      file: docker-compose.yml
      service: backend
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/varion_dev
    volumes:
      - ./backend:/app
      - /app/node_modules
    command: ["pnpm", "start:dev"]

Produção (docker-compose.prod.yml)

version: '3.8'

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      target: production
      args:
        - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-https://api.yourdomain.com}
    container_name: varion-frontend-prod
    restart: unless-stopped
    environment:
      - NODE_ENV=production
    networks:
      - varion-network

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
      target: production
    container_name: varion-backend-prod
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - JWT_SECRET=${JWT_SECRET}
      - CORS_ORIGIN=${CORS_ORIGIN}
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - varion-network

  postgres:
    image: postgres:15-alpine
    container_name: varion-postgres-prod
    restart: unless-stopped
    environment:
      - POSTGRES_DB=${DB_NAME:-varion_prod}
      - POSTGRES_USER=${DB_USER:-postgres}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres_prod_data:/var/lib/postgresql/data
      - ./backup/scripts:/backup/scripts:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - varion-network

  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    container_name: varion-nginx-prod
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - ./logs/nginx:/var/log/nginx
    depends_on:
      - frontend
      - backend
    networks:
      - varion-network

  redis:
    image: redis:7-alpine
    container_name: varion-redis-prod
    restart: unless-stopped
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    networks:
      - varion-network

volumes:
  postgres_prod_data:
  redis_data:

networks:
  varion-network:
    driver: bridge

🔧 Configurações Avançadas

.dockerignore Files

# .dockerignore (root)
.git
.gitignore
README.md
.env
.env.example
.env.local
.env.*.local
.vscode
.idea
*.log
.DS_Store
Thumbs.db
coverage/
docs/
*.md
# backend/.dockerignore
node_modules
.git
.env
.env.*
*.log
coverage/
docs/
src/**/*.test.ts
src/**/*.spec.ts
jest.config.js
# frontend/.dockerignore
node_modules
.git
.env
.env.*
.next
*.log
coverage/
docs/
src/**/*.test.tsx
src/**/*.spec.tsx
jest.config.js
playwright.config.ts
e2e/

Configuração Nginx

# nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;

    # Performance
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml
        text/plain
        text/css
        text/js
        text/xml
        text/javascript;

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

    # Upstream servers
    upstream frontend {
        server frontend:3000;
        keepalive 32;
    }

    upstream backend {
        server backend:3001;
        keepalive 32;
    }

    # HTTP to HTTPS redirect
    server {
        listen 80;
        server_name yourdomain.com www.yourdomain.com api.yourdomain.com;
        return 301 https://$server_name$request_uri;
    }

    # Frontend HTTPS
    server {
        listen 443 ssl http2;
        server_name yourdomain.com www.yourdomain.com;

        # SSL Configuration
        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers on;

        # Security headers
        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";

        # Frontend proxy
        location / {
            proxy_pass http://frontend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_cache_bypass $http_upgrade;
            proxy_redirect off;
        }

        # Next.js specific
        location /_next/static {
            proxy_pass http://frontend;
            add_header Cache-Control "public, max-age=31536000, immutable";
        }

        location /_next/webpack-hmr {
            proxy_pass http://frontend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }

    # Backend API HTTPS
    server {
        listen 443 ssl http2;
        server_name api.yourdomain.com;

        # SSL Configuration (same as above)
        ssl_certificate /etc/nginx/ssl/fullchain.pem;
        ssl_certificate_key /etc/nginx/ssl/privkey.pem;
        ssl_session_cache shared:SSL:10m;
        ssl_session_timeout 10m;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers on;

        # Security headers
        add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;

        # Rate limiting for API
        location /api {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_redirect off;
        }

        # Stricter rate limiting for auth endpoints
        location /api/auth {
            limit_req zone=login burst=5 nodelay;
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_redirect off;
        }
    }
}

📦 Scripts Docker

Build Scripts

#!/bin/bash
# scripts/docker-build.sh

set -e

echo "🐳 Building Docker images..."

# Build arguments
BUILD_ENV=${1:-development}
TAG=${2:-latest}

if [ "$BUILD_ENV" = "production" ]; then
    echo "🏭 Building production images..."

    # Build backend
    docker build \
        --target production \
        --tag varion/backend:$TAG \
        --tag varion/backend:latest \
        ./backend

    # Build frontend
    docker build \
        --target production \
        --build-arg NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL \
        --tag varion/frontend:$TAG \
        --tag varion/frontend:latest \
        ./frontend

    # Build nginx
    docker build \
        --tag varion/nginx:$TAG \
        --tag varion/nginx:latest \
        ./nginx
else
    echo "🚧 Building development images..."

    # Build with development target
    docker build \
        --target development \
        --tag varion/backend:dev \
        ./backend

    docker build \
        --target development \
        --tag varion/frontend:dev \
        ./frontend
fi

echo "✅ Docker images built successfully!"

Deploy Script

#!/bin/bash
# scripts/docker-deploy.sh

set -e

ENVIRONMENT=${1:-development}
COMPOSE_FILE="docker-compose.yml"

if [ "$ENVIRONMENT" = "production" ]; then
    COMPOSE_FILE="docker-compose.prod.yml"
fi

echo "🚀 Deploying to $ENVIRONMENT..."

# Pull latest images (if using registry)
if [ "$ENVIRONMENT" = "production" ]; then
    docker-compose -f $COMPOSE_FILE pull
fi

# Build and start services
docker-compose -f $COMPOSE_FILE up --build -d

# Wait for services to be healthy
echo "⏳ Waiting for services to be ready..."
sleep 30

# Run health checks
./scripts/health-check.sh

# Run database migrations
if [ "$ENVIRONMENT" = "production" ]; then
    docker-compose -f $COMPOSE_FILE exec backend pnpm migration:run
fi

echo "✅ Deployment completed successfully!"

Cleanup Script

#!/bin/bash
# scripts/docker-cleanup.sh

echo "🧹 Cleaning up Docker resources..."

# Stop and remove containers
docker-compose down

# Remove dangling images
docker image prune -f

# Remove unused volumes (careful with this!)
read -p "Remove unused volumes? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    docker volume prune -f
fi

# Remove unused networks
docker network prune -f

# Remove build cache
docker builder prune -f

echo "✅ Cleanup completed!"

🔍 Monitoramento e Logs

Health Checks

#!/bin/bash
# scripts/docker-health-check.sh

echo "🔍 Checking Docker container health..."

services=("frontend" "backend" "postgres")

for service in "${services[@]}"; do
    container_name="varion-$service-prod"

    if docker ps --filter "name=$container_name" --filter "status=running" | grep -q $container_name; then
        echo "$service: Running"

        # Additional health checks
        case $service in
            "frontend")
                if curl -f http://localhost:3000 > /dev/null 2>&1; then
                    echo "  📡 Frontend responding"
                else
                    echo "  ❌ Frontend not responding"
                fi
                ;;
            "backend")
                if curl -f http://localhost:3001/api/health > /dev/null 2>&1; then
                    echo "  📡 Backend API responding"
                else
                    echo "  ❌ Backend API not responding"
                fi
                ;;
            "postgres")
                if docker exec $container_name pg_isready -U postgres > /dev/null 2>&1; then
                    echo "  📡 Database responding"
                else
                    echo "  ❌ Database not responding"
                fi
                ;;
        esac
    else
        echo "$service: Not running"
    fi
done

Log Management

#!/bin/bash
# scripts/docker-logs.sh

SERVICE=${1:-all}
LINES=${2:-100}

case $SERVICE in
    "frontend")
        docker-compose logs -f --tail=$LINES frontend
        ;;
    "backend")
        docker-compose logs -f --tail=$LINES backend
        ;;
    "postgres")
        docker-compose logs -f --tail=$LINES postgres
        ;;
    "nginx")
        docker-compose logs -f --tail=$LINES nginx
        ;;
    "all")
        docker-compose logs -f --tail=$LINES
        ;;
    *)
        echo "Usage: $0 [frontend|backend|postgres|nginx|all] [lines]"
        exit 1
        ;;
esac

🐛 Troubleshooting

Problemas Comuns

Containers não iniciam

# Verificar logs
docker-compose logs [service-name]

# Verificar status
docker-compose ps

# Verificar recursos
docker system df

Problemas de rede

# Listar redes
docker network ls

# Inspecionar rede
docker network inspect varion_varion-network

# Recriar rede
docker-compose down
docker network prune
docker-compose up

Problemas de volume

# Listar volumes
docker volume ls

# Inspecionar volume
docker volume inspect varion_postgres_data

# Remover volume (cuidado!)
docker volume rm varion_postgres_data

Build cache issues

# Build sem cache
docker-compose build --no-cache

# Limpar build cache
docker builder prune -a

Debug de Container

# Executar comandos no container
docker-compose exec backend sh
docker-compose exec frontend sh

# Verificar logs de um container específico
docker logs varion-backend-prod -f --tail=100

# Inspecionar container
docker inspect varion-backend-prod

# Verificar recursos do container
docker stats varion-backend-prod

⚡ Otimizações

Build Performance

# Usar .dockerignore adequadamente
# Ordenar comandos por frequência de mudança
# Usar multi-stage builds
# Cachear camadas de dependências

Runtime Performance

# docker-compose.yml
services:
  backend:
    # Limitar recursos
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
        reservations:
          memory: 256M
          cpus: '0.25'

    # Health checks
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Security Optimizations

# Usar usuário não-root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nodejs
USER nodejs

# Usar imagens Alpine (menores)
FROM node:18-alpine

# Minimizar surface de ataque
RUN rm -rf /tmp/* /var/cache/apk/*

📋 Checklist Docker

Desenvolvimento

  • docker-compose.yml configurado
  • Hot reload funcionando
  • Volumes mapeados corretamente
  • Variáveis de ambiente definidas
  • Health checks implementados

Produção

  • Multi-stage builds otimizados
  • Usuários não-root configurados
  • Secrets gerenciados adequadamente
  • SSL/TLS configurado
  • Backup automatizado
  • Monitoramento implementado
  • Logs centralizados

Segurança

  • Imagens atualizadas
  • Vulnerabilidades verificadas
  • Firewall configurado
  • Network isolation
  • Resource limits definidos