systemd服务崩溃后自动重启不生效,主因是默认仅对非正常退出触发重启,需合理配置restart=、restartsec=、startlimitburst/intervalsec,并结合watchdogsec、bindsto及反向代理健康检查实现端到端故障恢复。

systemd 服务崩溃后自动重启不生效?检查 Restart= 和 RestartSec= 配置
很多服务加了 Restart=always 却没真正重启,根本原因是 systemd 默认只对「非正常退出」(如 crash、kill -9)触发重启,而 Restart=always 确实能覆盖正常退出(exit code 0),但前提是服务不能被 systemd 认定为「成功启动完成」——否则它会跳过后续重启逻辑。
实操建议:
-
Restart=on-failure更贴近「故障恢复」本意:只在非 0 退出、超时、被信号终止时重启;on-abort或on-watchdog可按需补充 - 务必配
RestartSec=5(单位秒),否则默认 100ms,高频崩溃可能触发 rate limiting,导致服务被StartLimitIntervalSec拦住 - 检查
StartLimitBurst=和StartLimitIntervalSec=,默认是 5 次/10 秒,频繁失败时 systemd 会彻底拒启,日志里会出现start request repeated too quickly - 用
systemctl show <service-name> | grep -E "(Restart|StartLimit)"</service-name>快速核对当前生效值
服务启动成功但内部卡死,systemd 怎么感知不到?加 HealthCheck= 或 ExecStartPost= 检测
systemd 默认只管进程是否 fork 出来、是否返回 exit code,不管进程是否真在干活。比如一个 Python Web 服务 gunicorn 进程活着,但主线程卡在数据库连接上,systemd 完全无感。
实操建议:
- 优先用
WatchdogSec=30+ 应用层主动发systemd-notify --watchdog,这是最可靠的方式;没改代码能力就退而求其次 - 用
ExecStartPost=/bin/sh -c 'sleep 2 && curl -f http://127.0.0.1:8000/health || exit 1'做简单探活,失败则整个启动视为失败,触发Restart=逻辑 - 避免用
ExecStartPre做健康检查,它只在启动前跑一次,无法覆盖运行中僵死场景 - 注意
curl -f会把 HTTP 4xx/5xx 当错误,要确保 /health 接口真正返回 200 才算活
服务恢复后依赖项(如 DB、redis)还没就绪,硬重启反而雪崩?用 BindsTo= 和 After= 控制依赖顺序
单纯设 Restart=always 可能导致服务在 mysql 还没起来时反复尝试连接,打满连接数或触发限流,形成连锁故障。
实操建议:
-
After=mysqld.service只控制启动顺序,不阻止你的服务在 mysqld 崩溃后单独重启——这正是问题所在 - 必须加
BindsTo=mysqld.service:一旦 mysqld 停止,你的服务会被 systemd 自动 stop;再配合WantedBy=multi-user.target,就能保证两者生命周期绑定 - 如果依赖的是网络服务(如远程 Redis),
BindsTo不适用,改用ExecStartPre=/usr/bin/wait-for-it.sh redis:6379 -t 30 --类脚本做启动前阻塞等待 - 所有依赖项都得是 systemd 管理的服务,裸进程或 docker run 启的容器无法被
BindsTo跟踪
日志里看到 restart 成功,但业务请求仍 502?检查 reverse proxy 的 upstream 健康检查间隔
nginx 或 haproxy 默认不会实时感知后端 systemd 服务的重启过程,可能还在往旧 worker 进程转发请求,或缓存了已失效的 upstream 连接。
实操建议:
- nginx 中确认
proxy_next_upstream Error timeout http_502;已启用,并调小proxy_next_upstream_tries 2; - haproxy 需显式配置
option httpchk GET /health+http-check expect status 200,且inter 5s(默认 2000ms 太长) - 不要依赖
max_fails=1就以为够灵敏——它只统计连接拒绝,不包括应用返回 502;必须配合 httpchk 才能真正检测业务层存活 - systemd 服务重启后,第一个请求仍可能失败(冷启动耗时),前端需容忍短暂 5xx,服务端日志里
systemctl status <service></service>和journalctl -u <service> -n 20</service>要交叉比对时间戳
真正难的不是加几行 Restart=,而是让整个链路里的每个环节——从进程管理、依赖协调、健康反馈到流量调度——都对「瞬时故障」有明确、可验证的响应边界。漏掉任意一环,自动化恢复就只是看起来在动。