Docker

Trabajos en contenedores programados, sin adoptar Kubernetes para lograrlo.

Si tu equipo usa Docker pero no Kubernetes, un trabajo en contenedor programado no tiene dónde vivir. Dagu es un único binario que controla el demonio de Docker que ya tienes y añade a los contenedores dependencias, reintentos, registros e interfaz web.

Sin Kubernetes ni plataforma de contenedores
Los pasos comparten un contenedor, o cada uno trae su imagen
Exec en contenedores que tu stack de compose ya ejecuta
Podman funciona con la misma API compatible con Docker
01

El hueco entre docker run y Kubernetes CronJob

Programar un contenedor es el punto donde se acaban las buenas opciones. Kubernetes CronJob es la respuesta madura, pero solo si ya tienes un clúster. Por debajo, lo habitual es cron llamando a docker run, que programa y nada más.

  • cron más docker run no da reintentos, ni dependencias entre trabajos, ni historial, ni forma de ver por qué falló anoche
  • Adoptar Kubernetes para programar un informe nocturno es mucha plataforma para poco trabajo
  • Un orquestador general que trata los contenedores como un tipo de paso, y no como el destino del despliegue, encaja justo en medio
02

Un contenedor para todo el flujo

Declarar un contenedor a nivel de flujo hace que todos los pasos se ejecuten dentro del mismo contenedor de larga vida. Comparten el sistema de archivos, así que lo que instala un paso sigue ahí para el siguiente.

  • Instalar dependencias una vez y reutilizarlas entre pasos, sin reconstruir imágenes ni reinstalar por tarea
  • Montar rutas del host con volumes y fijar variables de entorno una sola vez para todo el flujo
  • Reintentos, dependencias y manejo de fallos siguen siendo campos normales del flujo, sin cambiar por ejecutarse en un contenedor

En este modo los pasos se ejecutan mediante docker exec, por lo que ENTRYPOINT y CMD de la imagen no se invocan para los comandos del paso. Pon el comando en el paso.

Un trabajo nocturno íntegramente en una imagen
# nightly-report.yaml
schedule: "0 3 * * *"
max_active_runs: 1

container:
  image: python:3.12
  volumes:
    - ./data:/data
  env:
    - TZ=Asia/Tokyo

steps:
  - id: install
    run: pip install -r /data/requirements.txt

  - id: build_report
    run: python /data/build_report.py
    depends: install
    retry_policy:
      limit: 2
      interval_sec: 120

  - id: publish
    run: python /data/publish.py
    depends: build_report

mail_on:
  failure: true
03

Mantenimiento dentro de los contenedores que ya tienes en marcha

El modo exec apunta un flujo a un contenedor que ya está en ejecución, por ejemplo iniciado por Docker Compose. El trabajo programado ocurre dentro del contenedor de aplicación real, no en una copia nueva.

  • Migraciones de base de datos, limpieza de caché y mantenimiento de colas se ejecutan contra el servicio realmente en marcha
  • Nombrar el contenedor como una cadena es toda la configuración
  • El flujo conserva dependencias y notificación de fallos, que un archivo compose no puede expresar
Mantenimiento programado sobre un servicio compose en marcha
# app-maintenance.yaml
schedule: "0 4 * * *"
max_active_runs: 1

# docker compose で起動済みのコンテナに exec する
container: myapp-web

steps:
  - id: migrate
    run: php artisan migrate --force

  - id: prune_sessions
    run: php artisan session:prune
    depends: migrate

  - id: clear_cache
    run: php artisan cache:clear
    depends: prune_sessions

mail_on:
  failure: true
04

Una imagen distinta por paso

