cron 与 systemd timer 的选型、写法与两个常见坑
什么时候该用 systemd timer
cron 由 crond 守护进程按分钟粒度轮询 crontab,最小间隔 1 分钟,没有依赖管理,没有日志重定向以外的失败追踪。systemd timer 由 systemd 调度,支持秒级精度(OnCalendar=*-*-* *:*:00/15 可做到 15 秒),支持 Before/After 顺序、Requires 依赖、Persistent=true 补跑,并把 stdout/stderr 交给 journald。
结论很直接:需要秒级精度、需要任务间依赖、需要开机补跑、需要结构化日志,用 timer;只是每天凌晨跑个备份脚本,cron 一行就够。
cron 的写法要点
系统级任务放 /etc/cron.d/,比改 root 的 crontab 更好管理。注意 cron.d 格式比用户 crontab 多一个用户字段:
# /etc/cron.d/backup SHELL=/bin/bash PATH=/usr/local/bin:/usr/bin:/bin 30 2 * * * root /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
cron 的执行环境 PATH 默认只有 /usr/bin:/bin,脚本里调用 /usr/local/bin 下的工具会报 command not found。最省事的做法是在 crontab 顶部显式声明 SHELL 和 PATH,而不是在脚本里到处写绝对路径。
systemd timer 的写法要点
一个 timer 必须配一个同名的 service,两者放同一目录(用户级放 ~/.config/systemd/user/,系统级放 /etc/systemd/system/):
# backup.service [Unit] Description=daily backup [Service] Type=oneshot ExecStart=/opt/scripts/backup.sh User=ops # backup.timer [Unit] Description=run backup at 02:30 [Timer] OnCalendar=*-*-* 02:30:00 Persistent=true RandomizedDelaySec=300 [Install] WantedBy=timers.target
启用并验证:
systemctl daemon-reload systemctl enable --now backup.timer systemctl list-timers --all journalctl -u backup.service -n 50 --no-pager
坑一:cron 任务在容器里根本不执行
现象:crontab 写好了,service cron status 显示 running,但任务从不触发,/var/log/cron 里没有任何记录。原因通常是容器基础镜像里 /etc/pam.d/cron 缺失或 PAM 配置导致 crond 拒绝会话,或者 /etc/cron.d 里的文件没有 644 权限、文件名带点号(如 backup.sh),被 cron 直接忽略。
解决:文件名不带扩展名,权限 chmod 644;容器场景更稳的做法是改用 systemd timer 或者前台进程守护(supervisor、s6),别在容器里依赖 crond。验证手段是看 journalctl -u cron 或 ps aux | grep cron 确认真的有一个 crond 进程在跑,而不是只看服务状态。
坑二:Persistent=true 导致任务重复执行
现象:服务器维护停机 3 天,开机后 backup 任务连续跑了 3 次,因为 Persistent=true 会记住上次触发时间,停机期间错过的每次 OnCalendar 都补跑一次。对于幂等脚本无所谓,对推送、扣款这类任务就是事故。
解决:需要补跑一次但只跑一次的,在 service 里加 [Timer] 的 OnCalendar 保持不变,但把脚本改成幂等,并用文件锁兜底:
ExecStart=/usr/bin/flock -n /var/lock/backup.lock /opt/scripts/backup.sh
只关心开机后跑一次、不关心错过几次的,用 OnBootSec=5min 配合 Persistent=false,把补跑逻辑和周期逻辑拆开。
小结
分钟级、无依赖、单机简单的任务用 cron,注意 PATH、cron.d 文件权限和文件名。需要秒级精度、补跑、依赖和日志归集的用 systemd timer,重点检查 Persistent 的语义和 service 的幂等性。两者都可以用 flock 加文件锁防重叠,这是最便宜的保险。
- 点赞
- 收藏
- 关注作者
评论(0)