systemd timer 配置与使用
systemd timer 配置与使用
本文说明如何创建、启停和排查 systemd 定时任务,并收录 lvbu 服务器上 zahui.fan 证书续期的实际配置。适用于使用 systemd 的 Linux;以下系统级配置命令以 root 身份执行。
1. 两个文件分别负责什么
一个定时任务通常由两个同名文件组成:
| 文件 | 用途 |
|---|---|
demo-job.service |
定义要执行的命令、用户、工作目录、超时时间 |
demo-job.timer |
定义执行时间,以及是否补跑错过的任务 |
例如:demo-job.timer 到时间后启动 demo-job.service,后者执行脚本,输出可以通过 journalctl 查看。同名 service 是默认目标,也可以在 timer 中用 Unit= 明确指定。官方说明
系统级自定义文件放在 /etc/systemd/system/。日常启用的是 .timer;需要立即执行一次时,启动 .service。
2. 从零配置一个定时任务
下面创建一个每天北京时间 03:00 输出当前时间的示例任务,名称为 demo-job。
2.1 创建脚本
1 | cat > /usr/local/sbin/demo-job <<'EOF' |
实际使用时,将脚本内容替换为备份、数据同步等操作。脚本中的失败需要以非零状态退出,systemd 才能识别任务失败。
2.2 创建 service
文件:/etc/systemd/system/demo-job.service
1 | [Unit] |
Type=oneshot 适合执行完就退出的任务。User= 是执行用户;系统级 service 未填写时默认使用 root。需要相对路径时,设置 WorkingDirectory=/实际目录;命令和文件路径尽量使用绝对路径。
执行完后 service 通常显示 inactive (dead),要结合退出状态和日志判断是否成功。周期任务通常不要设置 RemainAfterExit=yes,否则任务完成后仍保持 active,后续 timer 触发时不会再次执行。service 说明、执行环境说明
2.3 创建 timer
文件:/etc/systemd/system/demo-job.timer
1 | [Unit] |
OnCalendar= 指定日历时间。Persistent=true 会在 timer 再次启动时检查是否错过了执行时间,必要时补跑一次;停机多天不会按天补跑多次。它只适用于日历定时器,也不表示“任务失败后自动重试”。timer 参数说明
2.4 校验、手动执行、启用
1 | # 检查配置文件 |
enable 设置开机启用,start 启动当前任务;enable --now 同时完成两件事。启用 timer 后,通常等待下一次触发;开启 Persistent=true 且存在错过的时间点时,可能先补跑。systemctl 说明
3. 常用时间规则
OnCalendar= 的常见格式:
1 | 星期 年-月-日 时:分:秒 时区 |
星期可以省略,* 表示任意值。时区未填写时使用服务器本地时区;写明 Asia/Shanghai 可以固定为北京时间。时间语法说明
| 需求 | OnCalendar 值 |
|---|---|
| 每天 03:15 | *-*-* 03:15:00 Asia/Shanghai |
| 每小时整点 | *-*-* *:00:00 Asia/Shanghai |
| 每 5 分钟,按钟表时间对齐 | *-*-* *:00/5:00 Asia/Shanghai |
| 每周一 03:00 | Mon *-*-* 03:00:00 Asia/Shanghai |
| 周一至周五 09:00 | Mon..Fri *-*-* 09:00:00 Asia/Shanghai |
| 每月 1 日 02:00 | *-*-01 02:00:00 Asia/Shanghai |
先预览规则对应的时间,再修改配置:
1 | systemd-analyze calendar --iterations=5 '*-*-* 03:15:00 Asia/Shanghai' |
这个命令预览日历规则,不包含 timer 的随机延迟;实际排期用 systemctl list-timers 查看。
按时间间隔执行
如果需要“开机 5 分钟后开始,上次任务结束 1 小时后再执行”,可以用以下内容替换示例 timer 的整个 [Timer] 段:
1 | [Timer] |
OnUnitActiveSec= 从上次启动计时,OnUnitInactiveSec= 从上次结束计时。它们与按整点匹配的 OnCalendar= 含义不同。不要无意中保留旧的时间规则:多条规则通常是各自到时间都可以触发。间隔参数说明
随机延迟与时间精度
1 | RandomizedDelaySec=45m |
它在基准时间之后增加 0~45 分钟的随机延迟,适合让多台机器错开执行。另一个参数 AccuracySec= 默认是 1 分钟,会允许一定的触发合并窗口;timer 不保证精确到秒。需要更小的合并窗口时,可设置 AccuracySec=1s。精度与延迟说明
4. 日常管理命令
将下面的 demo-job 替换为实际任务名称。
| 操作 | 命令 |
|---|---|
| 查看所有已加载 timer,包含未运行的 | systemctl list-timers --all |
| 查看本任务排期 | systemctl list-timers --all demo-job.timer |
| 查看 timer 状态 | systemctl status demo-job.timer |
| 查看是否开机启用 | systemctl is-enabled demo-job.timer |
| 查看文件及覆盖配置 | systemctl cat demo-job.timer demo-job.service |
| 立即执行一次 | systemctl start demo-job.service |
| 暂停定时触发 | systemctl stop demo-job.timer |
| 恢复定时触发 | systemctl start demo-job.timer |
| 取消开机启用并暂停 | systemctl disable --now demo-job.timer |
| 查看上次结果与退出码 | systemctl show demo-job.service -p Result -p ExecMainStatus |
停止 timer 不会终止已经开始执行的 service。如确实需要中止正在运行的任务,另行执行 systemctl stop demo-job.service。管理命令说明
查看日志:
1 | # 最近 50 条日志 |
service 的标准输出和错误输出通常由 journal 收集,可用 -u 按服务筛选。日志说明
5. 修改已有任务
例如把示例任务改成每天 04:00:
1 | vi /etc/systemd/system/demo-job.timer |
将对应行改为:
1 | OnCalendar=*-*-* 04:00:00 Asia/Shanghai |
然后执行:
1 | systemd-analyze verify /etc/systemd/system/demo-job.service /etc/systemd/system/demo-job.timer |
daemon-reload 重新加载配置;重启 timer 后核对新的排期。只修改脚本内容,通常不需要 daemon-reload,下一次执行会读取新脚本。
也可以用 systemctl edit demo-job.timer 创建覆盖配置。覆盖已有日历规则时,先用空值清除,再写入新值:
1 | [Timer] |
否则新规则会与原规则叠加。修改后重启 timer,并用 systemctl cat 核对原文件与覆盖文件。规则重置说明
6. 常见问题
timer 已启用,但任务没执行
先运行 systemctl status demo-job.timer,确认状态为 active (waiting),再用 systemctl list-timers --all demo-job.timer 查看 NEXT。只有 enabled 并不代表当前已启动。随后检查时区、随机延迟和 service 日志。
在 SSH 终端能运行,timer 执行却失败
systemd 不会自动读取交互式 shell 的 alias、虚拟环境激活状态等配置。检查绝对路径、WorkingDirectory=、执行用户与权限,所需变量通过 Environment= 或合适的凭据配置显式提供。执行环境说明
ExecStart= 不会自动解释 shell 的 &&、管道和重定向。复杂逻辑放入脚本,或明确调用 shell。例如 Nginx 检查与重载已经放在上面的独立脚本中。命令语法说明
执行失败后如何处理
1 | journalctl -u demo-job.service -n 100 --no-pager |
修复日志中对应的问题后,手动启动 service 验证。若遇到启动频率限制,修复后可执行 systemctl reset-failed demo-job.service 再启动;它只清除失败状态,不修复任务本身。
上一次任务还没结束,下一次时间到了
同一个 service 仍在运行时,timer 不会再启动一个并发实例,也不会为每个错过的时间点排队。任务可能运行很久时,可按需要使用 OnUnitInactiveSec=,从上次结束后开始计算间隔。触发行为说明
7. 本机帮助
不同发行版的 systemd 版本可能不同,排查时优先参考目标服务器上的手册:
1 | systemctl --version |