Linux 入门:用 systemd timer 代替 cron 做定时任务

举报
yd_235948304 发表于 2026/11/09 07:02:10 2026/11/09
【摘要】 云主机上要做「每天备份、每小时清理日志、开机后延迟跑一次健康检查」,很多人第一反应是安装 cron。现代发行版里,systemd timer 往往更省事:它和现有 service 共用同一套启动依赖、用户权限和日志通道,失败时直接 systemctl status 和 journalctl 就能看到原因,不必再翻 /var/log/cron,也不用猜任务到底有没有被静默跳过。对已经用 sys...

云主机上要做「每天备份、每小时清理日志、开机后延迟跑一次健康检查」,很多人第一反应是安装 cron。现代发行版里,systemd timer 往往更省事:它和现有 service 共用同一套启动依赖、用户权限和日志通道,失败时直接 systemctl status 和 journalctl 就能看到原因,不必再翻 /var/log/cron,也不用猜任务到底有没有被静默跳过。

对已经用 systemd 管理业务进程的机器来说,再单独维护一份 crontab,等于两套调度系统并行。时间一长,常见后果是:改了脚本路径却忘了改 cron、机器关机错过触发后没有补跑、多人修改 crontab 时互相覆盖。把定时逻辑收进 timer,可以减少这类「看起来在跑、其实没跑」的隐患。

1. 先有一个普通 service

timer 只负责「什么时候触发」,真正干活的是一个 Type=oneshot 的服务。例如每天备份应用数据:

# /etc/systemd/system/app-backup.service
[Unit]
Description=Backup app data

[Service]
Type=oneshot
User=app
ExecStart=/usr/local/bin/app-backup.sh
Nice=10

先单独跑一遍,确认脚本本身没问题:

sudo systemctl start app-backup.service
sudo systemctl status app-backup.service
journalctl -u app-backup.service -n 50 --no-pager

脚本权限不对、备份目录不存在、或者依赖的环境变量缺失,都会在这一步暴露。service 都跑不通时,timer 再准时也没有意义,所以不要一上来就先写日历表达式。

2. 再写对应的 timer

# /etc/systemd/system/app-backup.timer
[Unit]
Description=Daily app backup

[Timer]
OnCalendar=*-*-* 03:15:00
Persistent=true
RandomizedDelaySec=5m

[Install]
WantedBy=timers.target

几个常用字段值得记住:

  • OnCalendar:日历表达式,比 cron 的五段格式好读。hourly、daily、Mon *-*-* 09:00:00 都可以。
  • Persistent=true:如果到点时机器正好关机或维护,下次开机后会补跑一次,特别适合备份、报表这类「错过了最好补上」的任务。
  • RandomizedDelaySec:加一点随机抖动。机房里几十台机器如果都在 03:15 整点打同一套备份存储,很容易把自己和对象存储一起打满。

启用并确认下次触发时间:

sudo systemctl daemon-reload
sudo systemctl enable --now app-backup.timer
systemctl list-timers app-backup.timer

list-timers 会显示上次和下次触发时间,这是确认表达式写对了最直观的办法。如果这里显示的下次时间不是你预期的窗口,先改 OnCalendar,不要等到第二天早上去看备份在不在。

想预览日历表达式是否合法,可以用:

systemd-analyze calendar '*-*-* 03:15:00'
systemd-analyze calendar 'Mon *-*-* 09:00:00'

它会告诉你规范化之后的时间,以及下一次匹配点,排错比盯着 unit 文件猜测更快。

3. 和 cron 怎么选

并不是所有场景都要迁移:

  • 任务本来就是一个 systemd 服务、需要等网络或数据盘就绪、希望日志进入 journalctl:优先 timer。
  • 只要临时跑一句命令,或镜像里根本没有 systemd(很多极简容器):继续用 cron、Kubernetes CronJob 或外部调度器即可。
  • 不要让 cron 和 timer 同时调度同一脚本,否则会重复执行,备份互相踩、清理任务删到一半再删一次都麻烦。迁移时先 enable --now 新 timer,观察一次成功后再注释掉 crontab 里的旧行。

如果脚本需要「开机后等网络好了再跑」,可以在 service 里加 After=network-online.target 和 Wants=network-online.target,这比在 cron 里 sleep 30 可靠得多。

4. 几个常见坑

  1. 只写了 .timer 忘记配套 .service,或两个文件名对不上。惯例是 名字.service 配 名字.timer,systemd 才会自动关联。
  2. 改完 unit 没有执行 daemon-reload,看起来像没生效,其实还在用旧配置。
  3. 写成 OnCalendar=daily 时,默认是当天 00:00,不一定是业务低峰。不确定就写成完整的 *-*-* 03:15:00。
  4. 脚本依赖环境变量、密钥或工作目录时,把它们写进 service 的 Environment=、EnvironmentFile= 和 WorkingDirectory=。不要假设 timer 会继承你交互登录时的 shell profile。
  5. 用普通用户写的脚本,service 里却忘了 User=,结果任务以 root 跑起来,生成的文件权限后续业务读不到。

下次再加定时任务,建议固定顺序:先让 service 能手动跑通,再配 timer,最后用 list-timers 和一次 start 验证。这样出问题时,你面对的是熟悉的 status 和 journalctl,而不是一条被静默跳过、几天后才发现没备份的 cron 行。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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