dagucloud/dagu · 上手攻略

  • 仓库:dagucloud/dagu
  • 链接:https://github.com/dagucloud/dagu
  • 分类:database / app(自托管工作流编排引擎)
  • 作者:spark
  • 更新:2026-10-22

一句话结论

如果你有一堆能跑的脚本和 cron 任务,想要依赖关系、重试、历史和 Web UI,但不想为此运维 Airflow 那六个服务——Dagu 是当下最直接的答案;团队要 SSO/RBAC/审计时先确认授权边界,见坑 6。

它是什么

Dagu 是一个 local-first 的工作流引擎,用 Go 写成,卖点是「单个二进制 + 内置 Web UI + 不需要外部数据库或消息队列」,跑在 Linux、macOS、Windows 上。工作流用声明式 YAML 定义 DAG,原生支持 shell 命令、Docker 容器、Kubernetes Job、SSH 远程命令,还可以通过 Dagu Actions 扩展到 SQL、HTTP 等任意工具。

它的设计哲学写在 README 里很直白:编排不是你的主要工作。传统编排器要你运维 Web Server、Scheduler、Worker、PostgreSQL、Redis/RabbitMQ、Python Runtime 六个组件,并且你的任务会被重写成 @dag/@task 框架代码;Temporal 则把业务逻辑塞进它的 SDK。Dagu 的立场是「工作流结构是配置,不是代码」——顺序、依赖、重试、调度、人工任务都在脚本旁边的一个 YAML 文件里,而执行引擎是一个进程。

关键约束值得单独说:你的脚本永远不会 import 编排器。删掉 YAML,脚本照常运行;留着 YAML,每次运行都能得到依赖图、重试、逐步日志、历史和 Web UI。

⚠️ 版本状态:README 提到 Dagu Console 监控「free for one server on Dagu v2.19.2 or later」,openalternative 抓取显示 last commit 为 v2.19.0。许可 GPL-3.0(仓库内另有 LICENSING.md,涉及嵌入式 API 与商业嵌入说明——⚠️ 具体条款本文未展开,见坑 6)。约 4.4k stars。

解决什么问题

三个替代方案各自的代价,README 概括得相当准:

  • cron 能跑命令,但没依赖、没重试、没历史。
  • Airflow 能编排,但你要养一个平台,而且任务被改写成框架代码。
  • Temporal 提供持久化执行,但业务逻辑要搬进它的 SDK 和编程模型。

而这些工具的真实结果是:「你本来只想给几个任务定时,结果现在要运维第二套系统,而且编排器住进了本该被它服务的代码里。」

快速安装

六种方式,按场景挑:

# macOS / Linux:引导式安装向导(可加 PATH、注册后台服务、建管理员账号)
curl -fsSL https://raw.githubusercontent.com/dagucloud/dagu/main/scripts/installer.sh | bash

# Homebrew / npm(无向导)
brew install dagu
npm install -g --ignore-scripts=false @dagucloud/dagu

# Docker
docker run --rm -v ~/.dagu:/var/lib/dagu -p 8080:8080 ghcr.io/dagucloud/dagu:latest dagu start-all

# Kubernetes(Helm,注意 StorageClass 必须支持 ReadWriteMany)
helm repo add dagu https://dagucloud.github.io/dagu
helm repo update
helm install dagu dagu/dagu --set persistence.storageClass=<your-rwx-storage-class>

Windows PowerShell:irm https://raw.githubusercontent.com/dagucloud/dagu/main/scripts/installer.ps1 | iex。

只跑脚本安装器(shell / PowerShell)才有引导向导;Homebrew、npm、Docker、Helm 没有。

核心用法

最小工作流——在空目录建 hello.yaml:

steps:
  - id: hello
    run: echo "hello from Dagu"
dagu start hello.yaml        # 跑一次
dagu start-all --dags .      # 起服务 + 调度器 + 执行器
# 打开 http://localhost:8080

依赖与并行:

steps:
  - id: extract
    run: ./extract.sh
  - id: transform_a
    run: ./transform_a.sh
    depends: extract
  - id: transform_b
    run: ./transform_b.sh
    depends: extract
  - id: load
    run: ./load.sh
    depends: [transform_a, transform_b]

重试与容错:

steps:
  - name: flaky-api-call
    run: curl -f https://api.example.com/data
    retry_policy:
      limit: 3
      interval_sec: 10
    continue_on:
      failure: true      # 失败也继续往下走

调度(cron 语法 + 重叠策略 + 补偿窗口):

schedule:
  - "0 */6 * * *"      # 每 6 小时
