用 systemd 定时任务替代 cron:从入门到可落地的实操要点
一、先说结论:cron 能跑,不代表好管
很多服务器上仍然用 crontab 跑备份、清理日志和拉取数据。写法简单,但痛点也很固定:任务失败了不容易发现,环境变量和 cron 的默认环境经常不一致,机器关机错过的任务不会补跑,多个任务之间也没有明确的依赖关系。systemd 的 timer 不是为了把 cron 写得更复杂,而是把“什么时候跑”和“跑什么”拆成两件可管理、可排障的配置。
二、timer 由两个单元组成
一个定时任务通常需要两个文件:一个是真正干活的 service,一个是决定时间的 timer。timer 本身不执行命令,它只是在时间到了之后激活对应的 service。默认情况下,同名的 timer 会激活同名的 service,例如 backup.timer 激活 backup.service。
# /etc/systemd/system/backup.service
[Unit]
Description=每日备份应用数据
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=backup
WorkingDirectory=/srv/app
ExecStart=/usr/local/bin/backup.sh
# /etc/systemd/system/backup.timer
[Unit]
Description=每天凌晨两点半执行备份
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
[Install]
WantedBy=timers.target
注意这里的 Type=oneshot:它表示这个服务执行一次就退出,正好对应一次性定时任务。如果写成 Type=simple,systemd 会认为服务一直在运行,反而不利于判断一次任务是否正常结束。
三、OnCalendar 比 cron 更直观,也更容易写错
OnCalendar 的常见写法包括:每天固定时间用 *-*-* 02:30:00;每周一用 Mon *-*-* 02:30:00;每月 1 号用 *-*-01 03:00:00;每 15 分钟用 *-*-* *:00,15,30,45:00。它的星号和 cron 的星号意思接近,但字段顺序和分隔方式不同,不要直接把 crontab 的五个字段原样粘贴过来。
除了日历时间,systemd 还支持单调定时器:OnBootSec 表示开机后多久执行,OnUnitActiveSec 表示上次执行后间隔多久再执行。后者适合“每隔一段时间跑一次,但不关心具体几点”的任务,例如每 6 小时同步一次缓存。
四、落地步骤:只记住这几条命令
systemctl daemon-reload
systemctl enable --now backup.timer
systemctl list-timers --all | grep backup
systemctl status backup.service
journalctl -u backup.service --since today
daemon-reload 是在新增或修改单元文件后必须做的,否则 systemd 读到的还是旧配置。enable 让 timer 开机自启,–now 让它立刻开始计时。list-timers 可以看到下一次触发时间,这是 cron 最难直接给出的信息。journalctl 则把服务的输出集中到一处,不用再到处找日志文件。
五、生产环境最容易踩的六个坑
第一,环境变量。cron 和 systemd 的默认环境都不等于交互式 shell 的环境,PATH、语言环境和应用配置都可能缺失。稳妥做法是在 service 里用 EnvironmentFile 显式加载,或者在脚本开头写绝对路径。
第二,工作目录和用户。没有 WorkingDirectory 时,服务默认工作目录是根目录;没有 User 时,默认以 root 运行。备份、清理类任务尤其要先定好用户和目录,避免误删或写错位置。
第三,错过执行。Persistent=true 的作用是:如果机器在预定时间关机或休眠,恢复后会尽快补跑一次。对备份、报表这类“不能漏”的任务,这个选项几乎是必选项。
第四,任务重叠。备份还没跑完,下一次又触发了,就可能出现两个进程写同一份数据。可以在 service 里用单实例思路处理,或者在脚本里加锁文件,并在设计上保证任务幂等。
第五,失败处理。定时任务失败后,默认不会自动重试。可以在 service 里设置 Restart=on-failure 和 RestartSec,或者用 OnFailure 触发一个告警服务,把失败变成看得见的事件,而不是静默丢失。
第六,时钟与时区。OnCalendar 默认使用系统本地时区,服务器时区改了,触发时间也会跟着变。跨时区部署前,先用 timedatectl 确认时区和 NTP 同步状态,再核对 list-timers 显示的下一次时间。
六、从 cron 迁移的检查清单
迁移时不要只抄时间,先把 crontab -l 的每一条任务列出来,逐条确认四件事:它依赖哪些环境变量,输出原来写到哪里,失败时有没有人知道,以及它能不能重复执行。输出建议从邮件和重定向改为 journalctl;原来靠邮件发现失败的任务,改成 OnFailure 告警或监控采集退出码。
迁移完成后,至少观察一个完整周期:用 list-timers 核对下一次时间,用 status 和 journalctl 核对上一次结果,再故意停掉一次相关服务或模拟失败,确认告警链路真的能走到人这里。
七、什么时候仍然可以用 cron
如果只是在个人开发机上跑一条简单的定时提醒,或者系统已经有成熟的 cron 运维规范,没有必要为了迁移而迁移。systemd timer 的优势出现在需要补跑、依赖管理、统一日志、精细权限和失败告警的场景。换句话说,任务越重要、越不能静默失败,就越值得用 timer。
结语
把 cron 换成 systemd timer,本质是把定时任务从“写在个人 crontab 里的一行命令”,变成“系统可以查看、可以启用、可以排障的正式服务”。先从备份、清理这类低风险任务开始迁移,跑稳一个周期后再推广,收益会比一次性全量替换更可控。
- 点赞
- 收藏
- 关注作者
评论(0)