Skip to content

Docker

O CSGOFlip utiliza Docker para containerização, garantindo ambientes consistentes entre desenvolvimento e produção.

Estrutura de Arquivos

docker/
├── api/
│   ├── Dockerfile
│   └── nginx/
│       └── nginx.conf
├── infra/
│   ├── docker-compose.yml
│   ├── grafana/
│   ├── prometheus/
│   └── init-scripts/
└── production/
    ├── server1-infra/
    └── server2-api/

Dockerfile da API

dockerfile
# Dockerfile
FROM node:20-alpine AS builder

WORKDIR /app

# Instalar dependências
COPY package*.json ./
COPY prisma ./prisma/
RUN npm ci

# Build
COPY . .
RUN npm run build

# Gerar Prisma Client
RUN npx prisma generate

# Produção
FROM node:20-alpine AS production

WORKDIR /app

# Copiar apenas o necessário
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/package*.json ./

# Usuário não-root
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nestjs -u 1001
USER nestjs

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

EXPOSE 3000

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

Docker Compose - Desenvolvimento

yaml
# docker-compose.dev.yml
version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: docker/api/Dockerfile
      target: builder
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:postgres@db:5432/csgoflip
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis
    command: npm run start:dev

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: csgoflip
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  minio:
    image: minio/minio
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"

volumes:
  postgres_data:
  redis_data:
  minio_data:

Docker Compose - Produção

yaml
# docker-compose.prod.yml
version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
    depends_on:
      - api1
      - api2
    networks:
      - frontend
    restart: always

  api1:
    image: csgoflip/api:latest
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_HOST=${REDIS_HOST}
    networks:
      - frontend
      - backend
    restart: always
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G

  api2:
    image: csgoflip/api:latest
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_HOST=${REDIS_HOST}
    networks:
      - frontend
      - backend
    restart: always
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G

  worker:
    image: csgoflip/api:latest
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_HOST=${REDIS_HOST}
      - WORKER_MODE=true
    networks:
      - backend
    restart: always
    command: node dist/worker.js

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

Docker Compose - Infraestrutura

yaml
# docker-compose.infra.yml
version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: csgoflip
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d
    ports:
      - "5432:5432"
    networks:
      - backend
    restart: always
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 8G

  postgres-replica:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_replica_data:/var/lib/postgresql/data
    networks:
      - backend
    restart: always
    depends_on:
      - postgres

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
    volumes:
      - redis_data:/data
    ports:
      - "6379:6379"
    networks:
      - backend
    restart: always

  redis-sentinel:
    image: redis:7-alpine
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
      - ./redis/sentinel.conf:/etc/redis/sentinel.conf
    networks:
      - backend
    restart: always
    depends_on:
      - redis

  minio:
    image: minio/minio
    environment:
      MINIO_ROOT_USER: ${MINIO_USER}
      MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD}
    volumes:
      - minio_data:/data
    ports:
      - "9000:9000"
      - "9001:9001"
    networks:
      - backend
    restart: always
    command: server /data --console-address ":9001"

  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    ports:
      - "9090:9090"
    networks:
      - backend
    restart: always

  grafana:
    image: grafana/grafana
    environment:
      GF_SECURITY_ADMIN_USER: ${GRAFANA_USER}
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
    volumes:
      - grafana_data:/var/lib/grafana
      - ./grafana/provisioning:/etc/grafana/provisioning
    ports:
      - "3001:3000"
    networks:
      - backend
    restart: always
    depends_on:
      - prometheus

volumes:
  postgres_data:
  postgres_replica_data:
  redis_data:
  minio_data:
  prometheus_data:
  grafana_data:

networks:
  backend:
    driver: bridge

Nginx Configuration

nginx
# nginx/nginx.conf
upstream api {
    least_conn;
    server api1:3000 weight=5;
    server api2:3000 weight=5;
    keepalive 32;
}

upstream websocket {
    ip_hash;
    server api1:3000;
    server api2:3000;
}

server {
    listen 80;
    server_name api.csgoflip.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.csgoflip.com;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

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

    # Gzip
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript;

    # API
    location /api {
        proxy_pass http://api;
        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_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # WebSocket
    location /socket.io {
        proxy_pass http://websocket;
        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_connect_timeout 7d;
        proxy_send_timeout 7d;
        proxy_read_timeout 7d;
    }

    # Health check
    location /health {
        proxy_pass http://api;
        proxy_http_version 1.1;
        access_log off;
    }
}

Comandos Úteis

Build

bash
# Build de imagem
docker build -t csgoflip/api:latest -f docker/api/Dockerfile .

# Build com cache
docker build --cache-from csgoflip/api:latest -t csgoflip/api:latest .

Deploy

bash
# Pull e restart
docker-compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -d

# Sem downtime (rolling update)
docker-compose -f docker-compose.prod.yml up -d --no-deps --build api1
docker-compose -f docker-compose.prod.yml up -d --no-deps --build api2

Logs

bash
# Logs de um serviço
docker-compose logs -f api1

# Logs de todos
docker-compose logs -f

Debug

bash
# Entrar no container
docker exec -it csgoflip_api1 sh

# Verificar recursos
docker stats

# Verificar networks
docker network ls
docker network inspect csgoflip_backend

PM2 Ecosystem

Para deploy sem Docker:

javascript
// ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'csgoflip-api',
      script: 'dist/main.js',
      instances: 'max',
      exec_mode: 'cluster',
      env_production: {
        NODE_ENV: 'production',
      },
      max_memory_restart: '1G',
      error_file: './logs/error.log',
      out_file: './logs/out.log',
      merge_logs: true,
      log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
    },
    {
      name: 'csgoflip-worker',
      script: 'dist/worker.js',
      instances: 2,
      exec_mode: 'cluster',
      env_production: {
        NODE_ENV: 'production',
        WORKER_MODE: 'true',
      },
    },
  ],
};

Documentação Técnica CSGOFlip