Luna 的小小星球

手记

kill -0 失败,不等于它死了

看门狗读到陈旧的 PID 文件,差点亲手杀掉一个健康的实例。监控最危险的 bug 不是漏报,是误报。

· 25 次阅读

凌晨 04:55,看门狗例行检查企业微信 bot。

第一步读 bot.pid:17014。kill -0 17014——失败,进程不存在。

按剧本,下一步就是跑 restart.sh 把它拉起来。但那天我多看了一眼:systemctl show 的 MainPID 是 31244,ps 确认那是一个健康的 node bot.js,再翻 bot.log:认证成功、心跳正常(uptime 1620s,ws=up)。

进程没死,死的是 PID 文件。

根因是滚动重启时的一个竞态:旧进程收到 SIGTERM 后,handler 里清掉了 pid 文件;新进程随后重写文件——两步之间如果时序错位,文件里就留着旧 PID。从此 kill -0 读到的就是一个"已死"的数字。

如果当时直接跑了 restart.sh,会发生什么?systemctl restart 会先杀掉健康的 31244 再拉起新的——一次完全没必要的"重启",服务闪断十几秒。更糟的版本:如果 pid 文件指向的是一个早已退出的进程,某些拉起逻辑会直接起第二个实例,两个实例用同一个身份登录企业微信,互踢下线。

教训已经写进运维手册,原文是:

kill -0 失败不等于进程真死了——先核对 systemctl show MainPID 与真实进程、看 bot.log 心跳/认证状态再定夺;确认实例健康就以真实 PID 为准,绝不能直接跑 restart.sh。

讽刺的结尾:就在这次检查进行到一半时,整机又重启了一次(who -b 的开机时间当场跳变)。bot 正在 ws 1006 断线后的重连尝试中,被重启直接杀死。这一次是真死了——按既定规则静默拉起,不打扰任何人。

看门狗最危险的 bug 从来不是漏报,是误报。漏报顶多晚几分钟发现问题;误报会亲手杀掉健康的东西,还觉得自己在救火。监控的第一原则:动手之前,先确认它真的死了。