Despliegue con Docker
Este ejemplo muestra cómo containerizar y desplegar aplicaciones Fox Framework usando Docker, Docker Compose, y orquestación con Kubernetes y servicios externos como bases de datos, caching y monitoreo.
Dockerfile Optimizado Multi-Stage
# Dockerfile
# Stage 1: Build
FROM node:18-alpine AS builder
# Instalar dependencias del sistema necesarias
RUN apk add --no-cache \
python3 \
make \
g++ \
git
# Crear directorio de trabajo
WORKDIR /app
# Copiar archivos de configuración
COPY package*.json tsconfig.json ./
COPY tsfox/ ./tsfox/
# Instalar dependencias
RUN npm ci --only=production && npm cache clean --force
# Copiar código fuente
COPY src/ ./src/
# Compilar TypeScript
RUN npm run build
# Stage 2: Production
FROM node:18-alpine AS production
# Crear usuario no-root
RUN addgroup -g 1001 -S nodejs && \
adduser -S fox -u 1001
# Instalar dependencias del sistema
RUN apk add --no-cache \
dumb-init \
curl
# Crear directorios
WORKDIR /app
RUN mkdir -p logs temp uploads
# Copiar archivos desde builder
COPY --from=builder --chown=fox:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=fox:nodejs /app/dist ./dist
COPY --from=builder --chown=fox:nodejs /app/package*.json ./
# Copiar assets estáticos si existen
COPY --chown=fox:nodejs public/ ./public/
COPY --chown=fox:nodejs views/ ./views/
# Configurar variables de entorno
ENV NODE_ENV=production
ENV PORT=3000
ENV LOG_LEVEL=info
ENV TEMP_DIR=/app/temp
ENV UPLOAD_DIR=/app/uploads
# Configurar usuario
USER fox
# Exponer puerto
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Usar dumb-init para manejo correcto de señales
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]Docker Compose para Desarrollo
# docker-compose.dev.yml
version: '3.8'
services:
# Aplicación principal
fox-app:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- PORT=3000
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=fox_development
- DB_USER=fox_user
- DB_PASS=fox_password
- REDIS_HOST=redis
- REDIS_PORT=6379
- SMTP_HOST=mailhog
- SMTP_PORT=1025
- JWT_SECRET=dev-secret-key
volumes:
- ./src:/app/src:ro
- ./logs:/app/logs
- ./uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- fox-network
restart: unless-stopped
# Base de datos PostgreSQL
postgres:
image: postgres:15-alpine
environment:
- POSTGRES_DB=fox_development
- POSTGRES_USER=fox_user
- POSTGRES_PASSWORD=fox_password
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U fox_user -d fox_development"]
interval: 10s
timeout: 5s
retries: 5
networks:
- fox-network
restart: unless-stopped
# Redis para caché y sesiones
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
- ./docker/redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
command: redis-server /usr/local/etc/redis/redis.conf
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- fox-network
restart: unless-stopped
# MailHog para testing de emails
mailhog:
image: mailhog/mailhog:latest
ports:
- "1025:1025" # SMTP
- "8025:8025" # Web UI
networks:
- fox-network
restart: unless-stopped
# Nginx como reverse proxy
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./docker/nginx/ssl:/etc/nginx/ssl:ro
- ./public:/var/www/public:ro
depends_on:
- fox-app
networks:
- fox-network
restart: unless-stopped
networks:
fox-network:
driver: bridge
volumes:
postgres_data:
redis_data:Docker Compose para Producción
# docker-compose.prod.yml
version: '3.8'
services:
# Aplicación principal con múltiples réplicas
fox-app:
image: your-registry/fox-framework:${VERSION:-latest}
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
environment:
- NODE_ENV=production
- PORT=3000
- DB_HOST=postgres-master
- DB_PORT=5432
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
- REDIS_HOST=redis-cluster
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- SMTP_HOST=${SMTP_HOST}
- SMTP_USER=${SMTP_USER}
- SMTP_PASS=${SMTP_PASS}
- JWT_SECRET=${JWT_SECRET}
- SENTRY_DSN=${SENTRY_DSN}
volumes:
- app_logs:/app/logs
- app_uploads:/app/uploads
secrets:
- db_password
- jwt_secret
- smtp_credentials
networks:
- fox-production
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# PostgreSQL con configuración de producción
postgres-master:
image: postgres:15-alpine
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
- POSTGRES_INITDB_ARGS=--encoding=UTF-8
volumes:
- postgres_master_data:/var/lib/postgresql/data
- ./docker/postgres/postgresql.conf:/etc/postgresql/postgresql.conf:ro
- ./docker/postgres/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
command: postgres -c config_file=/etc/postgresql/postgresql.conf
secrets:
- db_password
networks:
- fox-production
deploy:
placement:
constraints:
- node.role == manager
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '1.0'
memory: 1G
# Redis Cluster para alta disponibilidad
redis-cluster:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
volumes:
- redis_cluster_data:/data
networks:
- fox-production
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 256M
# Load Balancer con HAProxy
haproxy:
image: haproxy:2.6-alpine
ports:
- "80:80"
- "443:443"
- "8404:8404" # Stats
volumes:
- ./docker/haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- ./docker/ssl:/etc/ssl/certs:ro
depends_on:
- fox-app
networks:
- fox-production
deploy:
placement:
constraints:
- node.role == manager
# Monitoring con Prometheus
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
networks:
- fox-production
# Grafana para visualización
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./docker/grafana/provisioning:/etc/grafana/provisioning:ro
networks:
- fox-production
# Elasticsearch para logs
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.9.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- xpack.security.enabled=false
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
networks:
- fox-production
# Logstash para procesamiento de logs
logstash:
image: docker.elastic.co/logstash/logstash:8.9.0
volumes:
- ./docker/logstash/pipeline:/usr/share/logstash/pipeline:ro
- app_logs:/var/log/app:ro
networks:
- fox-production
depends_on:
- elasticsearch
# Kibana para análisis de logs
kibana:
image: docker.elastic.co/kibana/kibana:8.9.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
networks:
- fox-production
depends_on:
- elasticsearch
secrets:
db_password:
external: true
jwt_secret:
external: true
smtp_credentials:
external: true
networks:
fox-production:
driver: overlay
attachable: true
volumes:
postgres_master_data:
redis_cluster_data:
app_logs:
app_uploads:
prometheus_data:
grafana_data:
elasticsearch_data:Configuración Nginx
# docker/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;
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" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
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;
client_max_body_size 50M;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# 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;
# 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;
# Upstream para la aplicación Fox
upstream fox_app {
least_conn;
server fox-app:3000 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# Configuración principal del servidor
server {
listen 80;
server_name localhost;
# Redirigir HTTP a HTTPS en producción
# return 301 https://$server_name$request_uri;
# Root y índice
root /var/www/public;
index index.html;
# Archivos estáticos
location /static/ {
expires 1y;
add_header Cache-Control "public, no-transform";
access_log off;
}
location /uploads/ {
expires 30d;
add_header Cache-Control "public";
access_log off;
}
# API routes con rate limiting
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://fox_app;
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;
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# Login endpoint con rate limiting más estricto
location /api/auth/login {
limit_req zone=login burst=5 nodelay;
proxy_pass http://fox_app;
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;
}
# Health check
location /health {
proxy_pass http://fox_app;
access_log off;
}
# WebSocket support
location /ws/ {
proxy_pass http://fox_app;
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;
}
# Fallback para SPA
location / {
try_files $uri $uri/ /index.html;
}
# Error pages
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/html;
}
}
# SSL configuration (para producción)
server {
listen 443 ssl http2;
server_name localhost;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Resto de la configuración igual al servidor HTTP
# ... (mismo contenido que arriba)
}
}Scripts de Deployment
#!/bin/bash
# scripts/deploy.sh
set -euo pipefail
# Variables
REGISTRY="your-registry.com"
IMAGE_NAME="fox-framework"
VERSION=${1:-$(git rev-parse --short HEAD)}
ENVIRONMENT=${2:-staging}
echo "🚀 Starting deployment of $IMAGE_NAME:$VERSION to $ENVIRONMENT"
# Build Docker image
echo "📦 Building Docker image..."
docker build -t $REGISTRY/$IMAGE_NAME:$VERSION .
docker tag $REGISTRY/$IMAGE_NAME:$VERSION $REGISTRY/$IMAGE_NAME:latest
# Push to registry
echo "📤 Pushing to registry..."
docker push $REGISTRY/$IMAGE_NAME:$VERSION
docker push $REGISTRY/$IMAGE_NAME:latest
# Deploy based on environment
case $ENVIRONMENT in
"development")
echo "🔧 Deploying to development..."
docker-compose -f docker-compose.dev.yml pull
docker-compose -f docker-compose.dev.yml up -d
;;
"staging")
echo "🎭 Deploying to staging..."
docker-compose -f docker-compose.staging.yml pull
docker-compose -f docker-compose.staging.yml up -d
;;
"production")
echo "🎯 Deploying to production..."
# Usar Docker Swarm o Kubernetes
docker stack deploy -c docker-compose.prod.yml fox-production
;;
*)
echo "❌ Unknown environment: $ENVIRONMENT"
exit 1
;;
esac
# Health check
echo "🏥 Performing health check..."
sleep 30
if curl -f http://localhost/health; then
echo "✅ Deployment successful!"
else
echo "❌ Health check failed!"
exit 1
fi
echo "🎉 Deployment completed successfully!"Kubernetes Deployment
# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: fox-framework
labels:
name: fox-framework
---
# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: fox-app-config
namespace: fox-framework
data:
NODE_ENV: "production"
PORT: "3000"
LOG_LEVEL: "info"
DB_HOST: "postgres-service"
DB_PORT: "5432"
REDIS_HOST: "redis-service"
REDIS_PORT: "6379"
---
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: fox-app-secrets
namespace: fox-framework
type: Opaque
data:
DB_PASSWORD: <base64-encoded-password>
JWT_SECRET: <base64-encoded-jwt-secret>
SMTP_PASS: <base64-encoded-smtp-password>
---
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fox-app
namespace: fox-framework
labels:
app: fox-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
selector:
matchLabels:
app: fox-app
template:
metadata:
labels:
app: fox-app
spec:
containers:
- name: fox-app
image: your-registry/fox-framework:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: fox-app-config
- secretRef:
name: fox-app-secrets
resources:
requests:
memory: "256Mi"
cpu: "200m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: app-logs
mountPath: /app/logs
- name: app-uploads
mountPath: /app/uploads
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: fox-logs-pvc
- name: app-uploads
persistentVolumeClaim:
claimName: fox-uploads-pvc
---
# k8s/service.yaml
apiVersion: v1
kind: Service
metadata:
name: fox-app-service
namespace: fox-framework
spec:
selector:
app: fox-app
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
---
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fox-app-ingress
namespace: fox-framework
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- api.yourapp.com
secretName: fox-app-tls
rules:
- host: api.yourapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fox-app-service
port:
number: 80
---
# k8s/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: fox-app-hpa
namespace: fox-framework
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: fox-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80Monitoring y Logging
# docker/prometheus/prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert_rules.yml"
scrape_configs:
- job_name: 'fox-app'
static_configs:
- targets: ['fox-app:3000']
metrics_path: '/metrics'
scrape_interval: 5s
- job_name: 'nginx'
static_configs:
- targets: ['nginx:80']
- job_name: 'postgres'
static_configs:
- targets: ['postgres:5432']
- job_name: 'redis'
static_configs:
- targets: ['redis:6379']
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093Variables de Entorno
# .env.production
# Application
NODE_ENV=production
PORT=3000
LOG_LEVEL=info
# Database
DB_HOST=postgres-cluster
DB_PORT=5432
DB_NAME=fox_production
DB_USER=fox_user
DB_PASS=super-secure-password
# Redis
REDIS_HOST=redis-cluster
REDIS_PORT=6379
REDIS_PASSWORD=redis-secure-password
# JWT
JWT_SECRET=super-secure-jwt-secret-key
JWT_REFRESH_SECRET=super-secure-refresh-secret
# SMTP
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=sendgrid-api-key
# Monitoring
SENTRY_DSN=https://your-sentry-dsn
PROMETHEUS_ENDPOINT=/metrics
# File uploads
MAX_FILE_SIZE=50MB
UPLOAD_DIR=/app/uploads
ALLOWED_FILE_TYPES=jpg,jpeg,png,pdf,doc,docxCaracterísticas Destacadas
- Multi-Stage Builds: Optimización de tamaño de imagen y seguridad
- Health Checks: Monitoreo automático de salud de contenedores
- Load Balancing: Distribución de carga con Nginx y HAProxy
- Auto-scaling: Escalado horizontal automático con Kubernetes HPA
- SSL/TLS: Configuración completa de HTTPS con certificados
- Monitoring Stack: Prometheus, Grafana, ELK para observabilidad
- Secret Management: Manejo seguro de credenciales y configuración
- Rolling Updates: Despliegues sin downtime
- Resource Limits: Control de recursos CPU y memoria
- Persistent Storage: Volúmenes persistentes para datos y logs