Despliegue de Aplicaciones Multi-Contenedor con Docker Compose: Guía Completa
Este tutorial te guiará a través del proceso de despliegue de aplicaciones multi-contenedor utilizando Docker Compose. Aprenderás a definir, configurar y ejecutar servicios interconectados, simplificando la orquestación de tus proyectos Docker.
Docker ha revolucionado la forma en que los desarrolladores empaquetan y despliegan aplicaciones. Sin embargo, en un entorno de producción o incluso en desarrollo, es raro encontrar una aplicación que consista en un solo contenedor. La mayoría de las aplicaciones modernas están compuestas por múltiples servicios interconectados, como una base de datos, un backend API, un frontend web, un sistema de caché, etc.
Aquí es donde entra en juego Docker Compose: una herramienta esencial para definir y ejecutar aplicaciones Docker multi-contenedor. Con Docker Compose, utilizas un único archivo YAML para configurar todos los servicios de tu aplicación, lo que facilita su orquestación, gestión y despliegue.
En este tutorial, exploraremos Docker Compose en profundidad, desde su instalación hasta la creación de un archivo docker-compose.yml para una aplicación web real, pasando por la gestión de volúmenes, redes y variables de entorno.
🎯 ¿Qué aprenderás en este tutorial?
- Entender la necesidad y los beneficios de Docker Compose.
- Instalar Docker Compose en tu sistema.
- Crear un archivo
docker-compose.ymlpara una aplicación multi-servicio. - Definir servicios, imágenes, puertos, volúmenes y redes.
- Ejecutar y gestionar tu aplicación con comandos de Docker Compose.
- Manejar la persistencia de datos y las variables de entorno.
- Desplegar una aplicación web de ejemplo con un frontend, backend y base de datos.
🚀 ¿Por qué Docker Compose es indispensable?
Imagina que tienes una aplicación web que requiere:
- Un servidor frontend (por ejemplo, Nginx sirviendo React).
- Un servidor backend (por ejemplo, una API en Node.js o Python).
- Una base de datos (por ejemplo, PostgreSQL o MySQL).
- Un sistema de caché (por ejemplo, Redis).
Sin Docker Compose, tendrías que iniciar cada contenedor individualmente, gestionando sus redes, volúmenes y dependencias de forma manual. Esto puede ser tedioso, propenso a errores y difícil de replicar en diferentes entornos.
Ventajas clave de Docker Compose:
- Definición de entornos repetibles: Define tu pila de aplicación una vez y replícala en cualquier lugar.
- Orquestación simplificada: Inicia, detiene y gestiona múltiples servicios con un solo comando.
- Aislamiento y comunicación: Cada servicio se ejecuta en su propio contenedor y pueden comunicarse fácilmente entre sí a través de redes definidas.
- Persistencia de datos: Gestiona volúmenes para asegurar que tus datos persisten más allá del ciclo de vida de los contenedores.
- Desarrollo local eficiente: Facilita la configuración de entornos de desarrollo que imitan la producción.
🛠️ Instalación de Docker Compose
Docker Compose se distribuye con Docker Desktop para Windows y macOS, por lo que si ya tienes Docker Desktop instalado, ¡probablemente ya lo tengas! Puedes verificarlo abriendo una terminal y ejecutando:
docker compose version
Si ves una versión, ¡estás listo! Si no, o si estás en Linux, sigue las instrucciones a continuación.
Instalación en Linux (método recomendado para el plugin CLI):
Generalmente, docker compose se instala junto con Docker Engine. Asegúrate de tener la última versión de Docker Engine. Si necesitas instalarlo por separado (versiones antiguas), puedes usar pip o descargar el binario directamente.
1. Actualizar Docker Engine (recomendado):
Sigue la guía oficial de instalación de Docker para tu distribución de Linux. Por ejemplo, para sistemas basados en Debian/Ubuntu:
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
2. Verificación:
docker compose version
Deberías ver una salida similar a:
Docker Compose version v2.x.x
📖 Anatomía de un archivo docker-compose.yml
El corazón de Docker Compose es el archivo docker-compose.yml. Este archivo define los servicios que componen tu aplicación, sus configuraciones, redes y volúmenes. Es un archivo YAML, lo que significa que la indentación es crucial.
Estructura básica:
version: '3.8' # Versión del formato de archivo Compose
services:
web:
# Configuración del servicio web
db:
# Configuración del servicio de base de datos
volumes:
# Definición de volúmenes con nombre
networks:
# Definición de redes personalizadas
Secciones clave:
-
version: Define la versión del formato de archivo Compose. Se recomienda usar la versión3.xpara las características más recientes. -
services: Es la sección principal donde defines cada contenedor que forma parte de tu aplicación. Cada servicio es un nombre arbitrario (por ejemplo,web,db,api) que encapsula la configuración de un contenedor. -
volumes: Aquí defines los volúmenes con nombre que utilizarán tus servicios para la persistencia de datos. Los volúmenes aseguran que tus datos no se pierdan cuando los contenedores son eliminados. -
networks: Permite definir redes personalizadas para que tus servicios se comuniquen entre sí de forma segura y aislada. Por defecto, Compose crea una red predeterminada para todos los servicios.
💻 Creando nuestra primera aplicación multi-contenedor
Vamos a crear una aplicación simple que consiste en un servicio web (usaremos Nginx para servir una página HTML estática) y un servicio de base de datos (PostgreSQL).
Paso 1: Estructura del proyecto
Crea una carpeta para tu proyecto y dentro de ella, los siguientes archivos y directorios:
mi-app-compose/
├── docker-compose.yml
└── web/
└── index.html
Paso 2: Crear el archivo index.html
En el directorio web/, crea un archivo index.html con el siguiente contenido:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi Aplicación Docker Compose</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 50px; background-color: #f4f4f4; }
h1 { color: #333; }
p { color: #666; }
.container { background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); display: inline-block; }
</style>
</head>
<body>
<div class="container">
<h1>👋 ¡Hola desde Docker Compose!</h1>
<p>Esta es una aplicación web simple sirviendo Nginx.</p>
<p>Base de datos PostgreSQL también está en ejecución.</p>
</div>
</body>
</html>
Paso 3: Crear el archivo docker-compose.yml
En la raíz de tu proyecto (mi-app-compose/), crea el archivo docker-compose.yml:
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80" # Mapea el puerto 80 del host al puerto 80 del contenedor Nginx
volumes:
- ./web:/usr/share/nginx/html # Monta nuestro directorio 'web' en el directorio de servicio de Nginx
depends_on:
- db # Indica que el servicio 'web' depende de 'db' (no inicia 'web' hasta que 'db' esté listo)
networks:
- app_network
db:
image: postgres:13
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data # Persistencia de datos para PostgreSQL
networks:
- app_network
volumes:
db_data: # Define un volumen con nombre para la base de datos
networks:
app_network: # Define una red personalizada para nuestros servicios
driver: bridge
Analicemos este archivo:
-
webservicio:image: nginx:latest: Usa la imagen oficial de Nginx más reciente.ports: "80:80": Mapea el puerto 80 del host al puerto 80 del contenedor. Esto significa que podrás acceder a tu aplicación a través dehttp://localhost:80ohttp://tu_ip.volumes: ./web:/usr/share/nginx/html: Monta el directoriowebde nuestro host (donde estáindex.html) dentro del contenedor Nginx, en la ubicación donde Nginx sirve contenido estático.depends_on: - db: Una instrucción que indica a Docker Compose que el serviciowebtiene una dependencia del serviciodb. Esto asegura quedbse inicie antes queweb. Importante:depends_onsolo asegura el orden de inicio, no espera a que el servicio esté completamente listo para aceptar conexiones.networks: - app_network: Conecta este servicio a nuestra red personalizadaapp_network.
-
dbservicio:image: postgres:13: Utiliza la imagen oficial de PostgreSQL versión 13.environment: Establece variables de entorno requeridas por la imagen de PostgreSQL para configurar la base de datos (nombre, usuario, contraseña).volumes: db_data:/var/lib/postgresql/data: Monta un volumen con nombre llamadodb_dataen el directorio donde PostgreSQL almacena sus datos. Esto asegura que los datos de tu base de datos persistan incluso si eliminas el contenedordb.networks: - app_network: Conecta este servicio a la misma red personalizada.
-
volumessección:db_data:: Declara el volumen con nombredb_dataque será utilizado por el serviciodb.
-
networkssección:app_network:: Declara una red personalizada llamadaapp_networkcon el driverbridge(el driver predeterminado para redes de Docker). Todos los servicios en esta red pueden comunicarse entre sí por su nombre de servicio (por ejemplo,webpuede conectarse adbusandodbcomo hostname).
🏃 Ejecutando tu aplicación con Docker Compose
Navega a la raíz de tu proyecto (mi-app-compose/) en la terminal y ejecuta el siguiente comando:
docker compose up -d
up: Crea y inicia los contenedores para todos los servicios definidos endocker-compose.yml.-d: Ejecuta los contenedores en modo detached (segundo plano), liberando tu terminal.
Verás una salida que indica la creación de la red, los volúmenes y el inicio de los contenedores.
[+] Running 3/3
✔ Network mi-app-compose_app_network Created 0.0s
✔ Volume "mi-app-compose_db_data" Created 0.0s
✔ Container mi-app-compose-db-1 Started 0.8s
✔ Container mi-app-compose-web-1 Started 0.8s
Ahora, abre tu navegador y visita http://localhost (o http://tu_ip). Deberías ver la página index.html servida por Nginx.
Comandos útiles de Docker Compose:
- Ver el estado de los servicios:
docker compose ps
Esto te mostrará los contenedores en ejecución, sus puertos mapeados y su estado.
- Ver logs de los servicios:
docker compose logs
Para ver los logs de un servicio específico (por ejemplo, `web`):
docker compose logs web
- Detener los servicios:
docker compose stop
Esto detendrá los contenedores, pero no los eliminará. Puedes reiniciarlos con `docker compose start`.
- Detener y eliminar los servicios (y redes):
docker compose down
Esto detendrá y eliminará los contenedores y las redes creadas por Compose. Por defecto, los volúmenes con nombre *no* se eliminan para proteger tus datos. Si quieres eliminar también los volúmenes:
docker compose down -v
<div class="callout warning">⚠️ <strong>Advertencia:</strong> `docker compose down -v` eliminará los datos persistentes de tus volúmenes. ¡Úsalo con precaución!</div>
- Reconstruir imágenes de servicios:
Si has realizado cambios en el
Dockerfilede un servicio o en su contexto de construcción, puedes reconstruir la imagen y reiniciar el servicio:
docker compose up --build -d
- Ejecutar un comando dentro de un servicio:
docker compose exec web bash
Esto te dará un shell dentro del contenedor `web`.
💾 Persistencia de Datos con Volúmenes
La persistencia de datos es crucial para aplicaciones que manejan información importante, como bases de datos. Los contenedores son por naturaleza efímeros; si un contenedor es eliminado, cualquier dato almacenado dentro de él se perderá. Docker Compose, junto con los volúmenes de Docker, resuelve este problema.
En nuestro docker-compose.yml, definimos un volumen con nombre db_data para PostgreSQL:
volumes:
db_data:
Y lo montamos en el servicio db:
db:
# ...
volumes:
- db_data:/var/lib/postgresql/data
Esto significa que Docker gestionará un volumen en tu sistema de archivos del host (ubicación específica de Docker) que está vinculado al directorio /var/lib/postgresql/data dentro del contenedor db. Aunque elimines el contenedor db con docker compose down, los datos en db_data persistirán. Cuando vuelvas a iniciar el servicio db con docker compose up, se volverá a conectar a ese mismo volumen y tus datos estarán ahí.
¿Qué pasa si no uso volúmenes para mi base de datos?
Si no montaras un volumen en la base de datos, cada vez que el contenedor fuera eliminado y recreado (por ejemplo, con `docker compose down` seguido de `docker compose up`), todos los datos de tu base de datos se perderían. Los volúmenes son esenciales para la persistencia de datos críticos.🌐 Redes en Docker Compose
Docker Compose crea una red por defecto para tu aplicación, lo que permite que todos los servicios se comuniquen entre sí utilizando sus nombres de servicio como hostnames. En nuestro ejemplo, el servicio web puede "ver" al servicio db simplemente haciendo peticiones a db:5432 (el puerto predeterminado de PostgreSQL).
Aunque una red por defecto funciona bien para muchos casos, definir redes personalizadas (app_network en nuestro ejemplo) ofrece varias ventajas:
- Claridad: Hace explícita la arquitectura de red de tu aplicación.
- Aislamiento: Permite que diferentes aplicaciones Compose compartan la misma máquina Docker sin interferir en sus redes.
- Control: Puedes especificar drivers de red o configuraciones más avanzadas.
networks:
app_network: # Nombre de nuestra red personalizada
driver: bridge # Tipo de driver de red (bridge es el predeterminado)
Y luego conectas cada servicio a esta red:
web:
# ...
networks:
- app_network
db:
# ...
networks:
- app_network
Esto asegura que web y db están en la misma red y pueden comunicarse. Si tuvieras otro grupo de servicios que no necesitan interactuar con esta aplicación, podrían estar en una red diferente para un mejor aislamiento.
⚙️ Variables de Entorno y Configuración Dinámica
Las variables de entorno son una forma común de pasar configuraciones a los servicios sin codificarlas directamente en el archivo docker-compose.yml o en las imágenes. Esto es especialmente útil para credenciales de bases de datos, claves API u otros valores que cambian entre entornos (desarrollo, pruebas, producción).
En nuestro servicio db, usamos la sección environment:
db:
# ...
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
Docker Compose soporta la interpolación de variables de entorno del host. Puedes definir un archivo .env en el mismo directorio que tu docker-compose.yml para cargar variables automáticamente.
Ejemplo con .env:
Crea un archivo .env en mi-app-compose/:
# .env
DB_NAME=mydatabase
DB_USER=user
DB_PASSWORD=secretpassword
Luego, modifica tu docker-compose.yml para usar estas variables:
version: '3.8'
services:
web:
# ...
networks:
- app_network
db:
image: postgres:13
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- db_data:/var/lib/postgresql/data
networks:
- app_network
volumes:
db_data:
networks:
app_network:
driver: bridge
Ahora, cuando ejecutes docker compose up -d, Compose leerá las variables del archivo .env y las inyectará en los servicios. Esto es una práctica recomendada para la gestión de secretos y configuraciones.
✨ Extendiendo tu docker-compose.yml (Otras directivas útiles)
Docker Compose ofrece muchas otras directivas para configurar tus servicios. Aquí hay algunas de las más comunes:
build: En lugar deimage, puedes especificar unbuildpara construir una imagen a partir de unDockerfileen un contexto específico.
services:
app:
build: ./app # Busca un Dockerfile en el directorio ./app
# O con un Dockerfile específico:
# build:
# context: ./app
# dockerfile: Dockerfile.dev
restart: Define la política de reinicio del contenedor (e.g.,no,always,on-failure,unless-stopped).
services:
api:
# ...
restart: always
healthcheck: Define cómo Docker debe verificar si un contenedor está "saludable" (útil paradepends_oncon espera).
services:
db:
# ...
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydatabase"]
interval: 10s
timeout: 5s
retries: 5
expose: Expone puertos que solo son accesibles desde otros servicios en la misma red, no desde el host.
services:
backend:
# ...
expose:
- "3000" # Puerto interno para comunicación entre servicios
links(legado): En versiones anteriores de Compose,linksse usaba para la comunicación entre servicios. Con las redes definidas, ya no es necesario y el uso de nombres de servicio es la forma recomendada.
⏩ Flujo de Trabajo Típico con Docker Compose
Describe tus servicios, redes y volúmenes.
docker compose up -d para construir (si es necesario) e iniciar todos los servicios en segundo plano.Realiza cambios en tu código. Docker Compose montará volúmenes para ver cambios en tiempo real, o puedes reconstruir servicios si cambian las imágenes.
Usa
docker compose logs para diagnosticar problemas.Cuando termines de trabajar, usa
docker compose down para detener y limpiar los contenedores.Si necesitas empezar de cero con los datos, usa
docker compose down -v.✅ Conclusión
Docker Compose es una herramienta increíblemente potente y versátil para gestionar aplicaciones multi-contenedor. Simplifica drásticamente el proceso de configuración y orquestación, haciendo que el desarrollo, las pruebas y el despliegue de aplicaciones complejas sean mucho más manejables.
Dominar Docker Compose es un paso fundamental para cualquier desarrollador o ingeniero de DevOps que trabaje con Docker. Te permite definir tu infraestructura como código, asegurando la coherencia en todos tus entornos y liberándote para concentrarte en escribir un gran código, en lugar de luchar con la configuración de la infraestructura.
Sigue experimentando con diferentes servicios, combinaciones y configuraciones en tu archivo docker-compose.yml. La práctica es clave para dominar esta herramienta.
Tutoriales relacionados
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!