Cuando una aplicación necesita varios contenedores (por ejemplo, un backend, una base de datos y un caché), ejecutarlos uno a uno con docker run
se vuelve pesado. Hay que recordar puertos, redes, volúmenes y variables de entorno para cada servicio.
Docker Compose soluciona este problema permitiendo declarar toda la aplicación en un archivo docker-compose.yml
. El archivo describe los servicios, sus dependencias, redes y volúmenes. Después solo hay que ejecutar docker compose up
para tener el entorno listo.
Ventajas principales:
Compose viene integrado con Docker Desktop (Windows y macOS). En Linux se instala como plugin al instalar Docker Engine reciente.
Comandos básicos:
docker compose up [--build] [-d]
: crea la red, construye las imágenes necesarias y levanta todos los servicios (con -d
quedan en segundo plano).docker compose down
: detiene los contenedores y elimina la red y los volúmenes nombrados asociados al proyecto.docker compose logs [-f] [servicio]
: muestra los registros de todos los servicios o de uno en particular.docker compose ps
: lista el estado de cada contenedor administrado por Compose.docker compose exec servicio comando
: ejecuta un comando dentro de un contenedor ya iniciado.Vamos a crear una aplicación web que cuenta visitas. El backend en Flask incrementa un contador almacenado en Redis. Todo se define con Compose y funciona igual en cualquier máquina con Docker.
mi_proyecto/
|- app/
| |- Dockerfile
| |- app.py
| `- requirements.txt
`- docker-compose.yml
app/app.py
from flask import Flask
from redis import Redis
from redis.exceptions import ConnectionError
import os
app = Flask(__name__)
redis_host = os.environ.get("REDIS_HOST", "redis")
redis_port = int(os.environ.get("REDIS_PORT", "6379"))
redis_client = Redis(host=redis_host, port=redis_port, decode_responses=True)
@app.route("/")
def index():
try:
hits = redis_client.incr("visitas")
return f"Hola Docker Compose. Contador: {hits}"
except ConnectionError:
return "No se puede conectar a Redis todavía.", 503
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
app/requirements.txt
flask==3.0.3
redis==5.0.4
app/Dockerfile
# 1. Seleccionar la imagen base
FROM python:3.12-slim
# 2. Definir el directorio de trabajo
WORKDIR /app
# 3. Copiar dependencias e instalarlas
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 4. Copiar el código de la aplicación
COPY app.py ./
# 5. Exponer el puerto usado por Flask
EXPOSE 5000
# 6. Indicar el comando de inicio por defecto
CMD ["python", "app.py"]
docker-compose.yml
services:
web:
build: ./app
container_name: visitas_web
ports:
- "5000:5000"
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
- redis
redis:
image: redis:7.2-alpine
container_name: visitas_redis
volumes:
- redis_data:/data
command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"]
volumes:
redis_data:
mi_proyecto
y ejecuta docker compose up --build
.visitas_web | * Running on all addresses
, abre http://localhost:5000. Cada refresco incrementa el contador.Ctrl+C
si está en primer plano o ejecuta docker compose down
en otra terminal.docker compose down -v
.app/Dockerfile
.redis_data
para que el contador no se pierda al reiniciar.Compose crea una red privada para el proyecto. Gracias a ello, el contenedor web puede conectarse a Redis usando redis
como nombre de host, sin exponer el servicio al exterior.
docker-compose.yml
bajo control de versiones..env
para credenciales y configuraciones sensibles.docker-compose.override.yml
) si necesitas diferencias entre desarrollo y producción.