Dagu 日常运维手册

Dagu 2.21.0 日常运维闭环实测:六步巡检(服务/端口/UI-API/执行历史/日志异常/磁盘水位)、失败告警最小实现(Webhook 对接钉钉企微机器人 + 邮件通知 + 外部拨测)、备份恢复、升级回滚 SOP 与六则故障排查(附实测原始报错)。

Dagu自动化监控2026/10/19

Dagu 日常运维手册

目标:为已上线的 Dagu 2.21.0 服务建立日常巡检闭环(服务状态/端口探活/UI 可用性/DAG 执行历史/日志异常/磁盘水位)、失败告警最小实现(Webhook 告警 + 邮件通知)、备份与恢复、升级回滚与故障排查手册。全文命令与回显均在 Ubuntu 22.04 + Dagu 2.21.0 生产形态环境逐条实测通过,所有报错均为实测原始输出。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64),内核 6.8.0-138-generic。
  • Dagu:2.21.0,systemd 服务 dagu.service 托管(ExecStart=/usr/local/bin/dagu start-all,Restart=always)。
  • 数据目录:/var/lib/dagu/(dags 工作流、logs 日志、data 状态);配置:/etc/dagu/dagu.env(DAGU_HOST=0.0.0.0、DAGU_PORT=8114)。
  • 服务端口:Admin UI 与 API 同端口 8114(默认 8080,本机 8080 已被占用故改 8114)。
  • 管理员账号:admin(内置用户体系,API 认证为 Bearer JWT)。
  • 资源基准(实测):二进制 /usr/local/bin/dagu 约 173MB;单进程常驻 RSS 约 130MB;空载时 logs/ 52KB、data/ 320KB。

二、日常巡检

日常巡检按「服务 → 端口 → UI/API → 执行历史 → 日志异常 → 磁盘水位」六步走,每步给出命令与实测回显,可整段写进巡检脚本。

2.1 服务状态

systemctl is-active dagu && systemctl is-enabled dagu

实测回显:

active
enabled

active 为运行中;failed/inactive 时先 systemctl status dagu -n 30 看最近日志。服务异常退出时 systemd 会在 10 秒后自动拉起(单元文件 Restart=always, RestartSec=10),频繁重启说明存在持续故障,用启动次数辅助判断:

systemctl show dagu -p NRestarts

实测回显:

NRestarts=0

2.2 端口探活

ss -ltn | grep 8114

实测回显:

LISTEN 0    4096   0.0.0.0:8114   0.0.0.0:*

要点:监听地址必须是 0.0.0.0(远程可访问);若显示 127.0.0.1:8114 则远程全部不通,检查 /etc/dagu/dagu.env 的 DAGU_HOST(详见第七章 Q2 的还原事故)。

2.3 UI 与 API 可用性

curl -s -o /dev/null -w "UI: %{http_code}\n" http://127.0.0.1:8114/

实测回显:

UI: 200

API 层探活(带认证,返回 DAG 数量即业务面健康):

TOKEN=$(curl -s -X POST -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}' \
  http://127.0.0.1:8114/api/v1/auth/login | python3 -c "import json,sys; print(json.load(sys.stdin)['token'])")
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8114/api/v1/dags | python3 -c "
import json,sys
d=json.load(sys.stdin)
print('dags:', len(d['dags']))
for x in d['dags']:
    lr=x['latestDAGRun']
    print(x['dag']['name'],'|',lr['statusLabel'])
"

实测回显(节选):

dags: 10
c1-fail-demo | failed
c1-retry-demo | succeeded
c1-schedule-demo | not_started
d2-check-demo | succeeded
d2-webhook-demo | failed
example-01-basic-sequential | not_started
...

巡检关注点:failed 状态的 DAG 需结合 2.4 的执行历史确认是「历史失败已处理」还是「新失败待处理」;not_started 且配置了 schedule 的 DAG 若长期未触发,检查调度器状态(2.5)。

