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 迁移对照表

cronsystemd timer
0 2 * * *OnCalendar=*-*-* 02:00:00
0 3 * * 1OnCalendar=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 只看错误级别,日志多了不翻到手酸。

常见坑

  1. 忘了 daemon-reload:改了 timer 文件必须 systemctl daemon-reload,否则改的是寂寞
  2. 时区:OnCalendar 用的是系统时区,timedatectl 确认时区对了
  3. WantedBy 写错:timer 的 [Install] 里是 WantedBy=timers.target,不是 multi-user.target,写错了 enable 不报错但不开机自启
  4. 脚本没执行权限: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。工具为人服务,别反过来。