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인가?
| 특징 | Dagu | Airflow |
|---|---|---|
| 설치 | 단일 바이너리 | 6+ 서비스 |
| 의존성 | 없음 | PostgreSQL, Redis, Python |
| 메모리 | < 128MB | 4GB+ 권장 |
| 워크플로우 정의 | YAML | Python |
| 학습 곡선 | 매우 낮음 | 높음 |
| 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_DAGS | DAG 디렉토리 | $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 스크립트 오케스트레이션
- 코드 수정 없이 의존성 관리
- 실행 이력 및 로그 중앙화
도구 비교
| 기능 | Dagu | Airflow | Cron |
|---|---|---|---|
| 설치 복잡도 | 매우 낮음 | 높음 | 없음 |
| 의존성 | 없음 | PostgreSQL, Redis | 없음 |
| 웹 UI | ✅ | ✅ 풍부함 | ❌ |
| DAG 시각화 | ✅ | ✅ | ❌ |
| 워크플로우 정의 | YAML | Python | Crontab |
| Docker 지원 | ✅ 네이티브 | ✅ Operator | 수동 |
| SSH 실행 | ✅ 네이티브 | ✅ Operator | 수동 |
| 분산 실행 | ✅ (Worker) | ✅ (Celery/K8s) | ❌ |
| Air-gapped | ✅ | ❌ | ✅ |
| 메모리 사용 | < 128MB | 4GB+ | 거의 없음 |
결론
Dagu는 다음과 같은 경우에 완벽한 선택입니다:
- Cron의 한계: 의존성 관리, 모니터링, 재실행이 필요한 경우
- Airflow가 과한 경우: 소규모 팀, 간단한 워크플로우, 리소스 제한
- 레거시 환경: 기존 스크립트를 수정 없이 관리하고 싶은 경우
- Air-gapped 환경: 인터넷 없이 격리된 환경에서 실행
- 빠른 시작: 복잡한 설정 없이 즉시 사용
단일 바이너리, 제로 의존성, YAML 기반 워크플로우로 “다운로드하고 실행”만 하면 됩니다. 복잡한 인프라 없이 강력한 워크플로우 오케스트레이션이 필요하다면 Dagu를 추천합니다!
참고 링크
- Dagu 공식 문서
- GitHub 저장소
- 라이브 데모 (demouser / demouser)
- 예제 모음
- Docker Hub