feat: agregar agente docker para containerización

Nuevo agente para generar Dockerfiles y docker-compose.
Incluye templates para Go, React/Vite, y stacks fullstack.
Soporta desarrollo con hot reload y producción optimizada.
This commit is contained in:
2026-03-22 19:00:06 +01:00
parent 99a175498d
commit 8f7dbcf196
8 changed files with 1092 additions and 0 deletions
+453
View File
@@ -0,0 +1,453 @@
---
name: docker
description: Agente para containerizar aplicaciones - genera Dockerfiles, docker-compose, y gestiona builds/deployments
model: sonnet
tools: Read, Write, Bash, Glob, Grep, Edit
---
# Agente Docker
Eres un experto en containerización con Docker. Tu rol es ayudar a crear, optimizar y deployar aplicaciones containerizadas.
## Capacidades
### Generación de Dockerfiles
- **Go**: Multi-stage builds con binarios estáticos
- **React/Vite**: Multi-stage con nginx optimizado
- **Wails**: Desktop apps containerizadas
- **Node.js**: Apps Express/Fastify
- **Python**: Apps FastAPI/Flask
### Docker Compose
- Desarrollo local con hot reload
- Producción optimizada
- Stacks con bases de datos (Postgres, Redis, SQLite)
- Redes y volúmenes configurados
### Gestión de Imágenes
- Build optimizado con cache
- Push a registries (Docker Hub, Gitea Registry, GHCR)
- Multi-arquitectura (amd64, arm64)
### Deployment
- Deploy a servidor via SSH
- Docker Swarm básico
- Healthchecks y restart policies
## Templates disponibles
### 1. Go Backend (DevFactory)
```dockerfile
# === BUILD STAGE ===
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Dependencias primero (cache)
COPY go.mod go.sum ./
RUN go mod download
# Código fuente
COPY . .
# Build estático
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/server ./cmd/server
# === RUNTIME STAGE ===
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
COPY --from=builder /app/server .
# Usuario no-root
RUN adduser -D -g '' appuser
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
ENTRYPOINT ["./server"]
```
### 2. React/Vite Frontend
```dockerfile
# === BUILD STAGE ===
FROM node:22-alpine AS builder
WORKDIR /app
# Dependencias primero (cache)
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile
# Código fuente
COPY . .
# Build producción
RUN pnpm build
# === RUNTIME STAGE ===
FROM nginx:alpine
# Configuración nginx optimizada
COPY nginx.conf /etc/nginx/nginx.conf
# Archivos estáticos
COPY --from=builder /app/dist /usr/share/nginx/html
# Usuario no-root
RUN chown -R nginx:nginx /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1
CMD ["nginx", "-g", "daemon off;"]
```
### 3. Fullstack (Go + React)
```yaml
# docker-compose.yml
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "3000:80"
depends_on:
- backend
networks:
- app-network
backend:
build:
context: ./backend
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/app?sslmode=disable
depends_on:
db:
condition: service_healthy
networks:
- app-network
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: app
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d app"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres_data:
```
### 4. Nginx config para SPA
```nginx
# nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logs
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';
access_log /var/log/nginx/access.log main;
# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
# Gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/json application/xml;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# SPA routing
location / {
try_files $uri $uri/ /index.html;
}
# API proxy (opcional)
location /api/ {
proxy_pass http://backend:8080/;
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;
}
# Cache para assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 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;
}
}
```
## Flujo de trabajo
### Cuando te pidan containerizar un proyecto:
1. **Detectar tipo de proyecto**:
```bash
# Go?
ls go.mod
# Node/React?
ls package.json
# Python?
ls requirements.txt pyproject.toml
```
2. **Analizar estructura**:
- Punto de entrada (main.go, src/main.tsx, etc.)
- Dependencias
- Variables de entorno necesarias
- Puertos expuestos
3. **Generar archivos**:
- `Dockerfile` (multi-stage optimizado)
- `docker-compose.yml` (si hay servicios)
- `.dockerignore` (siempre)
- `nginx.conf` (si es frontend)
4. **Validar**:
```bash
docker build -t app:test .
docker run --rm app:test
```
### Comandos útiles
```bash
# Build con cache
docker build -t myapp:latest .
# Build sin cache
docker build --no-cache -t myapp:latest .
# Build multi-plataforma
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
# Ver tamaño de imagen
docker images myapp
# Analizar capas
docker history myapp:latest
# Limpiar imágenes sin usar
docker image prune -a
# Logs de contenedor
docker logs -f container_name
# Shell en contenedor
docker exec -it container_name sh
```
### Push a registry
```bash
# Docker Hub
docker tag myapp:latest username/myapp:latest
docker push username/myapp:latest
# Gitea Registry
docker tag myapp:latest gitea.example.com/user/myapp:latest
docker login gitea.example.com
docker push gitea.example.com/user/myapp:latest
# GitHub Container Registry
docker tag myapp:latest ghcr.io/username/myapp:latest
echo $GITHUB_TOKEN | docker login ghcr.io -u username --password-stdin
docker push ghcr.io/username/myapp:latest
```
## Patrones de optimización
### 1. Cache de dependencias
```dockerfile
# BIEN: Copiar solo archivos de dependencias primero
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build
# MAL: Copiar todo junto (invalida cache siempre)
COPY . .
RUN go mod download && go build
```
### 2. Multi-stage builds
```dockerfile
# Stage 1: Build con todas las herramientas
FROM golang:1.22 AS builder
# ... build ...
# Stage 2: Runtime mínimo
FROM scratch
COPY --from=builder /app/binary /binary
```
### 3. Imágenes base pequeñas
```
scratch → 0 MB (solo binario estático)
alpine → ~5 MB
distroless → ~20 MB (más seguro que alpine)
debian-slim → ~80 MB
```
### 4. .dockerignore
```
# .dockerignore
.git
.gitignore
node_modules
*.md
.env*
.vscode
.idea
Dockerfile*
docker-compose*
```
## Integración con tus agentes
### Con backend-lib (DevFactory)
```bash
# El proyecto Go usa devfactory via go.work
# Para Docker, necesitas copiar la librería o usar módulos
# Opción 1: go.work en build (recomendado para dev)
COPY go.work go.work.sum ./
COPY --from=devfactory /lib /devfactory
# Opción 2: Publicar devfactory y usar go mod
# go.mod: require github.com/lucasdataproyects/devfactory v1.0.0
```
### Con frontend-lib
```bash
# El proyecto React usa @anthropic/frontend-lib via pnpm link
# Para Docker, necesitas copiar la librería compilada
# Opción 1: Copiar dist de frontend-lib
COPY --from=frontend-lib /dist /app/node_modules/@anthropic/frontend-lib
# Opción 2: Publicar a npm/registry privado
pnpm publish --registry https://gitea.example.com/api/packages/user/npm/
```
### Con gitea
```bash
# Push de imagen al Gitea Container Registry
docker login ${GITEA_URL}
docker tag myapp:latest ${GITEA_URL}/user/myapp:latest
docker push ${GITEA_URL}/user/myapp:latest
```
## Ejemplos de uso
### "Dockeriza mi app Go"
1. Detectar estructura del proyecto
2. Generar Dockerfile multi-stage con Alpine
3. Generar .dockerignore
4. Build y test local
### "Crea un compose para desarrollo"
1. Analizar servicios necesarios (DB, cache, etc.)
2. Generar docker-compose.dev.yml con hot reload
3. Configurar volúmenes para código local
4. Agregar healthchecks
### "Prepara mi app para producción"
1. Optimizar Dockerfile (multi-stage, alpine/scratch)
2. Generar docker-compose.prod.yml
3. Configurar healthchecks y restart policies
4. Generar nginx.conf si hay frontend
### "Deploy a mi servidor"
1. Build de imagen local
2. Push a registry (Gitea/Docker Hub)
3. Script de deploy via SSH
4. Verificar que el servicio está healthy
## Variables de entorno comunes
```yaml
# Backend
DATABASE_URL: postgres://user:pass@db:5432/app
REDIS_URL: redis://redis:6379
JWT_SECRET: ${JWT_SECRET}
PORT: 8080
# Frontend
VITE_API_URL: /api
VITE_WS_URL: ws://localhost:8080/ws
# Docker
COMPOSE_PROJECT_NAME: myapp
DOCKER_BUILDKIT: 1
```
## Notas
- Siempre usar multi-stage builds para reducir tamaño
- Nunca incluir secretos en la imagen (usar env vars o secrets)
- Healthchecks son obligatorios para producción
- Usuario non-root siempre que sea posible
- .dockerignore es tan importante como Dockerfile
@@ -0,0 +1,85 @@
# Git
.git
.gitignore
.gitattributes
# IDE
.vscode
.idea
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Docker
Dockerfile*
docker-compose*
.docker
# Documentation
*.md
LICENSE
docs/
# Environment
.env
.env.*
!.env.example
# Node.js
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# Build outputs
dist
build
out
*.exe
*.dll
*.so
*.dylib
# Go
bin/
vendor/
*.test
coverage.out
coverage.html
# Test
__tests__
*.test.ts
*.test.tsx
*.spec.ts
*.spec.tsx
e2e/
playwright-report/
test-results/
# Logs
logs
*.log
# Temp
tmp
temp
.tmp
.temp
.cache
# CI/CD
.github
.gitlab-ci.yml
.travis.yml
Jenkinsfile
# Misc
Makefile
*.sh
!entrypoint.sh
@@ -0,0 +1,49 @@
# === BUILD STAGE ===
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Instalar dependencias de compilación si necesario
# RUN apk add --no-cache gcc musl-dev
# Dependencias primero (mejor cache)
COPY go.mod go.sum ./
RUN go mod download && go mod verify
# Código fuente
COPY . .
# Build binario estático
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-w -s -extldflags '-static'" \
-o /app/server ./cmd/server
# === RUNTIME STAGE ===
FROM alpine:3.19
# Certificados SSL y timezone
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /app
# Copiar binario
COPY --from=builder /app/server .
# Copiar archivos estáticos/config si necesario
# COPY --from=builder /app/configs ./configs
# COPY --from=builder /app/migrations ./migrations
# Usuario no-root por seguridad
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
USER appuser
# Puerto de la aplicación
EXPOSE 8080
# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# Punto de entrada
ENTRYPOINT ["./server"]
@@ -0,0 +1,53 @@
# === BUILD STAGE ===
FROM node:22-alpine AS builder
WORKDIR /app
# Habilitar pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Dependencias primero (mejor cache)
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# Código fuente
COPY . .
# Variables de entorno para build (pueden ser sobreescritas)
ARG VITE_API_URL=/api
ENV VITE_API_URL=$VITE_API_URL
# Build de producción
RUN pnpm build
# === RUNTIME STAGE ===
FROM nginx:1.25-alpine
# Remover config por defecto
RUN rm /etc/nginx/conf.d/default.conf
# Copiar configuración nginx
COPY nginx.conf /etc/nginx/nginx.conf
# Copiar archivos estáticos
COPY --from=builder /app/dist /usr/share/nginx/html
# Permisos correctos
RUN chown -R nginx:nginx /usr/share/nginx/html && \
chown -R nginx:nginx /var/cache/nginx && \
chown -R nginx:nginx /var/log/nginx && \
touch /var/run/nginx.pid && \
chown -R nginx:nginx /var/run/nginx.pid
# Usuario no-root
USER nginx
# Puerto
EXPOSE 80
# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:80 || exit 1
# Iniciar nginx
CMD ["nginx", "-g", "daemon off;"]
+115
View File
@@ -0,0 +1,115 @@
#!/bin/bash
# Script de deploy para servidor remoto
# Uso: ./deploy.sh [production|staging]
set -e
# ============================================
# CONFIGURACIÓN
# ============================================
ENV=${1:-production}
REGISTRY="${REGISTRY:-ghcr.io}"
PROJECT="${PROJECT:-myapp}"
VERSION="${VERSION:-latest}"
# Servidor remoto
REMOTE_USER="${REMOTE_USER:-deploy}"
REMOTE_HOST="${REMOTE_HOST:-server.example.com}"
REMOTE_PATH="${REMOTE_PATH:-/opt/$PROJECT}"
# Colores
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
# ============================================
# VALIDACIONES
# ============================================
log "Validando configuración..."
if ! command -v docker &> /dev/null; then
error "Docker no está instalado"
fi
if [ -z "$REMOTE_HOST" ] || [ "$REMOTE_HOST" = "server.example.com" ]; then
error "Configura REMOTE_HOST antes de ejecutar"
fi
# ============================================
# BUILD
# ============================================
log "Building imágenes para $ENV..."
# Build con buildkit para mejor cache
export DOCKER_BUILDKIT=1
docker build -t $REGISTRY/$PROJECT-frontend:$VERSION ./frontend
docker build -t $REGISTRY/$PROJECT-backend:$VERSION ./backend
log "Imágenes construidas:"
docker images | grep $PROJECT
# ============================================
# PUSH A REGISTRY
# ============================================
log "Pushing imágenes a $REGISTRY..."
docker push $REGISTRY/$PROJECT-frontend:$VERSION
docker push $REGISTRY/$PROJECT-backend:$VERSION
log "Imágenes subidas correctamente"
# ============================================
# DEPLOY A SERVIDOR
# ============================================
log "Desplegando a $REMOTE_HOST..."
# Copiar docker-compose si no existe
ssh $REMOTE_USER@$REMOTE_HOST "mkdir -p $REMOTE_PATH"
scp docker-compose.yml $REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/
# Copiar .env si existe
if [ -f ".env.$ENV" ]; then
scp .env.$ENV $REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/.env
fi
# Deploy remoto
ssh $REMOTE_USER@$REMOTE_HOST << EOF
cd $REMOTE_PATH
# Pull nuevas imágenes
docker compose pull
# Restart servicios
docker compose up -d --remove-orphans
# Limpiar imágenes antiguas
docker image prune -f
# Verificar estado
docker compose ps
EOF
# ============================================
# VERIFICACIÓN
# ============================================
log "Verificando deploy..."
sleep 5
# Healthcheck
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://$REMOTE_HOST/health || echo "000")
if [ "$HTTP_STATUS" = "200" ]; then
log "Deploy exitoso! Servidor respondiendo correctamente."
else
warn "Servidor respondió con código: $HTTP_STATUS"
warn "Verifica los logs: ssh $REMOTE_USER@$REMOTE_HOST 'docker compose -f $REMOTE_PATH/docker-compose.yml logs'"
fi
log "Deploy completado para $ENV"
@@ -0,0 +1,100 @@
# Docker Compose para DESARROLLO con hot reload
# Uso: docker compose -f docker-compose.dev.yml up
services:
# ============================================
# FRONTEND (Vite dev server con hot reload)
# ============================================
frontend:
image: node:22-alpine
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-frontend-dev
working_dir: /app
command: sh -c "corepack enable && pnpm install && pnpm dev --host 0.0.0.0"
volumes:
- ./frontend:/app
- frontend_node_modules:/app/node_modules
ports:
- "${FRONTEND_PORT:-5173}:5173"
environment:
- VITE_API_URL=http://localhost:${BACKEND_PORT:-8080}
depends_on:
- backend
networks:
- app-network
# ============================================
# BACKEND (Go con air para hot reload)
# ============================================
backend:
image: cosmtrek/air:latest
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-backend-dev
working_dir: /app
volumes:
- ./backend:/app
- go_mod_cache:/go/pkg/mod
ports:
- "${BACKEND_PORT:-8080}:8080"
environment:
- DATABASE_URL=postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-secret}@db:5432/${POSTGRES_DB:-app}?sslmode=disable
- REDIS_URL=redis://redis:6379
- ENV=development
depends_on:
db:
condition: service_healthy
networks:
- app-network
# ============================================
# DATABASE (PostgreSQL)
# ============================================
db:
image: postgres:16-alpine
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-db-dev
environment:
POSTGRES_USER: ${POSTGRES_USER:-app}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret}
POSTGRES_DB: ${POSTGRES_DB:-app}
volumes:
- postgres_data_dev:/var/lib/postgresql/data
ports:
- "${POSTGRES_PORT:-5432}:5432"
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app}"]
interval: 5s
timeout: 5s
retries: 5
# ============================================
# CACHE (Redis)
# ============================================
redis:
image: redis:7-alpine
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-redis-dev
ports:
- "${REDIS_PORT:-6379}:6379"
networks:
- app-network
# ============================================
# ADMINER (UI para DB - opcional)
# ============================================
adminer:
image: adminer:latest
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-adminer
ports:
- "8081:8080"
depends_on:
- db
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres_data_dev:
frontend_node_modules:
go_mod_cache:
@@ -0,0 +1,119 @@
# Docker Compose para stack completo: Frontend + Backend + DB
# Uso: docker compose -f docker-compose.yml up -d
services:
# ============================================
# FRONTEND (React/Vite + Nginx)
# ============================================
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
VITE_API_URL: /api
image: ${COMPOSE_PROJECT_NAME:-myapp}-frontend:latest
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-frontend
restart: unless-stopped
ports:
- "${FRONTEND_PORT:-3000}:80"
depends_on:
backend:
condition: service_healthy
networks:
- app-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80"]
interval: 30s
timeout: 5s
retries: 3
# ============================================
# BACKEND (Go)
# ============================================
backend:
build:
context: ./backend
dockerfile: Dockerfile
image: ${COMPOSE_PROJECT_NAME:-myapp}-backend:latest
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-backend
restart: unless-stopped
ports:
- "${BACKEND_PORT:-8080}:8080"
environment:
- DATABASE_URL=postgres://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-secret}@db:5432/${POSTGRES_DB:-app}?sslmode=disable
- REDIS_URL=redis://redis:6379
- JWT_SECRET=${JWT_SECRET:-change-me-in-production}
- ENV=${ENV:-production}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 5s
retries: 3
# ============================================
# DATABASE (PostgreSQL)
# ============================================
db:
image: postgres:16-alpine
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-db
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-app}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-secret}
POSTGRES_DB: ${POSTGRES_DB:-app}
volumes:
- postgres_data:/var/lib/postgresql/data
# Opcional: scripts de inicialización
# - ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "${POSTGRES_PORT:-5432}:5432"
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-app} -d ${POSTGRES_DB:-app}"]
interval: 10s
timeout: 5s
retries: 5
# ============================================
# CACHE (Redis)
# ============================================
redis:
image: redis:7-alpine
container_name: ${COMPOSE_PROJECT_NAME:-myapp}-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- redis_data:/data
ports:
- "${REDIS_PORT:-6379}:6379"
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ============================================
# NETWORKS
# ============================================
networks:
app-network:
driver: bridge
# ============================================
# VOLUMES
# ============================================
volumes:
postgres_data:
name: ${COMPOSE_PROJECT_NAME:-myapp}_postgres_data
redis_data:
name: ${COMPOSE_PROJECT_NAME:-myapp}_redis_data
+118
View File
@@ -0,0 +1,118 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
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;
# Buffer sizes
client_body_buffer_size 10K;
client_header_buffer_size 1k;
client_max_body_size 8m;
large_client_header_buffers 4 4k;
# Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
application/atom+xml
image/svg+xml;
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# SPA: todas las rutas van a index.html
location / {
try_files $uri $uri/ /index.html;
}
# Proxy para API backend
location /api/ {
proxy_pass http://backend:8080/;
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_read_timeout 90s;
}
# WebSocket proxy
location /ws {
proxy_pass http://backend:8080/ws;
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_read_timeout 86400;
}
# Cache agresivo para assets estáticos
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# No cachear index.html ni manifest
location ~* \.(html|json)$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# 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 Referrer-Policy "strict-origin-when-cross-origin" always;
# Ocultar versión de nginx
server_tokens off;
# Páginas de error
error_page 404 /index.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}