¿Qué es Docker Compose?
Cuando trabajas en proyectos reales de desarrollo, rara vez tendrás un solo contenedor ejecutándose de forma aislada. La mayoría de aplicaciones modernas requieren múltiples servicios funcionando simultáneamente: una base de datos, un servidor web, un cache, una cola de mensajes, entre otros. Docker Compose resuelve el problema de orquestar estos múltiples contenedores de manera declarativa y reproducible.
Antes de Docker Compose, iniciar un entorno de desarrollo completo significaba ejecutar manualmente múltiples comandos docker run, recordar el orden de ejecución, configurar las redes, linkear los contenedores, y gestionar los volúmenes. Este proceso era propenso a errores y difícil de documentar o compartir con el equipo.
El Archivo docker-compose.yml
El corazón de Docker Compose es el archivo docker-compose.yml. Este archivo YAML define todos los servicios, redes y volúmenes necesarios para tu aplicación. Veamos su estructura básica:
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "80:80"
depends_on:
- api
networks:
- frontend
api:
build: ./api
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://db:5432/app
depends_on:
- db
networks:
- frontend
- backend
db:
image: postgres:14
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=app
- POSTGRES_USER=user
- POSTGRES_PASSWORD=secret
networks:
- backend
networks:
frontend:
backend:
volumes:
postgres_data:
Estructura del Archivo YAML
Versión
El campo version especifica la versión del formato de Docker Compose. Aunque es opcional en versiones recientes, es una buena práctica incluirla para garantizar compatibilidad y comportamiento predecible.
| Versión | Características | Recomendación |
|---|---|---|
| 3.x | Estándar para producción, sin dependencias de Swarm | ✅ Recomendada para la mayoría de casos |
| 2.x | Compatible con versiones anteriores | ⚠️ Solo para legacy systems |
| 3.8+ | Secrets, configs, y características avanzadas | ✅ Para proyectos que lo necesiten |
Services
La sección services define cada contenedor que conforma tu aplicación. Cada servicio puede especificar:
- image: La imagen Docker a utilizar
- build: Ruta al Dockerfile para construir la imagen
- ports: Mapeo de puertos (host:contenedor)
- environment: Variables de entorno
- volumes: Montaje de volúmenes
- depends_on: Dependencias entre servicios
- networks: Redes a las que conectar
build cuando necesitas personalizar la imagen con tu código, y image cuando usas imágenes pre-construidas de Docker Hub.Comandos Esenciales de Docker Compose
- docker-compose up: Crea y arranca todos los servicios definidos. Usa
-dpara ejecutarlos en segundo plano. - docker-compose down: Detiene y elimina todos los contenedores, redes y volúmenes creados por
up. Usa-vpara eliminar también los volúmenes anónimos. - docker-compose build: Construye o reconstruye las imágenes definidas con
build. - docker-compose logs: Muestra los logs de todos los servicios. Usa
-fpara seguimiento en tiempo real. - docker-compose ps: Lista todos los contenedores en ejecución.
- docker-compose exec: Ejecuta un comando dentro de un servicio en ejecución.
- docker-compose restart: Reinicia todos los servicios o los especificados.
Ejemplo Práctico: Stack MEAN
Veamos un ejemplo completo de una aplicación MEAN (MongoDB, Express, Angular, Node.js):
version: '3.8'
services:
mongodb:
image: mongo:5.0
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: password123
volumes:
- mongodb_data:/data/db
ports:
- "27017:27017"
networks:
- backend
backend:
build:
context: ./server
dockerfile: Dockerfile
working_dir: /app
command: npm start
environment:
NODE_ENV: development
MONGODB_URI: mongodb://admin:password123@mongodb:27017/?authSource=admin
PORT: 3000
volumes:
- ./server:/app
- /app/node_modules
ports:
- "3000:3000"
depends_on:
- mongodb
networks:
- backend
frontend:
build:
context: ./client
dockerfile: Dockerfile
stdin_open: true
tty: true
ports:
- "4200:4200"
depends_on:
- backend
networks:
- frontend
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- backend
- frontend
networks:
- frontend
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes:
mongodb_data:
/app/node_modules en el servicio backend es un volumen anónimo. Esto es crucial para evitar que tu directorio local sobrescriba node_modules dentro del contenedor durante el desarrollo.Variables de Entorno y Archivos .env
Docker Compose soporta archivos .env para gestionar variables de entorno. Este archivo debe estar en el mismo directorio que tu docker-compose.yml:
# .env
NODE_ENV=development
MONGODB_URI=mongodb://admin:password123@mongodb:27017/?authSource=admin
API_PORT=3000
CLIENT_PORT=4200
En tu docker-compose.yml, puedes referenciar estas variables:
services:
backend:
environment:
- NODE_ENV=${NODE_ENV}
- MONGODB_URI=${MONGODB_URI}
- PORT=${API_PORT}
.env al repositorio. Agrégalo a tu .gitignore y proporciona un archivo .env.example con valores de referencia para que otros desarrolladores sepan qué variables necesitan configurar.Gestión de Redes
Docker Compose crea automáticamente una red por defecto para todos los servicios. Sin embargo, para arquitecturas más complejas, puedes definir redes personalizadas:
networks:
frontend:
driver: bridge
backend:
driver: bridge
monitoring:
driver: bridge
Los servicios se conectan a las redes especificadas en su propiedad networks. Esto permite:
- Aislamiento de servicios por funcionalidad
- Control de qué servicios pueden comunicarse entre sí
- Segmentación para políticas de seguridad
Override y Extensión de Archivos
Docker Compose permite crear archivos docker-compose.override.yml que automáticamente se aplican sobre el archivo principal. Esto es útil para:
- Configuraciones específicas de desarrollo vs producción
- Personalización local sin modificar el archivo principal
- Separación de configuraciones sensibles
# docker-compose.override.yml (desarrollo local)
services:
backend:
environment:
- DEBUG=true
volumes:
- ./server:/app
frontend:
build:
target: development
docker-compose -f docker-compose.yml -f docker-compose.prod.yml config > docker-compose.full.yml para fusionar y verificar la configuración final.Healthchecks
Define verificaciones de salud para tus servicios:
services:
db:
image: postgres:14
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
Recursos y Límites
services:
api:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Escalado
docker-compose up --scale api=3
Dependencias y Orden de Inicio
El campo depends_on asegura que los servicios inicien en el orden correcto. Sin embargo, es importante entender sus limitaciones:
«depends_on solo garantiza el orden de inicio, no que el servicio dependiente esté listo para aceptar conexiones.»
Para esperar a que un servicio esté realmente disponible, puedes usar:
- Scripts de espera: Espera a que el puerto esté abierto antes de conectar.
- Healthchecks: Define condiciones de salud y espera a que el servicio sea healthy.
- wait-for-it.sh o dockerize: Herramientas específicas para espera de servicios.
services:
api:
depends_on:
db:
condition: service_healthy
db:
image: postgres:14
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
¿Cuál es la principal ventaja de usar un archivo docker-compose.override.yml?
- A) Aumentar el rendimiento de los contenedores
- B) Mantener configuraciones específicas de entorno sin modificar el archivo principal
- C) Eliminar la necesidad de archivos .env
- D) Reemplazar completamente docker-compose.yml
Flujo de Trabajo Típico en Desarrollo
Un flujo de trabajo típico usando Docker Compose sería:
- Clonar el repositorio: git clone <repo-url>
- Configurar entorno: Copiar .env.example a .env y ajustar valores
- Iniciar servicios: docker-compose up -d --build
- Verificar estado: docker-compose ps
- Seguir logs: docker-compose logs -f
- Desarrollar: Editar código, ver cambios reflejados automáticamente
- Detener entorno: docker-compose down (o docker-compose down -v para limpiar volúmenes)
alias dc="docker-compose" y alias dcup="docker-compose up -d --build".Debugging con Docker Compose
Cuando algo no funciona como esperas, Docker Compose ofrece herramientas útiles:
docker-compose config: Valida y muestra la configuración fusionadadocker-compose logs [service]: Revisa los logs de un servicio específicodocker-compose exec [service] sh: Accede al shell del contenedor para inspeccionardocker-compose events: Observa eventos en tiempo real
mongodb://localhost:27017 → mongodb://mongodb:27017).Conclusión
Docker Compose transforma la gestión de aplicaciones multi-contenedor de un proceso manual y propenso a errores en una experiencia declarativa, reproducible y documentada. Dominar su uso es fundamental para cualquier desarrollador que trabaje con contenedores Docker, ya sea en desarrollo local, pruebas automatizadas o despliegues de integración continua.
docker-compose up.