Dagu Docker 설치 가이드: 경량 워크플로우 엔진 – Cron과 Airflow의 대안




Dagu는 단일 바이너리로 실행되는 경량 워크플로우 엔진으로, 복잡한 인프라 없이 YAML로 DAG를 정의하여 워크플로우를 관리할 수 있습니다. PostgreSQL, Redis, Python 런타임 없이 실행되며, 128MB 미만의 메모리로 동작합니다. Cron의 현대적 대안이자 Airflow보다 훨씬 가벼운 솔루션으로, 레거시 스크립트를 수정 없이 오케스트레이션할 수 있습니다.


Dagu란?

Dagu는 “Just another Cron alternative with a Web UI”를 모토로 하는 오픈소스 워크플로우 엔진입니다. 기존 Cron 작업의 복잡한 의존성을 시각화하고, 웹 UI로 모니터링하며, 실패 시 재실행을 간편하게 처리할 수 있습니다.

Airflow vs Dagu

Traditional Orchestrator          Dagu
┌────────────────────────┐       ┌──────────────────┐
│ Web Server             │       │                  │
│ Scheduler              │       │ dagu start-all   │
│ Worker(s)              │       │                  │
│ PostgreSQL             │       └──────────────────┘
│ Redis / RabbitMQ       │        Single binary.
│ Python runtime         │        Zero dependencies.
└────────────────────────┘        Just run it.
   6+ services to manage

왜 Dagu인가?

특징DaguAirflow
설치단일 바이너리6+ 서비스
의존성없음PostgreSQL, Redis, Python
메모리< 128MB4GB+ 권장
워크플로우 정의YAMLPython
학습 곡선매우 낮음높음
Air-gapped✅ 지원
레거시 스크립트수정 없이 실행래핑 필요

주요 기능

🚀 Zero-Ops

  • 단일 바이너리 실행
  • 파일 기반 스토리지 (DB 불필요)
  • 128MB 미만 메모리 사용

📝 YAML 기반 워크플로우

steps:
  - name: extract
    command: python extract.py
  - name: transform
    command: python transform.py
    depends:
      - extract
  - name: load
    command: python load.py
    depends:
      - transform

🔌 19+ Executor 지원

  • Shell 명령어
  • Docker 컨테이너
  • SSH 원격 실행
  • HTTP 요청
  • 메일 발송

🤖 AI-Native

  • 내장 LLM 에이전트
  • 자연어로 워크플로우 생성/수정
  • 자동 디버깅

🏢 프로덕션 기능

  • 재시도 및 에러 핸들링
  • 웹훅 알림
  • RBAC (역할 기반 접근 제어)
  • Git 동기화
  • 분산 실행 모드

사전 요구사항

  • Docker 및 Docker Compose
  • 최소 128MB RAM
  • 웹 UI 접근을 위한 포트 (기본: 8080)

Docker Compose 설치 방법

방법 1: 최소 설치 (All-in-One)

가장 간단한 단일 컨테이너 설정입니다:

version: '3.8'

services:
  dagu:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DAGU_HOST=0.0.0.0
      - DAGU_PORT=8080
      - DAGU_TZ=Asia/Seoul
    volumes:
      - dagu_data:/var/lib/dagu
      - ./dags:/var/lib/dagu/dags
    command: ["dagu", "start-all"]

volumes:
  dagu_data:

실행합니다:

mkdir -p dags
docker compose up -d

http://localhost:8080으로 접속합니다.

방법 2: 기본 인증 포함

version: '3.8'

services:
  dagu:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DAGU_HOST=0.0.0.0
      - DAGU_PORT=8080
      - DAGU_TZ=Asia/Seoul
      # 기본 인증
      - DAGU_AUTH_BASIC_USERNAME=admin
      - DAGU_AUTH_BASIC_PASSWORD=your_secure_password
    volumes:
      - dagu_data:/var/lib/dagu
      - ./dags:/var/lib/dagu/dags
    command: ["dagu", "start-all"]

volumes:
  dagu_data:

