W weiserv
← 返回博客

Django+uWSGI 重启 502:双 master 与取 PID 失灵排障 SOP

背景

给生产站(Django + uWSGI 生产部署)打 API 补丁后需要重启 uWSGI 使代码生效。
第一版补丁脚本因 r''' 里又嵌套了 """ 文档字符串,直接 SyntaxError 失败;
随后重启命令又误用了 ./venv/bin/uwsgi(该文件不存在),结果旧实例被 kill、新实例起不来,站点直接 502。

一、现象:uwsgi 重启 502 的典型现场

补丁打完、执行重启命令,站点返回 502。登录服务器排查,发现 ps同时存在两个 uWSGI master

2434688 ?  12:00:00 uwsgi   # 旧实例(仍在服务旧代码),日志 /tmp/uwsgi_new.log
2607020 ?   3:00:00 uwsgi   # 刚误起的实例,日志 /var/log/uwsgi/weiserv-new.log

两个 master 同时去绑 weiserv.sock → socket 争抢 / 旧代码仍在服务 → 对外 502。

二、根因:双 master 并存 + 取 PID 命令失灵

坑 1:uwsgi reload 只重载 pidfile 记录的那个实例。
uwsgi --reload pidfile 读 pidfile 里的 PID 去 reload,但真正在对外服务的旧 master 并不在那个 pidfile 里
(它是更早的另一次启动留下的),于是模板/代码改动「看起来没生效」,容易误判为缓存问题。

坑 2:常规取 PID 命令在本机失灵。

$ pgrep -x uwsgi        # 时而返回空(本机 PATH 不稳定)
$ ps -C uwsgi           # 过滤失效,返回的是全部进程而非 uwsgi

两个「标准」命令都不可靠,手动 kill 极易漏杀,残留 master 继续占着 socket。

三、干净重启 SOP(uwsgi 杀进程 重启 的正确姿势)

唯一可靠的取 PID 方式是按 comm 精确匹配,避开 pgrep / ps -C 的坑:

# 1) 可靠取全部 uwsgi PID(按 comm 精确匹配)
PIDS=$(ps -eo pid,comm | awk '$2=="uwsgi"{print $1}')

# 2) 强杀全部残留(双 master 必须全清,否则 socket 冲突 / 旧代码仍在服务)
kill -9 $PIDS

# 3) 删残留 socket(属组 [部署用户]:www-data 才会被 nginx 访问)
rm -f /home/[部署用户]/wwwroot/weiserv/weiserv.sock

# 4) 单实例重启——用 PATH 上的 uwsgi,勿用 ./venv/bin/uwsgi
cd /home/[部署用户]/wwwroot/weiserv
/usr/local/bin/uwsgi --ini uwsgi_conf.ini --chown [部署用户]:www-data \
  --daemonize /var/log/uwsgi/weiserv-new.log

注:uwsgi_conf.inidaemonize禁用状态,日志路径由启动命令行的 --daemonize 指定(即上行的 /var/log/uwsgi/weiserv-new.log)。「现象」段旧实例的 /tmp/uwsgi_new.log 为更早一次手工启动遗留,非 ini 默认。

关键点:永远用 which uwsgi 确认的那个二进制/usr/local/bin/uwsgi),
而不是想当然的 ./venv/bin/uwsgi。venv 里通常没有 uwsgi 可执行文件。

四、验证

# 进程只剩单 master + workers
$ ps -eo pid,ppid,comm | awk '$3=="uwsgi"'
2609296 ? uwsgi     # 单 master

$ ls -l /home/[部署用户]/wwwroot/weiserv/weiserv.sock
srw-rw-r-- 1 [部署用户] www-data ... weiserv.sock

# 站点恢复
$ curl -s -o /dev/null -w '%{http_code}' https://weiserv.com/
200

五、避坑清单

  1. 补丁脚本别在 r''' 里再嵌 """——先本地 python -c "compile(...)"py_compile 过一遍语法。
  2. 重启前先 which uwsgi,确认二进制路径,别用 ./venv/bin/uwsgi
  3. 取 PID 用 ps -eo pid,comm | awk '$2=="uwsgi"',别信 pgrep -x / ps -C
  4. 双 master 必须 kill -9 全清 + 删 sock,再单实例重启,否则 socket 冲突。
  5. 重启后 curl 验 200 再离场,别只看进程起来了。
广告位占位 · post-inline