用 systemd timer 替代 cron:给定时任务加上日志、依赖和失败重试
很多人的第一个定时任务都是从 crontab 开始的:一行表达式、一个脚本路径,简单直接。但当任务变多之后,cron 的短板会慢慢显现——任务失败了没有统一日志、机器重启期间错过的执行不会补跑、任务之间有先后依赖时只能靠 sleep 硬凑。这时可以考虑 systemd 自带的 timer:它不是要完全取代 cron,而是给那些「需要被认真对待」的定时任务提供一套更完整的运行环境。
一、从一行 cron 到两个单元文件
systemd 的定时任务由两个文件组成:一个 .service 描述「做什么」,一个 .timer 描述「什么时候做」。以每天凌晨备份日志目录为例,先写 service:
# /etc/systemd/system/log-backup.service
[Unit]
Description=Backup application logs
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-logs.sh
再写同名 timer:
# /etc/systemd/system/log-backup.timer
[Unit]
Description=Run log backup daily
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
执行 systemctl daemon-reload && systemctl enable --now log-backup.timer 后即可生效。其中 Persistent=true 是关键差异:关机错过的执行会在下次开机时补跑,这对笔记本和不定期开机的开发机非常友好;RandomizedDelaySec 则能把大量任务错峰,避免整点雪崩。
二、日志和状态,一条命令就能看
cron 的输出通常靠重定向到文件或本地邮件,出问题时要翻好几个地方。systemd timer 的每次运行都是一次完整的 service 启动,直接用 journalctl 查看:
systemctl list-timers # 下次触发时间和上次结果
journalctl -u log-backup.service --since today
systemctl status log-backup.service
失败退出码、标准输出和错误输出都在同一处,新人接手时不用再猜「这个任务到底跑没跑」。
三、依赖与重试,写在配置里而不是脚本里
如果备份必须等挂载完成、失败后希望自动重试,这些在 cron 里都要写进脚本自己处理,而 systemd 只需在 service 中声明:
[Unit]
RequiresMountsFor=/data
StartLimitIntervalSec=0
[Service]
Restart=on-failure
RestartSec=10min
把调度策略从脚本逻辑中抽出来,脚本本身只需要专注做事、失败时以非零码退出即可,职责更清晰。
四、什么时候继续用 cron
需要明确的是,timer 并非处处更好:一次性简单提醒、跨发行版可移植的最小任务、以及团队已经有成熟 crontab 管理规范时,cron 依然足够。另外,timer 依赖 systemd 环境,容器和 macOS 上并不适用。我的经验法则是:任务失败需要被看见、错过需要被补跑、顺序需要被保证时,上 timer;只是「到点跑一下」时,留给 cron。
定时工具本身没有高下,关键是让任务的运行状态可观测。能回答「上次什么时候跑的、成功了吗、日志在哪」这三个问题,就是一套合格的定时方案。
- 点赞
- 收藏
- 关注作者
评论(0)