방법 3: 서버 + 스케줄러 분리

스케줄러와 웹 서버를 분리하여 운영합니다:

version: '3.8'

services:
  # 권한 초기화
  init:
    image: ghcr.io/dagu-org/dagu:latest
    user: root
    volumes:
      - dagu_config:/home/dagu/.config/dagu
      - dagu_data:/home/dagu/.local/share
    command: chown -R dagu /home/dagu/.config/dagu/ /home/dagu/.local/share

  # 웹 UI 서버
  server:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu-server
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DAGU_PORT=8080
      - DAGU_HOST=0.0.0.0
      - DAGU_DAGS=/home/dagu/.dagu/dags
    volumes:
      - dagu_config:/home/dagu/.config/dagu
      - dagu_data:/home/dagu/.local/share
      - ./dags:/home/dagu/.dagu/dags
    depends_on:
      - init

  # 스케줄러
  scheduler:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu-scheduler
    restart: unless-stopped
    environment:
      - DAGU_DAGS=/home/dagu/.dagu/dags
    volumes:
      - dagu_config:/home/dagu/.config/dagu
      - dagu_data:/home/dagu/.local/share
      - ./dags:/home/dagu/.dagu/dags
    command: dagu scheduler
    depends_on:
      - init

volumes:
  dagu_config:
  dagu_data:

방법 4: Docker-in-Docker 지원

Docker Executor를 사용하여 컨테이너 내에서 Docker를 실행합니다:

version: '3.8'

services:
  dagu:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DAGU_HOST=0.0.0.0
      - DAGU_PORT=8080
      - DAGU_TZ=Asia/Seoul
      - DAGU_DEBUG=false
    volumes:
      - dagu_data:/var/lib/dagu
      - ./dags:/var/lib/dagu/dags:ro
      # Docker 소켓 마운트 (Docker-in-Docker)
      - /var/run/docker.sock:/var/run/docker.sock
    # Docker 소켓 접근을 위해 root로 실행
    user: "0:0"
    command: ["dagu", "start-all"]

volumes:
  dagu_data:

방법 5: 프로덕션 설정 (헬스체크 + 로깅)

version: '3.8'

services:
  dagu:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DAGU_HOST=0.0.0.0
      - DAGU_PORT=8080
      - DAGU_TZ=Asia/Seoul
      - DAGU_LOG_FORMAT=json
      - DAGU_AUTH_BASIC_USERNAME=${DAGU_USERNAME:-admin}
      - DAGU_AUTH_BASIC_PASSWORD=${DAGU_PASSWORD:-admin}
    volumes:
      - dagu_data:/var/lib/dagu
      - ./dags:/var/lib/dagu/dags
      - /var/run/docker.sock:/var/run/docker.sock
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/api/v2/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"
    user: "0:0"
    command: ["dagu", "start-all"]

volumes:
  dagu_data:

.env 파일:

DAGU_USERNAME=admin
DAGU_PASSWORD=your_secure_password

Traefik 리버스 프록시 연동

version: '3.8'

services:
  dagu:
    image: ghcr.io/dagu-org/dagu:latest
    container_name: dagu
    restart: unless-stopped
    environment:
      - DAGU_HOST=0.0.0.0
      - DAGU_PORT=8080
      - DAGU_TZ=Asia/Seoul
      - DAGU_AUTH_BASIC_USERNAME=admin
      - DAGU_AUTH_BASIC_PASSWORD=your_secure_password
    volumes:
      - dagu_data:/var/lib/dagu
      - ./dags:/var/lib/dagu/dags
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dagu.rule=Host(`dagu.yourdomain.com`)"
      - "traefik.http.routers.dagu.entrypoints=websecure"
      - "traefik.http.routers.dagu.tls=true"
      - "traefik.http.routers.dagu.tls.certresolver=letsencrypt"
      - "traefik.http.services.dagu.loadbalancer.server.port=8080"
    user: "0:0"
    command: ["dagu", "start-all"]
    networks:
      - dagu
      - traefik

volumes:
  dagu_data:

