systemd oneshot 任务总被超时杀掉?RuntimeMaxSec 对它无效
结论先放前面
Type=oneshot 的 unit,RuntimeMaxSec 不起作用。要限制它的运行时长,必须用 TimeoutStartSec。
很多人(包括我)照着常驻服务的经验配了 RuntimeMaxSec,然后发现任务要么照样被更短的时间掐掉,要么完全没被限制、跑到天荒地老。
一、两个参数各管哪段时间
参数 | 管什么 | 对 oneshot 有效吗
TimeoutStartSec | 从启动到"启动完成"的时间 | 有效
TimeoutStopSec | 停止阶段的时间 | 有效
RuntimeMaxSec | 服务进入 running 之后的最长运行时间 | 无效
原因在语义上:oneshot 就是"跑一下就退出",在 systemd 眼里它一直处于启动阶段(activating),从来没有进入过 running。既然没有 running 阶段,RuntimeMaxSec 就没有可计时的区间,配了也白配。
二、怎么确认到底是哪个参数在杀你
systemctl show 你的服务名 -p TimeoutStartUSec -p RuntimeMaxUSec -p Type
要看实际生效值,而不是看你配置文件里写了什么——drop-in 目录可能把主文件里的设置覆盖掉。
然后去日志里找关键词:
journalctl -u 你的服务名 --since '1 hour ago' | tail -30
日志关键词 | 对应参数
start operation timed out | TimeoutStartSec
runtime limit exceeded | RuntimeMaxSec
再看退出码:超时先发 SIGTERM(143),宽限期过了才 SIGKILL(137)。
三、正确写法
[Service]
Type=oneshot
ExecStart=/usr/local/bin/long-task.sh
TimeoutStartSec=2h
注意默认值:TimeoutStartSec 的默认是 90 秒(部分发行版显示为 1min 30s)。一个跑十分钟的备份脚本,不显式配这个值,就会稳定地在 90 秒被 TERM 掉,而你会以为脚本自己有 bug。
四、四种常见错法
1. 只配 RuntimeMaxSec —— 对 oneshot 完全无效,任务可以无限跑
2. 配 TimeoutStartSec=infinity —— 永不超时;卡死的任务会一直挂着,如果这个服务由 timer 或队列驱动,后面的任务全被堵住
3. 用 Type=simple 包一个会自己后台化的脚本 —— 脚本 fork 出去后自己退出,systemd 认为"启动完成",之后真正干活的子进程跑多久都不受 TimeoutStartSec 管
4. Type=simple 但忘了 KillMode 默认值 —— 默认 KillMode=control-group,主进程退出时 systemd 会把整个 cgroup 一起收掉,被后台化的子进程也跟着死。这个坑排查起来非常迷惑:日志显示脚本正常打印"已启动后台任务",然后什么都没有了
五、长任务该怎么选
场景 | 用什么
一次性任务,要限时长 | Type=oneshot + TimeoutStartSec
常驻守护进程 | Type=simple + RuntimeMaxSec(这时才有意义)
定时触发 | xxx.timer + 对应的 oneshot service
需要后台化子进程且要它活着 | Type=forking,或 KillMode=process,或改写程序不做后台化
六、Restart 对 oneshot 也别指望
Restart=always 和 Type=oneshot 是无效组合,systemd 会直接拒绝这种配置。一次性任务失败重试应该交给 timer 或者外部调度,而不是让 service 自己重启自己。
七、一个自查清单
systemctl show确认Type确实是 oneshotTimeoutStartSec显式写了,且大于任务实际最长耗时- 没有只靠
RuntimeMaxSec来做时长限制 - 任务内有打印进度,被 TERM 时能看出卡在哪一步
- 由 timer 触发的,确认上一轮还没跑完不会又起一轮(这是最常见的堆积原因)
小结
oneshot 用 TimeoutStartSec,simple 用 RuntimeMaxSec;90 秒默认值是最容易踩的坑;定位靠 systemctl show 加 journalctl 的关键词对照表。
- 点赞
- 收藏
- 关注作者
评论(0)