systemd timer:cron 的现代替代,定时任务第二课
cron 够用,但 systemd timer 更现代:有日志、能管依赖、精度更高。小够教你写 service + timer 两个文件,把备份定时任务迁过来,一次讲透。
我是小够。前面讲过 cron 做定时任务,简单直接。但 cron 有两个短板:任务执行失败了没有现成的日志可查;机器在任务时间点关机了,这次任务就直接错过。systemd timer 是更现代的方案,有日志、有依赖管理、错过的任务可以补跑。
什么时候用 timer 不用 cron
- 需要看任务执行日志:
journalctl直接查 - 任务依赖网络/挂载:可以声明"网络就绪后再跑"
- 机器可能关机:
Persistent=true让错过的任务开机补跑 - 只是跑个简单脚本:cron 更省事,别折腾
小够的建议:备份、同步这类重要任务用 timer;随手的小脚本继续 cron。
两个文件
timer 由一对文件组成,放在 /etc/systemd/system/。
1. service 文件(定义要做什么):backup.service
[Unit]
Description=网站备份任务
[Service]
Type=oneshot
ExecStart=/root/scripts/backup.sh
2. timer 文件(定义什么时候做):backup.timer
[Unit]
Description=每天凌晨 2 点备份
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
OnCalendar 的写法很灵活:*-*-* 02:00:00 是每天凌晨 2 点,Mon *-*-* 03:00:00 是每周一凌晨 3 点。
启用
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
# 看所有 timer 状态
systemctl list-timers
查日志:最大的好处
# 看这个任务上次跑得怎么样
journalctl -u backup.service
# 看最近几次
journalctl -u backup.service --since "7 days ago"
cron 想做到这个,得自己在脚本里重定向日志,timer 天生就有。
实用技巧
- 随机延迟:多台机器同时跑任务会打爆目标,加上
RandomizedDelaySec=300,在 5 分钟内随机延迟 - 失败重试:service 里加
Restart=on-failure,任务失败自动重试 - 先手动跑一次:
systemctl start backup.service,确认脚本没问题再交给 timer
小够的建议
cron 不会消失,简单任务它依然是最快的选择。但备份、同步、检查这类"失败了你要知道"的任务,值得迁到 systemd timer 上。两个文件、三个命令,换来完整的日志和补跑机制,够用的很。
OnCalendar 语法速查
*-*-* 02:00:00 每天凌晨 2 点
Mon *-*-* 03:00:00 每周一凌晨 3 点
*-*-01 04:00:00 每月 1 号凌晨 4 点
*:0/15 每 15 分钟
比 cron 的 5 个星号直观多了,而且支持自然语言式的表达。
timer 的其他类型
除了日历型,还有:
[Timer]
OnBootSec=5min # 开机 5 分钟后跑一次
OnUnitActiveSec=1h # 上次跑完 1 小时后再跑(间隔型)
间隔型适合"每隔多久跑一次"的任务,不用算日历。
实战:备份 timer 完整例子
# /etc/systemd/system/site-backup.service
[Unit]
Description=网站备份
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/root/scripts/backup.sh
# /etc/systemd/system/site-backup.timer
[Unit]
Description=每天凌晨 2 点备份
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
RandomizedDelaySec=600
[Install]
WantedBy=timers.target
After=network-online.target 保证网络就绪才跑备份(要推送到异地的话很重要),RandomizedDelaySec 错峰。
cron 迁移对照表
| cron | systemd timer |
|---|---|
0 2 * * * | OnCalendar=*-*-* 02:00:00 |
0 3 * * 1 | OnCalendar=Mon *-*-* 03:00:00 |
*/15 * * * * | OnCalendar=*:0/15 |
| 日志靠自己重定向 | journalctl -u xxx 天生有 |
| 关机错过就错过 | Persistent=true 开机补跑 |
常见问题
Q:timer 没跑,怎么排查?
A:三步:systemctl status xxx.timer 看 timer 是否启用 → systemctl list-timers 看下次触发时间 → journalctl -u xxx.service 看上次执行日志。比 cron 好查得多。
Q:一个 timer 能触发多个 service 吗?
A:不能一对多,但可以写多个 timer 指向同一个 service,或者 service 里用 ExecStart 多行。简单场景够用了。
Q:cron 要全迁到 timer 吗? A:不用。简单的、跑起来没毛病的 cron 别折腾。新写的、重要的任务用 timer,老任务稳定运行就让它跑着。
timer 调试技巧
# 看 timer 下次什么时候触发
systemctl list-timers --all
# 手动触发一次(不等时间到)
systemctl start backup.service
# 看上次跑了多久、有没有报错
systemctl status backup.service
journalctl -u backup.service -p err
-p err 只看错误级别,日志多了不翻到手酸。
常见坑
- 忘了 daemon-reload:改了 timer 文件必须
systemctl daemon-reload,否则改的是寂寞 - 时区:
OnCalendar用的是系统时区,timedatectl确认时区对了 - WantedBy 写错:timer 的
[Install]里是WantedBy=timers.target,不是multi-user.target,写错了 enable 不报错但不开机自启 - 脚本没执行权限:
ExecStart指的脚本要chmod +x,或者写ExecStart=/bin/bash /root/scripts/backup.sh
cron 和 timer 混用
一台机器上 cron 和 timer 可以共存,小够的划分:
- cron:简单的、跑了很久没出过问题的
- timer:新的、重要的、需要日志和补跑的
别为了"统一"把稳定的 cron 全迁了,稳定本身就是价值。
监控 timer
# 检查所有 timer 都是 active
systemctl list-timers --no-pager | grep -v "active"
# 有输出就说明有 timer 没在跑
加到日常巡检里,或者写个脚本每天发报告。定时任务最大的风险不是配错,是悄悄停了没人知道。
日历表达式更多例子
*-*-* 02:00:00 每天凌晨2点
Mon..Fri *-*-* 09:00:00 工作日早上9点
Sat,Sun *-*-* 10:00:00 周末上午10点
*-*-01,15 03:00:00 每月1号和15号凌晨3点
比 cron 直观,Mon..Fri 这种范围写法 cron 得写 1-5。
AccuracySec:别太较真
[Timer]
OnCalendar=*-*-* 02:00:00
AccuracySec=1h
AccuracySec 允许系统在精度范围内"合并"任务,省电、省唤醒。默认 1 分钟,笔记本上可以设大点,服务器上保持默认。备份任务别设太大,差一小时可能撞上业务高峰。
小够的总结
systemd timer 不是来取代 cron 的,是给"重要任务"多一个选择。判断标准很简单:这个任务失败了,你想不想立刻知道? 想,就用 timer;无所谓,就用 cron。工具为人服务,别反过来。