用 systemd timer 替代 cron 做定时任务的实战

举报
yd_229851404 发表于 2026/11/03 06:15:22 2026/11/03
【摘要】 在 Linux 服务器上做定时任务,很多人第一反应就是 cron。它简单、随处可见,用了几十年。但当任务越来越多、越来越重要时,cron 的短板也越来越明显:任务失败了没有人知道,日志散落在各处,机器关机期间错过的任务不会补跑,环境变量还得在脚本里一遍遍处理。这篇文章结合一次真实的备份任务改造,讲讲怎么用 systemd timer 替代 cron,以及迁移时要注意的几个坑。 一、为什么考虑...

在 Linux 服务器上做定时任务,很多人第一反应就是 cron。它简单、随处可见,用了几十年。但当任务越来越多、越来越重要时,cron 的短板也越来越明显:任务失败了没有人知道,日志散落在各处,机器关机期间错过的任务不会补跑,环境变量还得在脚本里一遍遍处理。这篇文章结合一次真实的备份任务改造,讲讲怎么用 systemd timer 替代 cron,以及迁移时要注意的几个坑。

一、为什么考虑换掉 cron

先说 cron 的几个实际痛点。第一,失败静默。cron 任务退出码非零时,默认只靠邮件通知,而现在很多服务器根本没配邮件,任务挂了几天都没人发现。第二,日志分散。cron 本身只记录任务是否启动,脚本自己的输出要靠重定向到文件,查问题时要在 syslog 和业务日志之间来回翻。第三,错过不补跑。机器在凌晨两点关机维护,备份任务就直接跳过了,不会自动补。第四,环境受限。cron 的环境变量极少,PATH、语言环境都得在脚本里重新声明,稍不注意就出现“手动能跑、定时失败”的经典问题。

systemd timer 正好补上了这些短板。它和 systemd service 配合:timer 只负责“什么时候触发”,service 负责“具体做什么”。执行记录统一进 journald,用 journalctl 就能查;支持 Persistent=true,错过执行开机后自动补跑;还能设置依赖、超时、随机延迟,精度可以到秒级。

二、一个最小例子:每天凌晨做备份

假设要每天凌晨 2 点半执行备份脚本 /usr/local/bin/backup.sh。需要两个文件,先写 service:

[Unit]
Description=每日备份任务
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

再写 timer,文件名要和 service 对应,叫 backup.timer:

[Unit]
Description=每天凌晨触发备份

[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true

[Install]
WantedBy=timers.target

启用只需要三条命令:

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

list-timers 会显示下次触发时间,确认时间没错,任务就算正式接管了。原来的 cron 那一行就可以注释掉,保留一段时间观察,确认无误再删除。

三、cron 表达式怎么换算

最常见的几种写法可以直接对照:每天凌晨两点半,cron 是 30 2 * * ,timer 写 OnCalendar=-- 02:30:00;每小时执行一次,cron 是 0 * * * *,timer 可以写 OnCalendar=hourly;每 15 分钟一次,cron 是 /15 * * * ,timer 写 OnCalendar=:0/15;每周一早上九点,cron 是 0 9 * * 1,timer 写 OnCalendar=Mon -- 09:00:00。

除了固定时间,timer 还支持相对时间。OnBootSec=15min 表示开机 15 分钟后执行,OnUnitActiveSec=1h 表示上次执行完一小时后再执行一次,适合做“间隔执行”而不是“固定时刻执行”的任务,这是 cron 做不到的。

四、排障就用这三条命令

任务没按预期跑,先看 timer 状态:systemctl status backup.timer 能看到是否启用、下次触发时间。再看 service 的执行记录:journalctl -u backup.service --since today,直接看到脚本的全部输出和退出码,比翻 cron 日志方便得多。如果想看全系统所有定时器,systemctl list-timers --all 一条命令就够了。

五、迁移时要注意的四个坑

第一,service 的 Type 建议用 oneshot,表示一次性任务,执行完即退出,超时和失败状态都清晰。第二,脚本里一律写绝对路径,ExecStart、脚本内部调用的其他命令都一样,别依赖 cron 或者交互 shell 的 PATH。第三,需要的环境变量不要写在脚本里猜,用 service 文件的 Environment= 或 EnvironmentFile= 显式声明,出了问题一眼就能看到。第四,多台机器同时跑定时任务去压同一个数据库时,可以在 timer 里加 RandomizedDelaySec=5min,让触发时间在五分钟内随机打散,避免惊群。

另外提醒一点:Persistent=true 只补跑“错过”的触发,机器长期关机期间错过的多次触发只会补跑一次,不会攒着全补回来,这对备份这类任务恰恰是合理的行为。

结语

cron 不会消失,简单的一次性任务继续用它完全没问题。但当定时任务开始承载备份、清理、同步这类“失败了要有人知道、错过了要补跑”的职责时,值得花半小时迁移到 systemd timer:一个 service 加一个 timer,日志、补跑、依赖都交给 systemd 统一管理,后续排障和交接都会轻松很多。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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