Cuando una tubería cruza herramientas, cada paso puede traer su propia imagen. El flujo sigue siendo un archivo y un horario mientras los pasos permanecen independientes.

  • Extraer con una imagen de cliente de base de datos, transformar con una imagen de lenguaje y cargar con otro cliente, sin una imagen que contenga las tres
  • Un contenedor a nivel de paso prevalece sobre el del flujo, de modo que un flujo casi uniforme admite excepciones
  • La política de descarga de imagen se configura por paso, para imágenes fijadas y para las que se reconstruyen a menudo
Un flujo, tres imágenes
# etl-pipeline.yaml
schedule: "0 2 * * *"
max_active_runs: 1

steps:
  - id: extract
    container:
      image: mysql:8
      volumes:
        - ./work:/work
    run: mysqldump --host db.internal -u svc app > /work/dump.sql

  - id: transform
    container:
      image: python:3.12
      volumes:
        - ./work:/work
    run: python /work/transform.py
    depends: extract

  - id: load
    container:
      image: postgres:16
      volumes:
        - ./work:/work
    run: psql -h dw.internal -f /work/out.sql
    depends: transform

handler_on:
  failure:
    run: ./scripts/notify-failure.sh

mail_on:
  failure: true
05

Qué necesita del host

Los pasos en contenedor hablan con una API compatible con Docker. Es el único requisito y también la restricción que conviene conocer antes de planificar un despliegue.

  • Funciona tanto un socket de Docker local como un demonio remoto mediante DOCKER_HOST
  • Podman se admite a través de su API compatible con Docker fijando DAGU_CONTAINER_RUNTIME=podman
  • Como Dagu es un único binario, el planificador en sí no necesita clúster, base de datos de metadatos ni broker

Las instancias gestionadas de Dagu Cloud se ejecutan con aislamiento gVisor y no exponen un socket de demonio de contenedores, por lo que allí no hay pasos en contenedor. Usa Dagu autoalojado o dirige el flujo a un worker autoalojado.

06

Dónde Kubernetes sigue siendo la respuesta correcta

Esta página defiende una herramienta más pequeña en una situación concreta, no evitar Kubernetes en general.

  • Si ya ejecutas un clúster, CronJob es un sitio razonable para contenedores programados y no requiere nada nuevo
  • Si los trabajos necesitan planificación a nivel de pod, autoescalado o empaquetado entre nodos, eso es tarea de un clúster y no de un orquestador
  • Para flujos que envían trabajo a un clúster manteniendo la orquestación fuera, Dagu tiene un paso de Kubernetes

FAQ

Practical questions before adopting

¿Necesito Kubernetes para ejecutar trabajos en contenedores programados?

No. Dagu habla directamente con un demonio compatible con Docker, así que basta un host con Docker instalado. Kubernetes merece la pena cuando hace falta planificación y escalado a nivel de clúster, no solo para lanzar un contenedor a las 3 de la madrugada.

¿En qué se diferencia del operador Docker de Airflow?

Sobre todo en el coste de operación. Airflow necesita un planificador, una base de datos de metadatos y un framework de DAG en Python antes de ejecutar un contenedor. Dagu es un binario con estado en archivos, y los contenedores son un campo del flujo o del paso, no un operador de Python.

¿Los pasos pueden compartir archivos?

Sí. Con un contenedor a nivel de flujo, los pasos se ejecutan en el mismo contenedor y comparten su sistema de archivos directamente. Con imágenes por paso, monta una ruta del host compartida en cada paso.

¿Funciona Podman?

Sí, a través de su API compatible con Docker. Fija DAGU_CONTAINER_RUNTIME=podman en un despliegue autoalojado y los pasos en contenedor se comportan igual.

¿Puedo ejecutar un paso dentro de un contenedor iniciado por Docker Compose?

Sí, ese es el modo exec. Nombra el contenedor en marcha y los pasos del flujo se ejecutan dentro de él. Las migraciones programadas y el mantenimiento de caché contra un stack de compose vivo se conectan normalmente así.

Next step

Start with one workflow.

Install Dagu, move one script that runs on cron today into YAML, and decide from a real run history.