cron 与 systemd timer 的选型、写法与两个常见坑

举报
云上小栈 发表于 2026/11/02 00:06:11 2026/11/02
【摘要】 什么时候该用 systemd timercron 由 crond 守护进程按分钟粒度轮询 crontab,最小间隔 1 分钟,没有依赖管理,没有日志重定向以外的失败追踪。systemd timer 由 systemd 调度,支持秒级精度(OnCalendar=*-*-* *:*:00/15 可做到 15 秒),支持 Before/After 顺序、Requires 依赖、Persistent...

什么时候该用 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 加文件锁防重叠,这是最便宜的保险。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。