overlap_policy: skip     # 上一次还在跑就跳过
catchup_window: "5h"    # 调度器宕机 5 小时内可补跑
timeout_sec: 3600
handler_on:
  failure:
    run: notify-team.sh
  exit:
    run: cleanup.sh

带类型参数的表单(params 会在 Web UI 渲染成类型化输入表单):

params:
  - name: customer_id
    type: string
    description: Customer or account identifier
  - name: change_scope
    type: string
    enum: [metadata_only, permissions, full_account]
    default: metadata_only
  - name: dry_run
    type: boolean
    default: true
steps:
  - id: extract
    run: >-
      ./scripts/extract.sh
      --customer "${params.customer_id}"
      --scope "${params.change_scope}"
      --dry-run="${params.dry_run}"

SSH 步骤与 Sub-DAG 并行:

# 父 DAG:对多个目标并行调同一个子 DAG,限并发
steps:
  - id: patch
    action: dag.run
    with:
      dag: patch-host
      params:
        host: ${ITEM}
      parallel:
        items: [web-1.internal, web-2.internal, db-1.internal]
        max_concurrent: 2
---
# 子 DAG
name: patch-host
params:
  - name: host
    type: string
ssh:
  user: deploy
  host: ${params.host}
steps:
  - id: apply
    run: apt-get update -q && apt-get upgrade -y

Docker 步骤(直接透传 docker run 选项):

resources:
  limits:
    cpu: 500m
    memory: 512Mi
steps:
  - id: report
    action: docker.run
    with:
      image: ghcr.io/acme/reporting:1.4.2
      pull: always
      platform: linux/amd64
      working_dir: /work
      volumes: ["~/orders:/work/data"]
      auto_remove: true
      command: python generate_report.py --input /work/data/orders.csv

Build workflow(缓存复用)——命令和文件都没变就复用上一步结果,Dagu 还能从匹配的路径推断依赖:

type: build
working_dir: .
steps:
  - id: uppercase
    inputs:
      - name: source
        path: source.txt
    outputs:
      - name: result
        path: uppercase.txt
    run: |
      #!/bin/sh
      tr '[:lower:]' '[:upper:]' < "${inputs.source}" > "${outputs.result}"

再跑一次会复用 uppercase.txt;改了 source.txt 才重跑。

部署拓扑三选一:单服务(dagu start-all,开发/单机/边缘)、临时 worker(Cloud Run Jobs / K8s Job / CI 每次起一个 dagu start,跑完销毁,状态写共享卷)、分布式 worker(常驻 worker 通过 gRPC 轮询协调器,按 label 路由)。CI 或纯 CLI 环境用 DAGU_HEADLESS=true 关掉 Web UI。

典型适用场景

README 列的场景我挑几个最有代表性的:

  • ETL 与数据运维:把已有的抽取脚本、SQL、dbt 命令变成可观测的流水线。
  • 遗留脚本与定时任务:把互相依赖的 cron 脚本变成有 UI、有日志、有重试、有通知的 DAG。
  • 媒体转码:ffmpeg 批处理,文件态状态让 worker 并行跑而不需要外部数据库。
  • 基础设施自动化:通过 SSH 在远端服务器跑命令,日志和结果集中一处。
  • GitHub 驱动的自动化:用 GitHub 事件触发工作流,在私有基础设施上跑自动化,不用把服务器暴露到公网。
  • 客服自助运维:给非工程团队提供自助工作流做诊断、查库、常规操作。
  • IoT / 边缘:传感器轮询、本地推理、备份、离线同步,仍有 Web UI 可见性。

