Este documento aborda todos os aspectos da configuração Docker no projeto Varion, incluindo desenvolvimento, produção, otimizações e troubleshooting.
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
# 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
# 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
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;"]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: bridgeversion: '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"]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# .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/# 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;
}
}
}#!/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!"#!/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!"#!/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!"#!/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#!/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# Verificar logs
docker-compose logs [service-name]
# Verificar status
docker-compose ps
# Verificar recursos
docker system df# Listar redes
docker network ls
# Inspecionar rede
docker network inspect varion_varion-network
# Recriar rede
docker-compose down
docker network prune
docker-compose up# Listar volumes
docker volume ls
# Inspecionar volume
docker volume inspect varion_postgres_data
# Remover volume (cuidado!)
docker volume rm varion_postgres_data# Build sem cache
docker-compose build --no-cache
# Limpar build cache
docker builder prune -a# 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# Usar .dockerignore adequadamente
# Ordenar comandos por frequência de mudança
# Usar multi-stage builds
# Cachear camadas de dependências# 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# 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/*- docker-compose.yml configurado
- Hot reload funcionando
- Volumes mapeados corretamente
- Variáveis de ambiente definidas
- Health checks implementados
- Multi-stage builds otimizados
- Usuários não-root configurados
- Secrets gerenciados adequadamente
- SSL/TLS configurado
- Backup automatizado
- Monitoramento implementado
- Logs centralizados
- Imagens atualizadas
- Vulnerabilidades verificadas
- Firewall configurado
- Network isolation
- Resource limits definidos