用 systemd timer 替代 crontab:Linux 定时任务的更可靠写法
前言
很多人习惯用 crontab 管理 Linux 定时任务:一行配置、简单直接。但任务一多,问题就来了:环境变量和交互式 shell 不一致、任务失败没有日志、无法限制资源、服务器重启期间错过的任务不会补跑。systemd timer 是 systemd 自带的定时机制,用 .timer 单元触发 .service 单元,能很好地解决这些痛点。本文介绍它的基本用法,并与 crontab 做对比。
一、基本结构:.service + .timer
systemd timer 由两个文件组成:一个 .service 描述"做什么",一个 .timer 描述"什么时候做"。两者同名即可自动关联。
1. 创建服务单元 /etc/systemd/system/backup.service
[Unit]
Description=Daily backup job
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Nice=10
2. 创建定时器单元 /etc/systemd/system/backup.timer
[Unit]
Description=Run backup every day at 02:30
[Timer]
OnCalendar=*-*-* 02:30:00
Persistent=true
RandomizedDelaySec=300
[Install]
WantedBy=timers.target
其中 Persistent=true 表示如果机器在计划时间处于关机状态,开机后会立即
补跑一次;RandomizedDelaySec 可以在多台机器上错开执行时间,避免同时压垮后端。
3. 启用并启动
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
注意要启用的是 .timer,而不是 .service。
二、OnCalendar 常见写法
OnCalendar 的格式为 “星期 年-月-日 时:分:秒”,常用例子如下:
OnCalendar=hourly:每小时整点OnCalendar=daily:每天 00:00OnCalendar=Mon *-*-* 09:00:00:每周一 09:00OnCalendar=*-*-01 00:00:00:每月 1 号零点OnCalendar=*:0/15:每 15 分钟一次
写完后可以用下面的命令校验表达式,并查看下一次触发时间:
systemd-analyze calendar "Mon *-*-* 09:00:00"
除了日历定时,还有相对定时:OnBootSec=5min 表示开机 5 分钟后执行,OnUnitActiveSec=1h 表示上次执行完成 1 小时后再执行。
三、查看定时器与日志
查看所有定时器、下次与上次触发时间:
systemctl list-timers --all
输出包含 NEXT、LEFT、LAST、PASSED、UNIT、ACTIVATES 等列,一眼就能看出任务是否按预期排期。
手动触发一次任务做测试:
sudo systemctl start backup.service
查看任务执行日志,这是相比 crontab 最大的优势之一:
journalctl -u backup.service -n 50 --no-pager
journalctl -u backup.service --since "today"
journalctl -u backup.timer -f
任务的标准输出和标准错误都会自动进入 journal,无需再手工重定向到日志文件。
四、与 crontab 对比
| 对比项 | crontab | systemd timer |
|---|---|---|
| 配置方式 | 一行文本,简单 | 两个单元文件,稍繁琐 |
| 日志 | 默认发邮件或需自行重定向 | 自动写入 journald |
| 错过的任务 | 关机期间错过则不补跑 | Persistent=true 可补跑 |
| 环境 | 环境变量极简,常踩坑 | 可用 Environment、EnvironmentFile 明确指定 |
| 资源限制 | 无 | 可使用 cgroup、Nice、MemoryMax 等 |
| 依赖管理 | 无 | 可用 After、Requires 声明依赖 |
| 随机延迟 | 需自己写 sleep | RandomizedDelaySec 原生支持 |
五、使用建议
- 简单的一次性小任务,crontab 依然够用;需要可观测、可补跑、有依赖关系的任务,优先选 systemd timer。
- 服务单元使用 Type=oneshot,并写明 User,避免以 root 运行不必要的任务。
- 修改单元文件后一定要 daemon-reload,再 restart 对应的 timer。
- 失败排查先看 systemctl status xxx.service,再看 journalctl 详细日志。
总结
systemd timer 把"定时"与"执行"拆开,配合 journald 与 cgroup,让定时任务更可靠、更易排查。迁移成本也不高:把原来 crontab 里的命令放进 ExecStart,把时间表达式改写成 OnCalendar 即可。建议从一个不重要的任务开始尝试,熟悉之后再逐步迁移。
- 点赞
- 收藏
- 关注作者
评论(0)