这类问题我又碰上了一次。一台 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 的服务。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。
