Linux 服务自动化故障恢复策略

1次阅读

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

Linux 服务自动化故障恢复策略

systemd 服务崩溃后自动重启不生效?检查 Restart= 和 RestartSec= 配置

很多服务加了 Restart=always 却没真正重启,根本原因是 systemd 默认只对「非正常退出」(如 crash、kill -9)触发重启,而 Restart=always 确实能覆盖正常退出(exit code 0),但前提是服务不能被 systemd 认定为「成功启动完成」——否则它会跳过后续重启逻辑。

实操建议:

  • Restart=on-failure 更贴近「故障恢复」本意:只在非 0 退出、超时、被信号终止时重启;on-aborton-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=,而是让整个链路里的每个环节——从进程管理、依赖协调、健康反馈到流量调度——都对「瞬时故障」有明确、可验证的响应边界。漏掉任意一环,自动化恢复就只是看起来在动。

text=ZqhQzanResources