首页 > 服务器    日期:2026-07-14 / 浏览

最近在部署一个内容服务时,遇到线上 502 Bad Gateway。这次故障非常典型:表面现象全部指向 Nginx 和 Docker 网络,最终根因却在应用代码里的一把锁。完整复盘如下。

一、项目背景

本次部署结构很常见:

  • 业务服务容器:story-service(FastAPI + Uvicorn)
  • 网关容器:nginx-gateway(反向代理)
  • 两者都在同一台服务器的 Docker 环境中
  • 设计上通过同一个 Docker 网络互通,Nginx 用容器名反代业务服务

部署完成后,用户访问接口返回 502,开始排查。

二、故障现象

1)宿主机探活失败

curl -i http://127.0.0.1:8070/health

返回:

curl: (56) Recv failure: Connection reset by peer

2)容器内探活也失败

容器是精简镜像,没有 curl/ss,改用 Python 探活:

docker exec -i story-service python - <<'PY'
import urllib.request
for p in (8080, 8070):
    url=f"http://127.0.0.1:{p}/health"
    try:
        with urllib.request.urlopen(url, timeout=3) as r:
            print(url, "OK", r.status, r.read().decode("utf-8", "ignore"))
    except Exception as e:
        print(url, "FAIL", repr(e))
PY

结果:

  • http://127.0.0.1:8080/health -> Connection refused
  • http://127.0.0.1:8070/health -> Connection refused

这一步非常关键:容器内回环都连不上,说明不是 Nginx 转发层的问题,应用根本没监听端口。

三、排查命令与结论

1)查看容器状态

docker ps -a --filter "name=story-service"

用于判断容器是否在异常重启或退出。

2)查看实时日志

docker logs -f story-service

日志显示数据库初始化成功,但启动流程停在“确保管理员账号”阶段,没有出现 Uvicorn running on ... 监听日志。

3)核对端口映射

docker port story-service

确认了映射关系,但由于容器内端口本身未监听,映射正确也无效。

4)核对启动参数

docker inspect story-service --format '{{.Path}} {{range .Args}}{{.}} {{end}}'

用于确认 uvicorn 启动参数和监听端口是否一致。

5)核对网络连通基础

docker network inspect mynet

确认 Nginx 与业务容器都在同一网络中,排除基础网络隔离问题。

四、根因定位

根因在数据库初始化链路的锁重入:

  • 应用启动时执行 ensure_admin_user()
  • ensure_admin_user() 内部可能调用 create_user()
  • create_user() 又会走到 get_conn()
  • 多个函数尝试获取同一把锁
  • 代码使用的是 threading.Lock()(不可重入)

结果是同线程重复加锁,启动阶段发生死锁。进程没有直接崩溃,但 startup 卡住,服务端口始终不监听,最终表现为 502。

五、修复方式

将数据库模块锁改为可重入锁:

  • threading.Lock() 改为 threading.RLock()

修复后重新部署,日志出现正常监听信息,容器内 health 可达,Nginx 502 消失,服务恢复。

六、两个排障细节

1)docker exec+ heredoc 不要用-t

错误写法会报:

the input device is not a TTY

正确写法是 -i

docker exec -i story-service python - <<'PY'
...
PY

2)python:slim没有curl/ss是常态

不必先改镜像,直接用 Python 内置库做 HTTP 探活即可完成核心定位。

七、复盘总结

这次故障最容易误判为“网络问题”,但真正根因是“应用启动死锁”。
有效的排障顺序是:

  1. 先看应用是否真正监听端口(容器内回环探活)
  2. 再看端口映射
  3. 最后看 Nginx 与 Docker 网络

502 出现时,先证明“后端服务真的活着”,再谈代理层配置,效率最高。

觉得上面的内容有用吗?快来点个赞吧!

点赞() 我要打赏

温馨提示 : 本站内容来自会员投稿以及互联网,所有源码及教程均为作者总结编辑,请大家在使用过程中提前做好备份,以免发生无法预知的错误,源码类教程请勿直接用于生产环境!

 可能感兴趣的文章