systemd Timer 未触发:OnCalendar、Persistent 与错过执行排查
systemd Timer 未触发:OnCalendar、Persistent 与错过执行排查
先确认正在检查正确的管理器
系统级 timer 与用户级 timer 属于不同的 systemd 管理器。先确认配置位于 /etc/systemd/system、/usr/lib/systemd/system,还是用户目录,并保持命令作用域一致:系统级使用 systemctl,用户级使用 systemctl --user。
容器、远程主机和多套环境也会造成“改了 A、看了 B”。记录主机名、systemd 版本、unit 路径和实际命令作用域,不要只凭文件名判断。
用 list-timers 查看调度事实
先执行:
systemctl list-timers --all
systemctl status backup.timer
systemctl show backup.timer -p LoadState -p ActiveState -p NextElapseUSecRealtime -p LastTriggerUSec
NEXT 为空通常表示没有有效的下一次触发时间、timer 未激活,或配置没有被当前管理器采用。LAST 只能证明 timer 曾触发,不能证明 service 成功。再检查 backup.service 的状态和日志。
enable 与 start 不是一回事
systemctl enable backup.timer 创建开机激活所需的链接,但不会必然立即启动当前会话中的 timer。首次启用通常使用:
sudo systemctl enable --now backup.timer
反过来,单独 start 能让本次启动有效,却可能在重启后消失。用 systemctl is-enabled 和 systemctl is-active 分别验证持久启用状态与当前运行状态。
修改后必须让管理器重新读取
编辑 unit 文件后执行 systemctl daemon-reload,再重启 timer。仅重启对应 service 不会重新安排 timer。使用以下命令确认生效内容与覆盖关系:
systemctl cat backup.timer
systemctl cat backup.service
systemd-analyze verify /etc/systemd/system/backup.timer /etc/systemd/system/backup.service
systemctl cat 能显示主文件和 drop-in,适合发现旧覆盖项;但删除过的 drop-in 仍需 daemon-reload 才会从管理器状态中消失。
检查 OnCalendar 表达式
不要凭自然语言猜测日历表达式。使用与运行主机相同版本的工具解析:
systemd-analyze calendar 'Mon..Fri 03:30'
systemd-analyze calendar --iterations=5 'Mon..Fri 03:30'
输出会给出规范化表达式和后续触发时刻。重点核对星期、日期、时区和当前时间。若 unit 内显式写了时区,也要确认该时区在目标系统可用。
Persistent 只补偿特定的错过窗口
对于 OnCalendar= timer,Persistent=true 会记录上次触发信息。timer 因关机或未激活而错过计划时刻,重新激活后可能立即补触发一次。它不是历史任务队列,也不会逐次补跑关机期间错过的每个周期。
Persistent 对单调计时器的语义不同,不能拿它替代应用自己的任务账本。涉及计费、备份或不可重复操作时,service 仍应自行检查业务 checkpoint,并做到幂等。
AccuracySec 不是固定延迟
AccuracySec= 定义触发精度窗口,默认值允许 systemd 合并唤醒以节省资源。任务可能不会精确落在 OnCalendar 给出的那一秒。若业务要求较高精度,应明确设置较小值,但不要在大量主机上无必要地制造同一秒唤醒。
RandomizedDelaySec= 用于增加随机延迟、分散负载;它与 AccuracySec 的目的不同。观察到几分钟偏移时,先查看两项的最终有效值,不要直接判定 timer 丢失。
时钟与时区变化会影响日历 timer
OnCalendar 基于实时时钟。NTP 校时、手工改时、时区切换、休眠和恢复都可能改变“下一次”触发的观察结果。记录 timedatectl 输出,并比较日志里的单调时间与墙上时间。
不要用修改系统时钟的方式在生产验证。可先用 systemd-analyze calendar 计算,再在隔离环境创建短周期 timer 测试。
Timer 名称决定默认触发对象
backup.timer 默认触发 backup.service。若 [Timer] 中设置了 Unit=other.service,实际目标会改变。检查拼写、实例化单元参数以及目标 service 是否存在。
timer 与 service 同名并不要求 service 被 enable;service 通常由 timer 按需激活。错误地只 enable service,不能建立周期调度。
service 失败不能归类为 timer 未触发
同时检查:
systemctl status backup.service
journalctl -u backup.timer -u backup.service --since today
systemctl show backup.service -p Result -p ExecMainStatus
若 LastTriggerUSec 已更新,且 service 有对应启动日志,调度链路已工作。接下来排查 ExecStart 路径、权限、工作目录、环境变量、超时和退出码,而不是反复修改 OnCalendar。
长任务不会自动并发启动第二份
如果 timer 到点时目标 service 已处于 active,systemd 通常不会为同一个 service 再启动一份进程。看起来像“漏了一次”,实际是上一轮尚未结束。对比 service 的启动/结束时间、timer 周期和任务锁。
不要为追求每周期一次而盲目复制实例。先决定业务需要跳过、排队还是允许并行,再用队列、模板单元或应用层锁表达。
OnBootSec 与 OnUnitActiveSec 的基准不同
OnBootSec= 从系统启动时间计算;OnActiveSec= 从 timer 被激活开始;OnUnitActiveSec= 则与目标单元上次激活相关。把它们当作固定墙上时间,会造成重启后触发时间“漂移”的错觉。
若要求每天固定时间,通常使用 OnCalendar。若要求任务完成或激活后一段时间再运行,应选相应单调计时选项,并清楚它在重启后的行为。
用户 timer 还受登录与 linger 影响
用户管理器可能在用户退出后停止,导致 systemctl --user timer 不再运行。需要无人登录也运行时,应根据系统策略评估 linger,而不是把用户 unit 悄悄复制成 root 服务。
启用 linger 会改变该用户管理器生命周期,应遵循最小权限,并确认脚本不依赖图形会话、临时环境变量或登录 shell 初始化文件。
最小可复现配置
先用无破坏性的 service 验证调度:
# /etc/systemd/system/timer-test.service
[Service]
Type=oneshot
ExecStart=/usr/bin/logger timer-test-fired
# /etc/systemd/system/timer-test.timer
[Timer]
OnCalendar=*:0/5
Persistent=true
AccuracySec=1s
[Install]
WantedBy=timers.target
加载并启动后,用 list-timers 与 journal 验证。测试成功说明 systemd 调度基本正常,再逐项替换成真实 service,能快速定位是 timer 配置还是任务环境引入故障。
修复后的验收清单
验收至少包含:unit 来源正确;daemon-reload 已执行;timer 同时 enabled 和 active;list-timers 有合理 NEXT;日历表达式解析符合预期;目标 Unit 正确;触发后 LAST 更新;service 日志、退出码和产物一致;重启后 timer 仍存在;错过窗口的 Persistent 行为符合业务预期。
监控不要只检查 timer 为 active。应同时监控上次成功完成时间、任务产物新鲜度和连续失败次数,避免调度成功但业务长期失败仍显示绿色。
常见问题
timer 显示 active,为什么任务仍没运行?
active 只表示 timer 正在等待或已安排。检查 NEXT、LAST、实际 Unit=,再检查对应 service 的 journal 与退出状态。
enable 后为什么 list-timers 里仍没有?
enable 主要配置下次启动时的依赖关系。使用 enable --now 或另行 start,并确认命令作用域是系统级还是 --user。
关机期间错过十次,Persistent 会补跑十次吗?
不会。它用于在重新激活时发现错过的日历触发并补触发,不能当成逐条持久任务队列;精确补偿需应用层 checkpoint。
OnCalendar 为什么没有精确到指定秒?
检查 AccuracySec、RandomizedDelaySec、系统时钟和时区。默认精度窗口可能允许偏移,这不等同于漏触发。
手动启动 service 成功,为什么 timer 触发仍失败?
手动会话的环境、目录、权限和凭据可能与 systemd 服务不同。以 service unit 的实际执行上下文和 journal 为准。
来源资料
- systemd.timer 官方手册:https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html
- systemd.time 官方手册:https://www.freedesktop.org/software/systemd/man/latest/systemd.time.html
- systemctl 官方手册:https://www.freedesktop.org/software/systemd/man/latest/systemctl.html
- systemd-analyze 官方手册:https://www.freedesktop.org/software/systemd/man/latest/systemd-analyze.html