2.4 DAG 执行历史检查

export DAGU_HOME=/var/lib/dagu
dagu history /var/lib/dagu/dags/d2-check-demo.yaml

实测回显:

DAG NAME       RUN ID                  STATUS     STARTED (UTC)        DURATION  PARAMS
d2-check-demo  034iRy7RoVhAwP9JvM919c  Succeeded  2026-10-19 05:14:07  < 1s      -

多看几条历史时加 --limit;单个 run 的细粒度状态用 dagu status /var/lib/dagu/dags/<名>.yaml --run-id <RUNID>。定时任务重点核对「最近一次计划触发时间」与历史里最新一条的差值——超过 1~2 个周期未跑即为异常。

2.5 服务日志异常检查

journalctl -u dagu --since "24 hours ago" --no-pager | grep -c "level=ERROR"
journalctl -u dagu --since "24 hours ago" --no-pager | grep "level=ERROR" | tail -5

实测回显(历史遗留错误的典型形态):

13
Oct 19 05:07:40 ubuntu dagu[2871]: time=2026-10-19T05:07:40.245Z level=ERROR msg="DAG load failed" err="[1:14] mapping value is not allowed in this context\n>  1 | description: \"C1 failure handler test\"..." file=/var/lib/dagu/dags/c1-fail-demo.yaml

判定口径:总数只作趋势参考(本例 13 条均为修复前的历史记录,时间戳在修复点之前即无需处理);重点关注时间戳持续前移的 ERROR。最常见的两类:DAG load failed(YAML 语法错,工作流作者修文件)与下载/网络类错误。调度器健康标志(启动日志中应存在):

msg="Updated scheduler status to active"
msg="Scheduler started"
msg="Started zombie detector" interval=45s

zombie detector(僵尸进程检测,45 秒一轮)与 retry scanner(30 秒一轮失败自动重试扫描)是两个常驻自愈组件,日志中消失说明调度面异常。

2.6 磁盘水位

df -h /var/lib/dagu | tail -1
du -sh /var/lib/dagu/logs /var/lib/dagu/data

实测回显:

overlay         786G  262G  490G  35% /
52K     /var/lib/dagu/logs
320K     /var/lib/dagu/data

水位口径:数据盘使用率超 80% 触发清理;日志增长主力是高频 DAG 的 per-run 目录(每次运行一个 dag-run_*/),保留期由 base.yaml hist_retention_days: 30(或 hist_retention_runs)自动控制,临时收紧可手动清理:

dagu rm --help   # 查看清理子命令用法
dagu cleanup --help   # 旧版清理命令(官方标记 deprecated,功能并入 rm --history)

三、监控告警

Dagu 的失败感知有三层:步骤/DAG 重试(自愈)、通知(邮件/Webhook,人感知)、外部拨测(UI 可用性,服务级感知)。本节落地后两层的最小实现。

3.1 Webhook 失败告警(衔接钉钉/企微机器人)

思路:利用 DAG 级 handler_on.failure 钩子,在失败时向钉钉/企微群机器人 Webhook 地址 POST 一条文本消息,格式与《钉钉机器人 webhook 告警接入指南》生态完全兼容。实测 DAG($DAGU_HOME/dags/d2-webhook-demo.yaml):

description: "D2 webhook alert test (DingTalk-style)"
handler_on:
  failure:
    run: |
      curl -s -X POST -H 'Content-Type: application/json' \
        -d '{"msgtype":"text","text":{"content":"[Dagu] d2-webhook-demo 执行失败,请排查"}}' \
        http://127.0.0.1:1026/dingtalk
steps:
  - id: broken_job
    run: sh -c "exit 7"

生产使用时把 http://127.0.0.1:1026/dingtalk 替换为真实机器人地址(钉钉:https://oapi.dingtalk.com/robot/send?access_token=xxx;企微:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx)。执行实测(dagu start /var/lib/dagu/dags/d2-webhook-demo.yaml):