坑与注意

  1. Docker socket 是权限逃逸面。现象:想让 Dagu 跑 container: 或 action: docker.run 步骤,于是挂载宿主 Docker socket。影响:挂载 socket 等于把宿主 daemon 的控制权交给工作流,等同于把宿主 root 权限开放。修复:README 的 Docker 安装命令故意不暴露 host daemon;若确实需要容器步骤,按官方 container-step Docker setup 单独配置并评估风险,不要图省事直接 -v /var/run/docker.sock。
  2. Helm 必须用 ReadWriteMany 的 StorageClass。现象:helm install 后 Pod 起不来或状态不持久。影响:工作流状态丢失。修复:--set persistence.storageClass=<rwx-storage-class>,先确认集群有支持 RWX 的 StorageClass。
  3. Docker 部署需要额外步骤才能用容器步骤。现象:官方 docker run 命令起来了,但容器步骤报错。影响:以为 Dagu 坏了。修复:README 明确说该命令不暴露 host daemon,容器步骤需按专门文档配置。
  4. 升级缺少明确迁移说明。现象:直接换二进制就升级。影响:状态存在本地文件里,格式若变更可能读不了历史。修复:README 未描述跨版本正式迁移路径,升级前务必读 release notes;重要环境先备份数据目录。
  5. 文件态存储的容量边界。现象:以为能无限扩展。影响:磁盘成为瓶颈。修复:README 称单机每天可跑数千次 workflow run,但明确「实际容量取决于 CPU、内存、磁盘和工作流形态」。用队列、并发上限和资源限制控制负载;大规模走 worker 横向扩展。⚠️ 官方未给出量化上限,务必按自己的工作流压测。
  6. 许可边界要看清。现象:以为所有功能都免费。影响:合规风险。修复:GPL-3.0(另有 LICENSING.md 说明嵌入式 API 与商业嵌入);功能上分两档——Community self-host 不要 license key,SSO、RBAC、审计日志、incident SaaS 集成属于 self-host license(付费)。⚠️ 具体条款本文未逐条核实,商用前请自行读 LICENSE 与 LICENSING.md。
  7. 开启监控即出网。现象:打开了 Dagu Console 监控。影响:元数据离开你的环境。修复:README 说得很明确——opt-in(需管理员在 Plan & features 开启或设 cloud.report: runs),仅出站(HTTPS 每 60 秒上报,免费档每 5 分钟),仅元数据(运行状态、耗时、失败的 step 名;日志、输出、参数和 secrets 永不外发,且服务器会显示最后一次上报的确切 JSON)。监控处于 beta;单台服务器免费需 Dagu v2.19.2 及以上。
  8. continue_on: failure: true 会吞掉失败。现象:某步失败但流程「成功」了。影响:以为全绿其实漏了数据。修复:只在明确可容忍失败的步骤上开,并配 handler_on: failure 通知。
  9. catchup 窗口会造成补偿风暴。现象:调度器宕机数小时后恢复。影响:catchup_window: "5h" 内的错过的运行被批量补跑,瞬间打满资源。修复:窗口按业务容忍度设,并配合并发上限。
  10. 脚本安装器才有引导向导。现象:想当然以为 brew/npm 装完能直接开 UI 看到管理员账号流程。影响:困惑。修复:Homebrew、npm、Docker、Helm 均无向导,需自行首次启动时创建管理员账号。brew install dagu 装的版本可能落后于上游 release,注意版本对齐。

与同类对比

  • vs cron:Dagu 严格是超集。cron 能做的 Dagu 都能做,且多了依赖、重试、历史和 UI。差别只是多引入一个二进制——如果你只有三五个无关紧要的任务,cron 仍然更省。
  • vs Airflow:README 的对比逻辑成立——Airflow 要你养 6+ 个服务,且任务被改写成 @dag/@task 代码。Dagu 是 1 个进程、配置与逻辑分离。代价是你失去了 Airflow 的成熟生态、丰富的 operator 库和大规模调度经验。Dagu 明确把自己定位为 Airflow / Cron / Job Scheduler 的替代品,而非同量级平台。
  • vs Temporal:Temporal 的优势是持久化执行的强语义保证,代价是业务逻辑要进 SDK。Dagu 是「薄编排」——执行仍由你的 shell/容器负责,编排器不接管重试语义到编程模型层面。
  • vs Mage-ai(第三方对比 ⚠️ 非官方数据):两者都属编排类,仓库年龄都约 4 年;差异是 Mage 侧重 ETL 与数据集成、许可是 Apache-2.0,Dagu 侧重通用自动化编排、许可是 GPL-3.0。GPL 对闭源商业集成的约束明显更紧,这点要先想清楚。
  • vs n8n / Node-RED:那两个是可视化节点编辑器,面向集成与业务连接;Dagu 是 YAML 声明式 + 运维取向,面向已有脚本与批处理。Dagu 有 Web UI 但主打观测与运维,不是拖拽编排画布。

不确定处

  • 版本:README 引用「v2.19.2 or later」用于 Console 免费档,openalternative 抓取为 v2.19.0,mise 版本列表最新可见 2.16.5(1 个月前)。⚠️ 核查于 2026-10-22,各数据源不一致,具体最新版请以 GitHub Releases 页面为准。
  • GPL-3.0 已确认(仓库卡、第三方多源一致);但 LICENSING.md 中关于嵌入式 API 与商业嵌入的条款本文未读取,商业使用前需自行核实。
  • self-host license 的定价与具体功能边界未核实(见 dagu.sh/pricing)。
  • 「单机每天数千次 run」为 README 自述,未找到压测数据支撑。
  • Dagu Console 处于 beta,其稳定性与上报行为的长期一致性未核实。
  • 本文未 clone、未运行任何命令;所有 YAML 与命令均直接摘自 README。