9. Docker Compose: orquestación básica

¿Qué es Docker Compose y por qué se usa?

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:

  • Configuración declarativa: el repositorio contiene la definición exacta de la infraestructura necesaria.
  • Un solo comando: levantar, detener o recrear todo el entorno se realiza con los comandos de Compose.
  • Redes automáticas: los servicios pueden comunicarse por nombre de host sin configuraciones adicionales.
  • Reproducibilidad: cada desarrollador o entorno de CI obtiene la misma configuración.

Instalación y comandos 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.

Ejemplo paso a paso: Flask + Redis con Docker Compose

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.

Estructura de directorios

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:

Cómo probar el proyecto

  1. Ubícate en la carpeta mi_proyecto y ejecuta docker compose up --build.
  2. Cuando aparezca visitas_web | * Running on all addresses, abre http://localhost:5000. Cada refresco incrementa el contador.
  3. Para detener todo, usa Ctrl+C si está en primer plano o ejecuta docker compose down en otra terminal.
  4. Si quieres borrar también el volumen de Redis, ejecuta docker compose down -v.
Diagrama de la aplicación Flask y Redis orquestada con Docker Compose

Claves del archivo Compose

  • services.web.build: construye la imagen del backend desde app/Dockerfile.
  • ports: expone Flask para acceder desde el navegador.
  • environment: define las variables que el backend usa para localizar Redis.
  • depends_on: garantiza que el contenedor web arranque después del servicio Redis.
  • redis.image: utiliza la imagen oficial ligera de Redis.
  • volumes: persiste los datos en un volumen llamado 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.

Buenas prácticas para avanzar

  • Mantén tus archivos docker-compose.yml bajo control de versiones.
  • Usa variables de entorno o archivos .env para credenciales y configuraciones sensibles.
  • Divide los servicios en archivos Compose separados (por ejemplo, docker-compose.override.yml) si necesitas diferencias entre desarrollo y producción.
  • Agrega healthchecks para que Compose sepa cuándo un servicio está listo antes de iniciar dependencias.