networks:
  dagu:
    driver: bridge
  traefik:
    external: true

샘플 DAG 작성

기본 워크플로우

dags/hello.yaml:

# 간단한 Hello World DAG
steps:
  - name: step1
    command: echo "Hello from Dagu!"
  - name: step2
    command: echo "Running step 2"
    depends:
      - step1

ETL 파이프라인

dags/etl_pipeline.yaml:

# ETL 데이터 파이프라인
schedule: "0 0 * * *"  # 매일 00:00 실행

env:
  - DATA_DIR: /data
  - LOG_DIR: /log

# 에러 및 종료 핸들러
handlerOn:
  failure:
    command: "echo 'Pipeline failed!' | mail -s 'ETL Error' admin@example.com"
  exit:
    command: "echo 'Cleanup completed'"

steps:
  # Step 1: 데이터 추출
  - name: extract_data
    command: bash
    script: |
      echo "Extracting data..."
      echo $(date '+%Y-%m-%d')
    output: DATE

  # Step 2: 데이터 정제
  - name: cleanse_data
    command: echo "Cleansing data for ${DATE}"
    depends:
      - extract_data

  # Step 3: 데이터 변환
  - name: transform_data
    command: echo "Transforming data..."
    depends:
      - cleanse_data

  # Step 4: 데이터 로드
  - name: load_data
    command: echo "Loading data to warehouse..."
    depends:
      - transform_data

  # Step 5: 리포트 생성
  - name: generate_report
    command: echo "Generating report..."
    depends:
      - load_data

Docker Executor 사용

dags/docker_workflow.yaml:

# Docker 컨테이너에서 실행
schedule: "0 */6 * * *"  # 6시간마다

steps:
  - name: python_task
    executor:
      type: docker
      config:
        image: python:3.11-slim
        autoRemove: true
    command: python -c "print('Hello from Python container!')"

  - name: data_processing
    executor:
      type: docker
      config:
        image: python:3.11
        volumes:
          - ./data:/data
    command: python /data/process.py
    depends:
      - python_task

SSH 원격 실행

dags/ssh_workflow.yaml:

# SSH를 통한 원격 서버 작업
steps:
  - name: remote_backup
    executor:
      type: ssh
      config:
        user: deploy
        host: server.example.com
        key: /home/dagu/.ssh/id_rsa
    command: /scripts/backup.sh

  - name: notify
    command: echo "Backup completed"
    depends:
      - remote_backup

HTTP 요청

dags/http_workflow.yaml:

# HTTP API 호출
steps:
  - name: api_call
    executor:
      type: http
      config:
        timeout: 30
    command: GET https://api.example.com/data
    
  - name: webhook_notify
    executor:
      type: http
    command: POST https://hooks.slack.com/services/xxx
    script: |
      {
        "text": "Workflow completed successfully!"
      }
    depends:
      - api_call

서브 워크플로우 (Composable)

dags/main_workflow.yaml:

# 서브 워크플로우 호출
steps:
  - name: setup
    command: echo "Setting up..."

  - name: run_etl
    run: etl_pipeline  # 다른 DAG 호출
    params: "DATE=2024-01-01"
    depends:
      - setup

  - name: cleanup
    command: echo "Cleaning up..."
    depends:
      - run_etl

환경 변수 설정

주요 환경 변수

변수명설명기본값
DAGU_HOST바인딩 호스트127.0.0.1
DAGU_PORT웹 UI 포트8080
DAGU_DAGSDAG 디렉토리$DAGU_HOME/dags
DAGU_TZ타임존UTC
DAGU_LOG_FORMAT로그 포맷 (text/json)text
DAGU_DEBUG디버그 모드false

인증 설정

# 기본 인증
DAGU_AUTH_BASIC_USERNAME=admin
DAGU_AUTH_BASIC_PASSWORD=your_password

# 또는 내장 RBAC (builtin 모드)
DAGU_AUTH_MODE=builtin

경로 설정

