登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux systemd 服务异常退出后怎样自动重启

来源:17golang原创

时间:2026-10-18 14:47:01 355浏览 收藏

如果 Linux 上的常驻服务异常退出,systemd 通常应该使用 Restart=on-failure 自动尝试恢复,再用 RestartSec 拉开两次启动的间隔。这个选择比 Restart=always 更容易保留“正常结束”和“真正故障”的区别;如果服务只想在信号或超时等异常情况下重启,则可以考虑 on-abnormal。

官方文档:https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html

先记住三个判断:
  • 常驻 worker 发生非零退出、异常信号或超时,通常选择 Restart=on-failure。
  • 管理员执行 systemctl stop 不应被当成故障,systemd 不会因此自动拉起服务。
  • 连续失败时先看 Result、ExecMainStatus 和 NRestarts,不要只把重启次数调大。

先分清异常退出和主动停止

systemd 的重启判断看的是服务进程怎样结束:退出码、信号、启动或停止操作超时,以及 watchdog 等状态都会参与判断。对于长期运行的服务,on-failure 会覆盖非零退出和异常信号,也会覆盖相关超时;但由 systemd 自己执行的停止或重启,不会再次触发自动重启。

这一区分很重要。发布脚本执行 systemctl restart 时,期望的是一次明确的运维动作;程序崩溃时,才需要让 unit 尝试恢复。如果把两者混成“只要进程没了就永远拉起”,服务就很难被安全停机。

按场景比较三种 Restart 策略

我在配置常驻服务时,通常先把需求归到下面三类,再决定参数,而不是一上来就用 always。它们的差别不在于“能不能重启”,而在于哪些退出被认为值得重试。

场景优先选择适合的原因
常驻服务,非零退出通常表示故障on-failure能恢复异常退出,同时保留正常退出语义
允许服务主动结束,只兜底信号和超时on-abnormal不会因为普通成功退出而重新拉起
任何结束都必须再次启动always适合确实需要无条件常驻的服务,但会掩盖退出原因

如果服务是一次性任务,不能因为看到了“自动重启”就套用常驻进程策略。尤其是 Type=oneshot,成功退出和 always、on-success 还有额外限制;这时应先确认 unit 的生命周期,再决定是否需要重启。

systemd 三种 Restart 策略按退出原因区分的静态关系图
图1:systemd Restart 策略的选择说明图,比较 on-failure、on-abnormal 与 always 的退出边界;这是原创静态说明图,不是运行截图。

用等待间隔和启动限流防止重启风暴

只写 Restart=on-failure 还不够。服务如果每次启动都立刻失败,systemd 会反复尝试,日志和依赖服务都会被拖入噪声。可以把等待时间和窗口内的启动次数一起写进 unit:

[Unit]
Description=Demo worker service
After=network-online.target
Wants=network-online.target
# 60 秒窗口内最多允许 5 次启动尝试,避免持续崩溃形成风暴
StartLimitIntervalSec=60s
StartLimitBurst=5

[Service]
Type=exec
ExecStart=/usr/local/bin/demo-worker
# 只对失败退出自动重启,正常退出仍保留为正常结果
Restart=on-failure
# 给依赖恢复和日志落盘留出时间
RestartSec=5s

[Install]
WantedBy=multi-user.target

RestartSec 控制一次自动重启前的等待;StartLimitIntervalSec 和 StartLimitBurst 则是启动速率保护。达到限制后,问题就从“要不要重启”变成了“为什么一直启动失败”,不应简单地把 Burst 调得很大。

复查服务状态,而不是只看 active

修改 unit 后,先重新读取配置,再观察退出原因、重启次数和日志。下面的命令只展示复查路径,输出内容会随服务本身和部署环境变化。

# 让 systemd 重新读取 unit 文件
sudo systemctl daemon-reload
# 启用并立即启动服务
sudo systemctl enable --now demo-worker.service
# 查看当前状态、最近一次结果和退出状态
systemctl status demo-worker.service --no-pager
# 读取适合排障的结构化字段
systemctl show demo-worker.service \
  -p ActiveState -p SubState -p Result \
  -p ExecMainCode -p ExecMainStatus -p NRestarts
# 查看当前启动周期内的服务日志
journalctl -u demo-worker.service -b --no-pager -n 80

如果 Result=exit-code,继续对照 ExecMainStatus 判断应用返回了什么;如果是 Result=start-limit-hit,说明 unit 已触发启动限流,应该回到日志和依赖上找根因。修复后可以执行下面的清理动作,再重新启动:

# 修复配置或依赖后清理 unit 的失败状态
sudo systemctl reset-failed demo-worker.service
# 重新发起一次启动,观察是否还会连续失败
sudo systemctl start demo-worker.service
systemd Result、ExecMainStatus 与 NRestarts 的排障关系说明图
图2:systemd 服务状态字段说明图,把退出码、结果、重启次数和启动限流放在同一排障关系中;这是原创静态说明图,不是运行截图。

几个容易误判的边界

写了 on-failure 还是不重启? 先检查服务是否以 systemd 认为“成功”的方式结束、是否命中了 RestartPreventExitStatus=,以及是否已经触发启动速率限制。不要先把策略改成 always。

什么时候使用 on-abnormal? 当程序有明确的正常退出路径,而你只希望对异常信号、超时或 watchdog 做恢复时,它比 on-failure 更贴近语义。

退出码能不能参与重启决策? 可以。对于代表“配置不可用、继续重试没有意义”的退出码,可以配合 RestartPreventExitStatus= 停止无效循环;但这需要应用和运维先约定退出码含义。

常见问题

systemctl stop 后服务为什么没有自动启动?

这是预期行为。systemd 主动执行的停止操作不属于需要自动恢复的进程故障,否则管理员将无法可靠停机。

on-failure 和 always 怎么选?

长期运行服务通常先用 on-failure;只有确实要求任何退出都重新启动时才用 always。如果服务允许正常结束但要兜底异常信号和超时,可以考虑 on-abnormal。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>