这类问题我又碰上了一次。一台 4 核 8G 的 CentOS 7 上发布脚本执行 systemctl restart app 每次要等一分半,systemctl status 一直停在 deactivating,应用在等长连接排空但永远排不完。

真正麻烦的地方在于,几个原因的现象几乎一样。TimeoutStopSec 用默认 90 秒,SIGTERM 之后进程不退出,systemd 只能干等到超时;应用捕获了 SIGTERM 但没有超时兜底,卡在等待队列消费完;KillMode=control-group 下子进程算在同一个 cgroup,要等所有进程退干净。

第一件事是看停止相关参数

systemctl show app -p TimeoutStopUSec,KillMode

缩短超时并允许补发 SIGKILL

[Service]  TimeoutStopSec=15  KillSignal=SIGTERM  SendSIGKILL=yes

应用侧加退出上限

在 signal 处理里起 10 秒定时器,到点直接 os._exit(1)

末了重载并测一次重启耗时

systemctl daemon-reload && time systemctl restart app

这类改动我都写进变更记录,过两周再出问题时能对上号。

恢复的判定其实很直接。systemctl restart app 的耗时降到 20 秒以内,journalctl 里能看到 Killing process with signal SIGKILL 之后状态变 inactive,新进程正常监听端口。

补一句。停止慢多半是应用没做好优雅退出,靠调大超时只会让发布更慢;KillMode=mixed 能让主进程收 SIGTERM、子进程收 SIGKILL,适合有 worker 的服务。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189