crontab 与定时任务的可靠性
定时任务「在本机手动跑没问题,进了 crontab 就失败」几乎是每个运维的必经之路。原因和对策都很明确。
四个经典问题
1. 环境不同。 cron 的 PATH 只有 /usr/bin:/bin,没有你的 shell 配置。php、docker 等命令一律写绝对路径,或在 crontab 顶部显式设置:
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
0 3 * * * /usr/bin/php /srv/app/bin/backup.php >> /var/log/backup.log 2>&1
2. 无输出、无日志。 任务失败无人知晓,是最危险的状态。规则:stdout/stderr 必须重定向到日志,关键任务再加失败告警(脚本内部失败时调用通知接口)。
3. 重叠执行。 任务耗时超过间隔,两个实例并发跑,数据写乱。用文件锁:
* * * * * flock -n /tmp/sync.lock /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1
flock -n 拿不到锁直接退出,简单可靠。
4. 时区。 服务器是 UTC,业务按北京时间排期就会整体偏移 8 小时。timedatectl 确认系统时区,或用 CRON_TZ=Asia/Shanghai 显式声明。
提升可靠性的两个升级
systemd timer 的优势:Persistent=true 补跑错过的任务(服务器重启后不丢)、依赖管理(After=network-online.target)、journal 自动收集输出。对「必须执行」的任务(备份、对账)优先用它。
集中管理:crontab 分散在各用户和各机器上,出了问题没人知道全貌。把任务定义收进配置管理(哪怕只是一个 Git 仓库 + 部署脚本),命名统一放 /etc/cron.d/,比散落登录用户目录好排查得多。
备份任务的额外要求
- 脚本退出码必须真实反映成败(
set -e或显式exit 1); - 备份产物做一次恢复演练的排期——没有验证过恢复的备份等于没有备份;
- 记录每次任务耗时,突增(数据量暴涨/依赖变慢)往往是上游问题的第一信号。
最后一条实践:新任务上线先以 5 分钟间隔试跑一天,观察日志与资源占用,确认无误再调成正式周期。