├─broken_job (0s) [failed]
│ ├─sh -c "exit 7"
│ │
│ └─error: exit status 7
│
└─onFailure (0s) [succeeded]
  └─stdout: /var/lib/dagu/logs/d2-webhook-demo/dag-run_20261019_050954Z_034iRriexAo3VlBGrArMNX/run_20261019_050954Z_525342/onFailure.20261019.050954.804.034iRrie.out
      {"errcode":0,"errmsg":"ok"}

Result: Failed

接收端实测原始报文(群机器人收到的 JSON):

WEBHOOK-HIT path=/dingtalk content-type=application/json body={"msgtype":"text","text":{"content":"[Dagu] d2-webhook-demo 执行失败,请排查"}}

要点:钩子 stdout 里落了机器人响应 {"errcode":0,"errmsg":"ok"},告警是否送达可直接从日志判断;钉钉机器人若开启「自定义关键词」风控,文案里带上 [Dagu] 关键词即可。多 DAG 复用时把该钩子块写进 base.yaml 的 handler_on(全局继承,官方支持),消息文案中用固定字样区分来源。

💡 技术拓展:Webhook 接收格式主流三派——钉钉/企微的 {"msgtype":"text","text":{"content":"..."}} 文本消息体、通用 JSON 自由体(自建接收器)、以及标准化事件订阅(如 Slack Events 的 challenge 握手)。本文因目标端是钉钉/企微机器人,采用 msgtype 文本体;对接自有告警平台时改 POST 自由 JSON 即可,钩子内 curl 参数同步调整。

3.2 邮件失败通知

全局 SMTP 配置(写入 /var/lib/dagu/base.yaml,所有 DAG 继承):

smtp:
  host: smtp.example.com
  port: "587"

error_mail:
  from: dagu@example.com
  to: ops@example.com
  prefix: "[DAGU]"
  attach_logs: true

mail_on:
  failure: true

实测回显(本环境 SMTP 调试服务器收到失败邮件的原始头):

b'To: ops@example.com'
b'From: dagu@example.com'
b'Subject: [DAGU] c1-fail-demo (failed)'
b'Content-Type: text/html; charset=UTF-8'

主题规则 {prefix} {DAG名} ({状态});attach_logs: true 时附步骤日志。字段逐项说明与 OAuth(Microsoft 365/Google)配置见 《Dagu 2.21.0 配置参数详解》5.6 节。

3.3 服务级拨测(UI 可用性)

服务挂了但 DAG 都没跑时,前面两层告警都不会响(钩子随进程一起死)。补一个外部拨测(crontab 或其他监控平台执行):

curl -s -o /dev/null -m 5 -w "%{http_code}" http://<服务器IP>:8114/ | grep -q 200 || echo "Dagu UI DOWN" | mail -s "[ALERT] dagu down" ops@example.com

200 之外的状态(000 连接失败、502 代理失败)即触发。本例实测正常返回 200。

四、备份与恢复

Dagu 状态全部落盘,备份即打包两个目录:/var/lib/dagu(工作流定义+日志+执行历史+用户)与 /etc/dagu(服务端环境变量)。实测打包:

tar -czf /root/dagu-backup-$(date +%Y%m%d).tar.gz -C / var/lib/dagu /etc/dagu
ls -la /root/dagu-backup-*.tar.gz

实测回显:

