crontab 用了二十年,但它有几个真问题:失败无感知、依赖无法表达、错过执行不补跑、日志散落。systemd timer 把这些都解决了。
crontab 的四个痛点
- 失败了没人知道 —— 除非自己重定向邮件,而邮件服务往往没配
- 无法表达依赖 —— “等网络起来再跑”只能写
sleep 30这种脏办法 - 机器休眠就跳过 —— 笔记本合盖,任务直接不执行
- 日志难查 —— 各任务自己重定向,格式混乱
基本用法
两个文件:一个 service 定义干什么,一个 timer 定义什么时候干。
# /etc/systemd/system/backup.service
[Unit]
Description= nightly backup
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description= run backup nightly
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
启用:
systemctl daemon-reload
systemctl enable --now backup.timer
关键参数解释
Persistent=true —— 这是相比 cron 最大的优势之一。任务到点时机器是关的,下次开机会自动补跑。crontab 做不到这点。
RandomizedDelaySec=300 —— 随机延迟 5 分钟内执行。多台机器同时跑同一任务时,能错开对下游的冲击。批量机器上这条特别有用。
OnCalendar 语法:
| 表达式 | 含义 | |—|—| | *-*-* 02:30:00 | 每天 02:30 | | Mon *-*-* 09:00:00 | 每周一 09:00 | | *-*-01 00:00:00 | 每月 1 号 | | *:0/15 | 每 15 分钟 | | hourly / daily / weekly | 简写 |
依赖与前置条件
这是 systemd timer 真正的杀手级能力:
[Unit]
After=network-online.target
Requires=network-online.target
备份任务要等网络就绪。用 cron 只能猜一个 sleep 时间,现在能精确声明。
查看与调试
systemctl list-timers --all # 看所有定时任务及下次执行时间
systemctl status backup.timer # 看状态
journalctl -u backup.service -n 50 # 看上次执行的完整日志
list-timers 会显示 NEXT 和 LEFT,确认任务真的排上了,比 cron 的”不知道跑没跑”直观得多。
日志自动进 journald
不需要任何重定向,stdout/stderr 自动进 journald,journalctl -u 服务名 就能看。也不会无限增长——journald 自带配额和轮转。
从 cron 迁移的对照
| cron | systemd timer | |—|—| | 0 2 * * * cmd | OnCalendar=*-*-* 02:00:00 | | */15 * * * * cmd | OnCalendar=*:0/15 | | 失败无通知 | journalctl + 自行加告警 | | 休眠跳过 | Persistent=true 补跑 |
什么时候还留着 cron
一次性临时任务、容器里没有 systemd 的环境、或者团队所有人都在用 cron——一致性也是价值。但生产服务器的常规定时任务,迁移到 systemd timer 收益明显,尤其是 Persistent=true 和依赖声明这两点。