用 systemd 定时任务替代 cron:从入门到可落地的实操要点

举报
yd_229284667 发表于 2026/11/03 07:09:14 2026/11/03
【摘要】 一、先说结论:cron 能跑,不代表好管很多服务器上仍然用 crontab 跑备份、清理日志和拉取数据。写法简单,但痛点也很固定:任务失败了不容易发现,环境变量和 cron 的默认环境经常不一致,机器关机错过的任务不会补跑,多个任务之间也没有明确的依赖关系。systemd 的 timer 不是为了把 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 里的一行命令”,变成“系统可以查看、可以启用、可以排障的正式服务”。先从备份、清理这类低风险任务开始迁移,跑稳一个周期后再推广,收益会比一次性全量替换更可控。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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