tar: Removing leading `/' from member names
-rw-r--r-- 1 root root 18968 Oct 19 05:23 /root/dagu-backup-20261019.tar.gz
tar -tzf /root/dagu-backup-20261019.tar.gz | head -5

实测回显(节选):

var/lib/dagu/
var/lib/dagu/data/
var/lib/dagu/data/service-registry/
var/lib/dagu/data/notifications/
var/lib/dagu/dags/

备份策略建议:每日一次全量(空载约 20KB数 MB,成本可忽略),保留 730 天;只需保工作流定义时单独打包 /var/lib/dagu/dags/ 即可。恢复(在装好 Dagu 的机器上):

systemctl stop dagu
tar -xzf /root/dagu-backup-20261019.tar.gz -C /
systemctl start dagu
systemctl is-active dagu

实测回显:

active

恢复后用 2.3 的 API 探活确认 DAG 列表完整。迁移到新服务器 = 安装 Dagu(见《Ubuntu 22.04 安装 Dagu 2.21.0 指南》)+ 恢复备份 + 复核 dagu.env 中的监听地址与端口。

五、部署形态与扩容缩容

Dagu 无传统「扩容缩容」概念,按规模选形态(官方 Deployment Models 文档口径):

形态适用规模说明
单机单进程(本文形态)数十~数百个工作流start-all 一进程含 UI+调度器,文件状态
多机共享卷多台服务器共管 DAG调度器部署在 NFS/EFS/CephFS 上可感知其他主机的 DAG 变更(v2.21 起秒级同步)
分布式 worker单机算力不足dagu coordinator + 多台 dagu worker,按 workerSelector 分发(v2.21 支持按 run 选择 worker 与 worker 从零扩容等待)

纵向扩容一般不需要:单进程 RSS 实测 130MB 起步,max_active_steps(默认 10)是首要并发旋钮;横向扩容走 worker 形态,配合 base.yaml 的 queue 做跨 DAG 并发分组。缩容即反向操作,单机形态无状态外移负担。

六、升级与回滚

升级走安装器原地覆盖(实测记录与完整命令见《Ubuntu 22.04 安装 Dagu 2.21.0 指南》第七章),运维侧流程与校验:

# 1. 备份(升级前必做,见第四章)
tar -czf /root/dagu-backup-preupgrade-$(date +%Y%m%d).tar.gz -C / var/lib/dagu /etc/dagu
# 2. 升级(安装器;或手动替换二进制)
# 3. 复核被安装器重写的配置
grep -E 'DAGU_HOST|DAGU_PORT' /etc/dagu/dagu.env
# 4. 服务与版本确认
systemctl is-active dagu && dagu version
# 5. 业务确认:DAG 列表与最近执行历史
dagu ls

实测回显(升级后复核发现配置被还原的原始形态):

DAGU_HOST='127.0.0.1'
DAGU_PORT='8080'

按需改回 0.0.0.0/8114 并 systemctl restart dagu。回滚 = 装回旧版二进制(安装器 --version v2.20.1 实测可降级,服务自动重启);执行历史数据格式小版本间兼容,跨大版本回滚前以升级前备份恢复数据目录。

七、故障排查手册

以下均为实测原始报错与定位路径。

7.1 DAG 定义错误(DAG load failed)

现象:DAG 不出现在 UI/dagu ls,服务日志报错。实测原始报错:

time=2026-10-19T05:07:40.245Z level=ERROR msg="DAG load failed" err="[1:14] mapping value is not allowed in this context\n>  1 | description: \"C1 failure handler test\"\n..." file=/var/lib/dagu/dags/c1-fail-demo.yaml

定位:报错含「行:列」精确位置([1:14]),多为值含冒号/特殊字符未加引号。修复:dagu validate <文件> 通过后再触发;保存后日志出现 msg="DAG added/updated" 即加载成功。

7.2 远程访问 404/拒绝(DAGU_HOST 被还原)

现象:升级后宿主侧 curl http://<IP>:8114/ 实测返回 000(连接失败)。定位三步:

ss -ltn | grep 8114            # 实测仅见 127.0.0.1:8114 → 监听地址问题
grep DAGU_HOST /etc/dagu/dagu.env   # 实测 DAGU_HOST='127.0.0.1'(安装器重写还原)
sed -i "s/DAGU_HOST='127.0.0.1'/DAGU_HOST='0.0.0.0'/" /etc/dagu/dagu.env && systemctl restart dagu

修复后实测 curl 返回 200。该问题源于官方安装器每次重跑都重写 dagu.env,升级 SOP 中必须保留复核步骤(见第六章)。

7.3 API 401(认证方式不符)

现象:curl -u admin:q1w2e3r4 http://127.0.0.1:8114/api/v1/dags 返回 401。实测响应头:

HTTP/1.1 401 Unauthorized
Www-Authenticate: Bearer realm="restricted"

原因:2.x 内置用户体系用 Bearer JWT,不接受 Basic Auth。修复:先 POST /api/v1/auth/login 取 token(登录体 {"username":"admin","password":"q1w2e3r4"}),再带 Authorization: Bearer <token> 调用(完整命令见 2.3)。

7.4 步骤失败定位(exit status N)

现象:DAG 状态 failed。定位路径:UI 的 run 详情页查看步骤 stderr,或命令行直读日志文件:

grep -r "Step execution failed" /var/lib/dagu/logs/<DAG名>/ | tail -3
cat /var/lib/dagu/logs/<DAG名>/dag-run_*/run_*/<步骤名>.*.err

实测原始记录:

time=2026-10-19T05:06:53.537Z level=INFO msg="Step execution failed; retrying" step=flaky err="exit status 1" retry=0 exit-code=1

exit status 3、exit status 7 等即脚本退出码;retry=N 显示已重试次数,超过 retry_policy.limit 后最终失败并触发 onFailure 钩子。

7.5 服务反复重启/起不来

systemctl status dagu -n 50
journalctl -u dagu -b --no-pager | grep -E "ERROR|panic|Fatal" | tail -10

常见根因:/etc/dagu/dagu.env 语法错误(EnvironmentFile 解析失败)、DAGU_HOME 目录权限不足(User=root 时通常无此问题)、端口被占(v2.21 起端口被占时进程直接退出,日志含明确提示,改 DAGU_PORT 或释放端口)。

7.6 安装/升级下载失败(网络类)

实测原始报错:

curl: (56) OpenSSL SSL_read: Connection reset by peer, errno 104
curl: (7) Failed to connect to github.com port 443 after 21053 ms: Connection refused

处理:走代理(export http_proxy=http://192.168.1.62:7897 https_proxy=http://192.168.1.62:7897)重试,或手动下载二进制后替换(离线方案见安装篇第五章)。

八、常见问题(FAQ)

Q1:日志和历史会无限膨胀吗? 不会。base.yaml hist_retention_days(默认 30 天)/hist_retention_runs 自动清理过期 run;巡检 2.6 监控磁盘水位即可,异常膨胀通常是高频 DAG + 大 stdout,调低 max_output_size 或缩短保留期。

Q2:重试会不会造成重复副作用? 会。重试对幂等任务(备份覆盖写、状态上报)安全;非幂等任务(下单、发消息)在 DAG 或步骤级显式关闭重试(retry_policy.limit: 0),失败走人工介入。

Q3:worker 挂了正在跑的任务怎么办? 单机形态任务随进程消失,systemd 会拉起服务但该次 run 标记中断,可在 UI 手动 retry(同 run-ID 重放);分布式形态由 coordinator 的 worker 等待/重派机制处理(v2.21 的 worker_wait_timeout,默认 2m)。

Q4:升级会丢执行历史吗? 不会。历史在 /var/lib/dagu 与二进制无关;但升级 SOP 必须含「备份 + 复核 dagu.env」两步(实测安装器会重写该文件,见第六章)。

Q5:多个 DAG 需要统一并发上限? 用 base.yaml 的 queue 字段把相关 DAG 归入同一命名队列,队列内并发由调度器统一控制(官方注释:Assign DAGs to a named queue for concurrency control across DAGs)。

九、参考链接