Despliegue con Caddy y Docker
Este documento describe el proceso completo de despliegue de UnoSportClub utilizando Caddy como reverse proxy y Docker para contenedorización, incluyendo configuraciones para entornos QA, Staging y Production.
Arquitectura de Despliegue
Componentes del Sistema
El sistema está compuesto por los siguientes componentes desplegados como contenedores Docker:
-
Backend (Functions): API REST implementada en Node.js/Express
-
Frontend Applications: 4 aplicaciones Angular independientes
-
unosport-app: Aplicación principal para usuarios finales -
unosport-panel: Panel de operador/administrador -
unosport-trainer: Panel de entrenador -
unosport-sudo: Panel de super administrador -
Caddy: Reverse proxy y load balancer con terminación SSL/TLS automática
-
PostgreSQL: Base de datos relacional (solo para Staging y QA, Production usa Cloud SQL externa)
Entornos de Despliegue
El sistema soporta tres entornos independientes:
-
Production: Ambiente de producción con datos reales
-
Staging: Ambiente de pruebas previo a producción
-
QA: Ambiente de control de calidad y testing
Cada entorno tiene: * Sus propios contenedores Docker * Su propia base de datos PostgreSQL (Staging y QA) * Sus propios subdominios * Configuraciones independientes
Configuración de Docker Compose
Estructura del docker-compose.yml
El archivo docker-compose.yml define todos los servicios necesarios:
version: '3.8'
services:
# Production Backend
unosport-backend:
image: ghcr.io/cortex-ia-com-co/unosportclub-functions:latest
pull_policy: always
restart: unless-stopped
user: 10000:10000
volumes:
- ./unosportclub/firebase/firebase-adminsdk.json:/firebase-adminsdk.json:ro
- ./unosportclub/.env:/app/.env:ro
networks:
- n8n_frontend
# Production Frontend Applications
unosport-app:
image: ghcr.io/cortex-ia-com-co/unosportclub:v0.0.1-p1
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-panel:
image: ghcr.io/cortex-ia-com-co/unosportclub-panel:v0.1.8
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-trainer:
image: ghcr.io/cortex-ia-com-co/unosportclub-trainer:latest
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-sudo:
image: ghcr.io/cortex-ia-com-co/unosportclub-sudo:latest
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
# Staging Environment
unosport-db-staging:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_NAME:-unosportclub_staging}
POSTGRES_USER: ${DB_USER:-unosport_admin}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- unosport_db_staging:/var/lib/postgresql/data
networks:
- n8n_frontend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-unosport_admin}"]
interval: 10s
timeout: 5s
retries: 5
unosport-backend-staging:
image: ghcr.io/cortex-ia-com-co/unosportclub-functions:staging
pull_policy: always
restart: unless-stopped
user: 10000:10000
depends_on:
unosport-db-staging:
condition: service_healthy
environment:
NODE_ENV: staging
DATABASE_URL: postgresql://${DB_USER:-unosport_admin}:${DB_PASSWORD}@unosport-db-staging:5432/${DB_NAME:-unosportclub_staging}
DB_HOST: unosport-db-staging
DB_PORT: 5432
DB_NAME: ${DB_NAME:-unosportclub_staging}
DB_USER: ${DB_USER:-unosport_admin}
DB_PASSWORD: ${DB_PASSWORD}
DB_SSL: "false"
GOOGLE_APPLICATION_CREDENTIALS: /firebase-adminsdk.json
FIREBASE_PROJECT_ID: unosportclubdev
FIREBASE_REGION: us-central1
volumes:
- ./unosportclub/firebase/firebase-adminsdk.json:/firebase-adminsdk.json:ro
command: >
sh -c "
cd /app &&
node db/install.js &&
node server_wrapper.js
"
networks:
- n8n_frontend
unosport-app-staging:
image: ghcr.io/cortex-ia-com-co/unosportclub:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-panel-staging:
image: ghcr.io/cortex-ia-com-co/unosportclub-panel:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-trainer-staging:
image: ghcr.io/cortex-ia-com-co/unosportclub-trainer:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-sudo-staging:
image: ghcr.io/cortex-ia-com-co/unosportclub-sudo:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
# QA Environment
unosport-db-qa:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_NAME_QA:-unosportclub_qa}
POSTGRES_USER: ${DB_USER_QA:-unosport_admin}
POSTGRES_PASSWORD: ${DB_PASSWORD_QA:-${DB_PASSWORD}}
volumes:
- unosport_db_qa:/var/lib/postgresql/data
networks:
- n8n_frontend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER_QA:-unosport_admin}"]
interval: 10s
timeout: 5s
retries: 5
unosport-backend-qa:
image: ghcr.io/cortex-ia-com-co/unosportclub-functions:staging
pull_policy: always
restart: unless-stopped
user: 10000:10000
depends_on:
unosport-db-qa:
condition: service_healthy
environment:
NODE_ENV: staging
DATABASE_URL: postgresql://${DB_USER_QA:-unosport_admin}:${DB_PASSWORD_QA:-${DB_PASSWORD}}@unosport-db-qa:5432/${DB_NAME_QA:-unosportclub_qa}
DB_HOST: unosport-db-qa
DB_PORT: 5432
DB_NAME: ${DB_NAME_QA:-unosportclub_qa}
DB_USER: ${DB_USER_QA:-unosport_admin}
DB_PASSWORD: ${DB_PASSWORD_QA:-${DB_PASSWORD}}
DB_SSL: "false"
GOOGLE_APPLICATION_CREDENTIALS: /firebase-adminsdk.json
FIREBASE_PROJECT_ID: unosportclubdev
FIREBASE_REGION: us-central1
volumes:
- ./unosportclub/firebase/firebase-adminsdk.json:/firebase-adminsdk.json:ro
command: >
sh -c "
cd /app &&
node db/install.js &&
node server_wrapper.js
"
networks:
- n8n_frontend
unosport-app-qa:
image: ghcr.io/cortex-ia-com-co/unosportclub:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-panel-qa:
image: ghcr.io/cortex-ia-com-co/unosportclub-panel:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-trainer-qa:
image: ghcr.io/cortex-ia-com-co/unosportclub-trainer:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
unosport-sudo-qa:
image: ghcr.io/cortex-ia-com-co/unosportclub-sudo:staging
pull_policy: always
restart: unless-stopped
networks:
- n8n_frontend
# Caddy Reverse Proxy
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
user: 10000:10000
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy_data:/data
- ./caddy_config:/config
networks:
- n8n_frontend
networks:
n8n_frontend:
volumes:
unosport_db_staging:
driver: local
unosport_db_qa:
driver: local
Características Importantes
Health Checks
Los servicios de base de datos incluyen health checks para asegurar que estén listos antes de iniciar los servicios dependientes:
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-unosport_admin}"]
interval: 10s
timeout: 5s
retries: 5
Configuración de Caddy
Estructura del Caddyfile
El archivo Caddyfile define el enrutamiento para todos los entornos:
# Production Environment
app.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend:3000
reverse_proxy unosport-app:80
}
entrenador.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend:3000
reverse_proxy unosport-trainer:80
}
panel.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend:3000
reverse_proxy unosport-panel:80
}
control.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend:3000
reverse_proxy unosport-sudo:80
}
# Staging Environment
app.staging.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-staging:3000
reverse_proxy unosport-app-staging:80
}
entrenador.staging.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-staging:3000
reverse_proxy unosport-trainer-staging:80
}
panel.staging.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-staging:3000
reverse_proxy unosport-panel-staging:80
}
control.staging.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-staging:3000
reverse_proxy unosport-sudo-staging:80
}
# QA Environment
app.qa.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-qa:3000
reverse_proxy unosport-app-qa:80
}
entrenador.qa.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-qa:3000
reverse_proxy unosport-trainer-qa:80
}
panel.qa.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-qa:3000
reverse_proxy unosport-panel-qa:80
}
control.qa.unosportclub.com.co {
encode zstd gzip
reverse_proxy /api* unosport-backend-qa:3000
reverse_proxy unosport-sudo-qa:80
}
Variables de Entorno
Archivo .env
El archivo .env en /opt/n8n/unosportclub/.env contiene la configuración de producción:
# Configuración de Base de Datos - Functions
DATABASE_URL="postgresql://unosportclub:GtTFWfP39IA643IrV1GxWJ5DVJAmBIif@pegasus.cortex-ia.com.co/unosportclub"
GOOGLE_APPLICATION_CREDENTIALS=/firebase-adminsdk.json
PORT=3000
HOST=0.0.0.0
Variables para Staging y QA
Las variables de entorno para Staging y QA se definen directamente en docker-compose.yml:
environment:
NODE_ENV: staging
DATABASE_URL: postgresql://${DB_USER:-unosport_admin}:${DB_PASSWORD}@unosport-db-staging:5432/${DB_NAME:-unosportclub_staging}
DB_HOST: unosport-db-staging
DB_PORT: 5432
DB_NAME: ${DB_NAME:-unosportclub_staging}
DB_USER: ${DB_USER:-unosport_admin}
DB_PASSWORD: ${DB_PASSWORD}
DB_SSL: "false"
GOOGLE_APPLICATION_CREDENTIALS: /firebase-adminsdk.json
FIREBASE_PROJECT_ID: unosportclubdev
FIREBASE_REGION: us-central1
Proceso de Despliegue
Despliegue Inicial
-
Clonar configuración de despliegue:
cd /opt/n8n git clone <repository-url> cd unosportclub -
Configurar variables de entorno:
cp .env.example .env # Editar .env con credenciales reales -
Configurar credenciales de Firebase:
# Colocar firebase-adminsdk.json en ./unosportclub/firebase/ -
Iniciar servicios:
docker-compose up -d
Actualización de Servicios
Para actualizar un servicio específico:
# Actualizar backend de producción
docker-compose pull unosport-backend
docker-compose up -d unosport-backend
# Actualizar aplicación frontend
docker-compose pull unosport-app
docker-compose up -d unosport-app
# Actualizar todos los servicios de un entorno
docker-compose pull unosport-backend-staging unosport-app-staging unosport-panel-staging unosport-trainer-staging unosport-sudo-staging
docker-compose up -d unosport-backend-staging unosport-app-staging unosport-panel-staging unosport-trainer-staging unosport-sudo-staging
Reinicio de Servicios
# Reiniciar un servicio específico
docker-compose restart unosport-backend
# Reiniciar todos los servicios de un entorno
docker-compose restart unosport-backend-staging unosport-app-staging unosport-panel-staging unosport-trainer-staging unosport-sudo-staging
# Reiniciar todos los servicios
docker-compose restart
Gestión de Base de Datos
Migraciones Automáticas
Los servicios de backend ejecutan automáticamente las migraciones al iniciar mediante el comando:
node db/install.js
Este script: * Verifica la conexión a la base de datos * Ejecuta migraciones pendientes * Carga seeders si es necesario
Migración Inicial Manual dentro del Contenedor
En algunos casos, puede ser necesario ejecutar las migraciones manualmente dentro del contenedor, especialmente cuando:
-
Las migraciones automáticas fallaron durante el inicio
-
Necesitas ejecutar migraciones en un contenedor ya corriendo
-
Quieres verificar el estado de las migraciones antes de iniciar el servidor
-
Necesitas ejecutar migraciones sin reiniciar el servicio
Ejecutar Migraciones en Contenedor Corriendo
Para ejecutar migraciones en un contenedor que ya está corriendo:
# Staging - Ejecutar migraciones dentro del contenedor
docker-compose exec unosport-backend-staging sh -c "cd /app && node db/install.js"
# QA - Ejecutar migraciones dentro del contenedor
docker-compose exec unosport-backend-qa sh -c "cd /app && node db/install.js"
Verificar Estado de Migraciones
Para verificar qué migraciones se han ejecutado:
# Conectarse al contenedor de backend
docker-compose exec unosport-backend-staging sh
# Dentro del contenedor, ejecutar:
cd /app
node -e "const {pool} = require('./db/index'); pool.query('SELECT * FROM schema_migrations ORDER BY id').then(r => {console.table(r.rows); pool.end();})"
O usando psql directamente en el contenedor de base de datos:
# Ver migraciones ejecutadas en Staging
docker-compose exec unosport-db-staging psql -U unosport_admin -d unosportclub_staging -c "SELECT * FROM schema_migrations ORDER BY id;"
# Ver migraciones ejecutadas en QA
docker-compose exec unosport-db-qa psql -U unosport_admin -d unosportclub_qa -c "SELECT * FROM schema_migrations ORDER BY id;"
Ejecutar Migraciones sin Seeders
Si necesitas ejecutar solo las migraciones sin los seeders:
# Staging - Solo migraciones
docker-compose exec unosport-backend-staging sh -c "cd /app && node -e \"const {runMigrations, pool} = require('./db/index'); runMigrations().then(() => pool.end())\""
# QA - Solo migraciones
docker-compose exec unosport-backend-qa sh -c "cd /app && node -e \"const {runMigrations, pool} = require('./db/index'); runMigrations().then(() => pool.end())\""
Ejecutar Migraciones en Contenedor Nuevo (antes de iniciar servidor)
Si necesitas ejecutar migraciones antes de iniciar el servidor, puedes usar un contenedor temporal:
# Staging - Ejecutar migraciones con contenedor temporal
docker-compose run --rm unosport-backend-staging sh -c "cd /app && node db/install.js"
# QA - Ejecutar migraciones con contenedor temporal
docker-compose run --rm unosport-backend-qa sh -c "cd /app && node db/install.js"
El flag --rm elimina automáticamente el contenedor después de ejecutar el comando.
Verificar Logs de Migración
Para ver los logs detallados de la ejecución de migraciones:
# Ver logs del backend durante inicio (muestra migraciones automáticas)
docker-compose logs unosport-backend-staging | grep -i migration
# Ver logs completos del backend
docker-compose logs unosport-backend-staging
# Seguir logs en tiempo real
docker-compose logs -f unosport-backend-staging
Solución de Problemas Comunes
Error: "Cannot connect to database"
Verifica que la base de datos esté corriendo y saludable:
# Verificar estado de base de datos
docker-compose ps unosport-db-staging
# Verificar salud de base de datos
docker-compose exec unosport-db-staging pg_isready -U unosport_admin
# Verificar variables de entorno en el contenedor
docker-compose exec unosport-backend-staging env | grep DB_
Error: "Migration already executed"
Este es un comportamiento normal. El sistema registra las migraciones ejecutadas en la tabla schema_migrations y no las ejecuta dos veces.
Error: "Permission denied"
Verifica que el usuario del contenedor tenga permisos adecuados. El contenedor se ejecuta con user: 10000:10000 por seguridad.
Re-ejecutar migraciones fallidas
Si una migración falló parcialmente, puedes verificar el estado y ejecutar manualmente:
# Ver estado de migraciones
docker-compose exec unosport-db-staging psql -U unosport_admin -d unosportclub_staging -c "SELECT * FROM schema_migrations ORDER BY id;"
# Ejecutar migraciones manualmente
docker-compose exec unosport-backend-staging sh -c "cd /app && node db/install.js"
Acceso a Base de Datos
Para acceder a una base de datos de un entorno:
# Staging
docker-compose exec unosport-db-staging psql -U unosport_admin -d unosportclub_staging
# QA
docker-compose exec unosport-db-qa psql -U unosport_admin -d unosportclub_qa
Backup de Base de Datos
# Backup de Staging
docker-compose exec unosport-db-staging pg_dump -U unosport_admin unosportclub_staging > backup_staging_$(date +%Y%m%d_%H%M%S).sql
# Backup de QA
docker-compose exec unosport-db-qa pg_dump -U unosport_admin unosportclub_qa > backup_qa_$(date +%Y%m%d_%H%M%S).sql
Monitoreo y Logs
Seguridad
Usuario no-root
Todos los servicios se ejecutan con usuario no-root (user: 10000:10000) para mayor seguridad.
Volúmenes de Solo Lectura
Los archivos de configuración se montan como solo lectura (:ro):
volumes:
- ./unosportclub/firebase/firebase-adminsdk.json:/firebase-adminsdk.json:ro
- ./unosportclub/.env:/app/.env:ro
Solución de Problemas
Servicio No Inicia
# Verificar logs
docker-compose logs unosport-backend-staging
# Verificar configuración
docker-compose config
# Verificar dependencias
docker-compose ps unosport-db-staging
Error de Conexión a Base de Datos
# Verificar que la base de datos esté saludable
docker-compose exec unosport-db-staging pg_isready -U unosport_admin
# Verificar variables de entorno
docker-compose exec unosport-backend-staging env | grep DB_
# Verificar conectividad de red
docker-compose exec unosport-backend-staging ping unosport-db-staging
Mejores Prácticas
Gestión de Versiones
-
Usar tags específicos en lugar de
latestpara producción -
Probar actualizaciones en QA antes de Staging
-
Probar en Staging antes de Production
Backups Regulares
-
Realizar backups diarios de bases de datos
-
Almacenar backups en ubicación segura
-
Probar restauración de backups periódicamente