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: trueDocker 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: bridgeNginx 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 api2Logs
bash
# Logs de um serviço
docker-compose logs -f api1
# Logs de todos
docker-compose logs -fDebug
bash
# Entrar no container
docker exec -it csgoflip_api1 sh
# Verificar recursos
docker stats
# Verificar networks
docker network ls
docker network inspect csgoflip_backendPM2 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',
},
},
],
};