Guía Completa de Docker Compose: Orquestación de Aplicaciones Multi-Contenedor
Docker Compose es una herramienta fundamental que lleva la gestión de contenedores al siguiente nivel. Mientras que Docker te permite ejecutar contenedores individuales, Docker Compose te permite definir y ejecutar aplicaciones multi-contenedor de manera elegante y declarativa.
En esta guía completa, aprenderás a dominar Docker Compose para orquestar aplicaciones complejas, gestionar servicios interconectados y simplificar tu flujo de trabajo de desarrollo y despliegue.
¿Qué es Docker Compose?
Docker Compose es una herramienta que utiliza archivos YAML para definir aplicaciones multi-contenedor. Con un solo comando, puedes crear, iniciar, detener y eliminar todos los servicios definidos en tu aplicación.
Ventajas de usar Docker Compose:
- Simplicidad: Define toda tu aplicación en un archivo YAML
- Reproducibilidad: Misma configuración en desarrollo, testing y producción
- Escalabilidad: Escala servicios individuales según necesidades
- Aislamiento: Cada aplicación tiene su propia red y volúmenes
- Integración: Funciona perfectamente con herramientas de CI/CD
📦 Instalación de Docker Compose
Verificar si Docker Compose está Instalado
Docker Compose v2 viene incluido con Docker Desktop y las instalaciones modernas de Docker Engine:
# Verificar versión de Docker Compose
docker compose version
# Comando legacy (si usas versión antigua)
docker-compose --version
Instalación Manual (si es necesario)
Si no tienes Docker Compose instalado:
# Descargar la última versión
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# Hacer ejecutable
sudo chmod +x /usr/local/bin/docker-compose
# Verificar instalación
docker-compose --version
🚀 Conceptos Fundamentales
Estructura de un archivo docker-compose.yml
version: '3.8' # Versión del formato de Compose
services: # Definición de contenedores
servicio1:
# Configuración del primer servicio
servicio2:
# Configuración del segundo servicio
volumes: # Volúmenes persistentes (opcional)
datos:
networks: # Redes personalizadas (opcional)
mi-red:
Comandos Básicos de Docker Compose
# Iniciar aplicación (crear e iniciar contenedores)
docker compose up
# Iniciar en segundo plano
docker compose up -d
# Reconstruir imágenes antes de iniciar
docker compose up --build
# Detener aplicación
docker compose down
# Ver servicios en ejecución
docker compose ps
# Ver logs de todos los servicios
docker compose logs
# Ver logs de un servicio específico
docker compose logs nombre-servicio
📋 Ejemplo Práctico: Aplicación Web con Base de Datos
Vamos a crear una aplicación completa con frontend, backend y base de datos:
Estructura del Proyecto
mi-aplicacion/
├── docker-compose.yml
├── frontend/
│ ├── Dockerfile
│ └── index.html
├── backend/
│ ├── Dockerfile
│ ├── app.py
│ └── requirements.txt
└── database/
└── init.sql
Frontend (Nginx + HTML)
frontend/Dockerfile:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/
EXPOSE 80
frontend/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Mi App con Docker Compose</title>
</head>
<body>
<h1>Frontend de la Aplicación</h1>
<p>Esta aplicación usa Docker Compose para orquestar múltiples servicios.</p>
<button onclick="fetchData()">Obtener datos del Backend</button>
<div id="resultado"></div>
<script>
async function fetchData() {
try {
const response = await fetch('/api/datos');
const data = await response.json();
document.getElementById('resultado').innerHTML =
`<p>Respuesta del backend: ${JSON.stringify(data)}</p>`;
} catch (error) {
document.getElementById('resultado').innerHTML =
`<p>Error: ${error.message}</p>`;
}
}
</script>
</body>
</html>
Backend (Python Flask)
backend/Dockerfile:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
backend/requirements.txt:
Flask==2.3.2
psycopg2-binary==2.9.6
backend/app.py:
from flask import Flask, jsonify
import psycopg2
import os
app = Flask(__name__)
def get_db_connection():
return psycopg2.connect(
host=os.environ.get('DB_HOST', 'database'),
database=os.environ.get('DB_NAME', 'miapp'),
user=os.environ.get('DB_USER', 'usuario'),
password=os.environ.get('DB_PASSWORD', 'password')
)
@app.route('/api/datos')
def obtener_datos():
try:
conn = get_db_connection()
cur = conn.cursor()
cur.execute('SELECT * FROM usuarios LIMIT 5;')
usuarios = cur.fetchall()
cur.close()
conn.close()
return jsonify({
'status': 'success',
'usuarios': usuarios,
'total': len(usuarios)
})
except Exception as e:
return jsonify({'error': str(e)}), 500
@app.route('/health')
def health_check():
return jsonify({'status': 'healthy'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
Base de Datos (PostgreSQL)
database/init.sql:
-- Crear tabla de usuarios
CREATE TABLE IF NOT EXISTS usuarios (
id SERIAL PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
fecha_creacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insertar datos de ejemplo
INSERT INTO usuarios (nombre, email) VALUES
('Juan Pérez', 'juan@ejemplo.com'),
('María García', 'maria@ejemplo.com'),
('Carlos López', 'carlos@ejemplo.com'),
('Ana Martín', 'ana@ejemplo.com'),
('Luis Rodríguez', 'luis@ejemplo.com');
Docker Compose Configuration
docker-compose.yml:
version: '3.8'
services:
# Servicio de Frontend
frontend:
build: ./frontend
ports:
- "8080:80"
depends_on:
- backend
networks:
- app-network
restart: unless-stopped
# Servicio de Backend
backend:
build: ./backend
ports:
- "5000:5000"
environment:
- DB_HOST=database
- DB_NAME=miapp
- DB_USER=usuario
- DB_PASSWORD=password123
depends_on:
database:
condition: service_healthy
networks:
- app-network
restart: unless-stopped
volumes:
- ./backend:/app # Para desarrollo hot-reload
# Servicio de Base de Datos
database:
image: postgres:15-alpine
environment:
- POSTGRES_DB=miapp
- POSTGRES_USER=usuario
- POSTGRES_PASSWORD=password123
volumes:
- postgres_data:/var/lib/postgresql/data
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app-network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U usuario -d miapp"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# Servicio de Proxy Reverso (Nginx)
proxy:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- frontend
- backend
networks:
- app-network
restart: unless-stopped
# Definición de volúmenes
volumes:
postgres_data:
driver: local
# Definición de redes
networks:
app-network:
driver: bridge
Configuración de Nginx (Proxy Reverso)
nginx.conf:
events {
worker_connections 1024;
}
http {
upstream backend {
server backend:5000;
}
server {
listen 80;
# Servir frontend
location / {
proxy_pass http://frontend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# Proxy para API backend
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
🎛️ Comandos Avanzados de Docker Compose
Gestión de Servicios
# Escalar un servicio específico
docker compose up -d --scale backend=3
# Reconstruir un servicio específico
docker compose build backend
# Reiniciar un servicio
docker compose restart backend
# Detener un servicio específico
docker compose stop backend
# Eliminar servicios y volúmenes
docker compose down -v
Monitoreo y Debugging
# Ver logs en tiempo real
docker compose logs -f
# Ver logs de un servicio específico
docker compose logs -f backend
# Ejecutar comandos en un servicio
docker compose exec backend bash
# Ver estadísticas de recursos
docker compose top
# Inspeccionar configuración
docker compose config
Entornos de Desarrollo vs Producción
docker-compose.override.yml (para desarrollo):
version: '3.8'
services:
backend:
volumes:
- ./backend:/app
environment:
- FLASK_ENV=development
- FLASK_DEBUG=1
command: python app.py
database:
ports:
- "5432:5432" # Exponer DB para herramientas externas
docker-compose.prod.yml (para producción):
version: '3.8'
services:
backend:
restart: always
environment:
- FLASK_ENV=production
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
database:
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M
# Usar configuración de producción
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
🔒 Mejores Prácticas de Seguridad
Variables de Entorno Seguras
.env:
# Base de datos
DB_PASSWORD=mi_password_super_seguro
DB_USER=usuario_app
DB_NAME=produccion_db
# Configuración de aplicación
SECRET_KEY=clave_secreta_muy_larga_y_compleja
API_TOKEN=token_api_seguro
docker-compose.yml con variables:
services:
database:
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_USER=${DB_USER}
- POSTGRES_DB=${DB_NAME}
# No exponer puertos en producción
# ports:
# - "5432:5432"
Health Checks Avanzados
services:
backend:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
🚨 Troubleshooting Común
Problemas de Red
# Verificar redes creadas
docker network ls
# Inspeccionar red de la aplicación
docker network inspect mi-aplicacion_app-network
# Probar conectividad entre servicios
docker compose exec backend ping database
Problemas de Volúmenes
# Listar volúmenes
docker volume ls
# Inspeccionar volumen
docker volume inspect mi-aplicacion_postgres_data
# Backup de volumen
docker run --rm -v mi-aplicacion_postgres_data:/data -v $(pwd):/backup ubuntu tar czf /backup/backup.tar.gz /data
Debugging de Servicios
# Ver configuración final de compose
docker compose config
# Ver eventos en tiempo real
docker compose events
# Verificar estado de servicios
docker compose ps --services
📊 Monitoreo con Docker Compose
Stack de Monitoreo Completo
docker-compose.monitoring.yml:
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- 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'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana_data:/var/lib/grafana
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points'
- '^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)'
volumes:
prometheus_data:
grafana_data:
🎯 Mejores Prácticas Avanzadas
- Usa health checks para todos los servicios críticos
- Implementa restart policies apropiadas para cada servicio
- Separa configuraciones de desarrollo y producción
- Usa .dockerignore para optimizar contextos de build
- Implementa logging centralizado con drivers de log
- Usa networks personalizadas para segmentación
- Configura resource limits para prevenir resource starvation
- Implementa backup strategies para volúmenes de datos
- Usa secrets management para información sensible
- Monitora métricas y logs de aplicaciones
Conclusión
Docker Compose transforma la complejidad de gestionar aplicaciones multi-contenedor en una experiencia simple y declarativa. Con esta guía, tienes todo lo necesario para crear, desplegar y mantener aplicaciones robustas y escalables.
Beneficios clave que has aprendido:
- Orquestación simplificada de servicios múltiples
- Configuración declarativa y versionable
- Separación clara entre entornos
- Escalabilidad horizontal sencilla
- Integración natural con flujos DevOps
Próximos pasos recomendados:
- Explora Docker Swarm para clustering
- Aprende Kubernetes para orquestación enterprise
- Implementa CI/CD con Docker Compose
- Estudia patrones de microservicios avanzados
¡Con Docker Compose, gestionar aplicaciones complejas nunca fue tan sencillo! 🐳✨
In conclusion, achieving cross-browser compatibility is an ongoing commitment that requires careful planning, testing, and adaptation. By prioritizing these strategies, you can ensure that your website provides a consistent and enjoyable experience for users, regardless of the browser they choose to use. Embracing diversity in the digital realm ultimately leads to a broader audience and increased user satisfaction.