从 cron 到 systemd timer:一次定时任务迁移的完整实践

举报
yd_213494201 发表于 2026/10/22 23:12:43 2026/10/22
【摘要】 把一行 crontab 拆成 service + timer:日志归集、失败重试、错过补跑、执行抖动,一次迁移解决 cron 排障难、无依赖控制、不感知系统状态三个老问题。

从 cron 到 systemd timer:一次定时任务迁移的完整实践

一、为什么要把 cron 换掉

cron 简单可靠,几乎每台 Linux 上都有。但它在近几年暴露了三个明显的短板:

  1. 没有日志归集:任务输出只进 MAILTO,容器里常常直接丢失,排障全靠 2>&1 >> file 手工重定向。
  2. 没有依赖与时序控制:上一条任务还没跑完,下一条又起来了,只能靠 flock + 手写锁。
  3. 不感知系统状态:错过执行时间(机器关机、休眠)后不会补跑,也没有"延迟执行""随机抖动"这类能力。

systemd timer 把这些能力交给了 systemd 本身:统一日志走 journald、After=/Requires= 管依赖、Persistent=true 支持补跑、RandomizedDelaySec= 做抖动,写起来也不复杂。

二、把一行 crontab 拆成两个 unit

假设原来有这一行:

# 每天 06:00 备份数据库
0 6 * * * /usr/local/bin/backup-db.sh >> /var/log/backup.log 2>&1

迁移后拆成 service 和 timer 两个文件。

/etc/systemd/system/backup-db.service:

[Unit]
Description=Backup MySQL database
After=network-online.target mysql.service
Wants=network-online.target

[Service]
Type=oneshot
User=backup
WorkingDirectory=/opt/backup
ExecStart=/usr/local/bin/backup-db.sh
# 失败重试:5 分钟内最多 3 次
Restart=on-failure
RestartSec=30s
StartLimitIntervalSec=300
StartLimitBurst=3

/etc/systemd/system/backup-db.timer:

[Unit]
Description=Run backup-db.service every day at 06:00

[Timer]
OnCalendar=*-*-* 06:00:00
Persistent=true
RandomizedDelaySec=300
AccuracySec=1s
Unit=backup-db.service

[Install]
WantedBy=timers.target

启用:

sudo systemctl daemon-reload
sudo systemctl enable --now backup-db.timer
systemctl list-timers --all | grep backup

三、几个关键参数的取舍

Persistent=true 解决"错过执行"问题:系统关机错过了 06:00,下次开机后 systemd 会立刻补跑一次。对备份这类必须每天有一次的任务非常关键;对"只在整点窗口内才有意义"的任务(比如 09:30 抢券脚本)就要谨慎,最好在脚本内部再判断一次时间窗口。

RandomizedDelaySec 是为了避免大量机器同一秒打向同一台服务器。cron 时代靠 sleep $((RANDOM % 300)),现在一行配置就够了。

AccuracySec 默认是 1min,systemd 为了省电会把多个 timer 合并唤醒,触发时间可能晚最多一分钟。要求准点就显式写 AccuracySec=1s。

OnCalendar 与 OnUnitActiveSec 的区别:前者是绝对时间(每天 06:00),后者是相对时间(上次激活后 30 分钟),等价于 cron 做不到的 */30 漂移执行。

四、日志:从重定向到 journalctl

service 里不需要再写 >> /var/log/xxx.log,直接:

journalctl -u backup-db.service -n 100 --no-pager
journalctl -u backup-db.service --since "24 hours ago"

如果确实需要落文件,加一行:

[Service]
StandardOutput=append:/var/log/backup.log
StandardError=append:/var/log/backup.log

既保留了文件,也留了一份在 journald 里可以按时间检索。

五、用 systemd-analyze 验证日历语法

OnCalendar 的语法有坑,写错了不会报错,只会永远不触发。用:

systemd-analyze calendar "*-*-* 06:00:00"
systemd-analyze calendar --iterations=5 "Mon..Fri 09:30"

它会直接打印下一次和之后的触发时间,是最省事的自检方式。

六、迁移清单

  • 逐条把 crontab 拆成 xxx.service + xxx.timer;
  • 需要补跑的任务加 Persistent=true;
  • 批量任务加 RandomizedDelaySec,避免惊群;
  • 原脚本里的 flock 可以保留,也可以换成 Type=oneshot + ExecStartPre 做前置检查;
  • 用 systemctl list-timers 和 journalctl -u 建立新的观测习惯。

七、小结

systemd timer 不是银弹,它依赖 systemd,容器化环境里仍然可能优先用 cron 或调度平台。但在常规 Linux 服务器上,它把日志、依赖、补跑、抖动四件事一次性解决掉,配置成本也不高。一次迁移之后,排障时的"这个任务到底跑没跑、输出在哪"这类问题基本就消失了。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。