DAGU_LOG_DIR=/var/lib/dagu/logs
DAGU_DATA_DIR=/var/lib/dagu/data
DAGU_BASE_CONFIG=/var/lib/dagu/config.yaml

웹 UI 사용법

대시보드

  • DAG 목록 및 실행 상태 확인
  • 실시간 실행 모니터링
  • 스케줄 관리

DAG 관리

  • 웹 UI에서 직접 DAG 생성/편집
  • 실행 이력 및 로그 확인
  • 수동 실행 및 중지

CLI 명령어

# DAG 실행
docker exec dagu dagu run my_workflow.yaml

# DAG 목록
docker exec dagu dagu list

# 상태 확인
docker exec dagu dagu status my_workflow

# 스케줄러 시작
docker exec dagu dagu scheduler

# 모든 서비스 시작
docker exec dagu dagu start-all

업그레이드 및 백업

버전 업그레이드

# 최신 이미지 풀
docker compose pull

# 컨테이너 재시작
docker compose down
docker compose up -d

데이터 백업

# 볼륨 백업
docker run --rm \
  -v dagu_data:/data \
  -v $(pwd)/backup:/backup \
  alpine tar czf /backup/dagu_backup_$(date +%Y%m%d).tar.gz /data

# DAG 파일 백업
tar czf dags_backup_$(date +%Y%m%d).tar.gz ./dags

문제 해결

웹 UI 접속 불가

# 로그 확인
docker logs dagu

# 포트 확인
docker compose ps

DAG 인식 안 됨

# DAG 디렉토리 확인
docker exec dagu ls -la /var/lib/dagu/dags

# 권한 확인
docker exec dagu cat /var/lib/dagu/dags/my_dag.yaml

Docker Executor 오류

# Docker 소켓 마운트 확인
docker exec dagu ls -la /var/run/docker.sock

# Docker 명령 테스트
docker exec dagu docker ps

스케줄 실행 안 됨

  • schedule 필드가 올바른 Cron 표현식인지 확인
  • 스케줄러 프로세스가 실행 중인지 확인
  • 타임존 설정 확인 (DAGU_TZ)

사용 사례

1. Cron 작업 대체

  • 복잡한 의존성을 가진 배치 작업
  • 웹 UI로 모니터링 및 재실행
  • 실패 알림 및 자동 재시도

2. 데이터 파이프라인

  • ETL/ELT 워크플로우
  • 데이터 백업 자동화
  • 리포트 생성

3. 인프라 자동화

  • 서버 모니터링
  • 배포 파이프라인
  • 정기 유지보수 작업

4. 레거시 스크립트 관리

  • 기존 Shell/Perl 스크립트 오케스트레이션
  • 코드 수정 없이 의존성 관리
  • 실행 이력 및 로그 중앙화

도구 비교

기능DaguAirflowCron
설치 복잡도매우 낮음높음없음
의존성없음PostgreSQL, Redis없음
웹 UI✅ 풍부함
DAG 시각화
워크플로우 정의YAMLPythonCrontab
Docker 지원✅ 네이티브✅ Operator수동
SSH 실행✅ 네이티브✅ Operator수동
분산 실행✅ (Worker)✅ (Celery/K8s)
Air-gapped
메모리 사용< 128MB4GB+거의 없음

결론

Dagu는 다음과 같은 경우에 완벽한 선택입니다:

  • Cron의 한계: 의존성 관리, 모니터링, 재실행이 필요한 경우
  • Airflow가 과한 경우: 소규모 팀, 간단한 워크플로우, 리소스 제한
  • 레거시 환경: 기존 스크립트를 수정 없이 관리하고 싶은 경우
  • Air-gapped 환경: 인터넷 없이 격리된 환경에서 실행
  • 빠른 시작: 복잡한 설정 없이 즉시 사용

단일 바이너리, 제로 의존성, YAML 기반 워크플로우로 “다운로드하고 실행”만 하면 됩니다. 복잡한 인프라 없이 강력한 워크플로우 오케스트레이션이 필요하다면 Dagu를 추천합니다!


참고 링크




댓글 남기기