Docker遇到502 Bad Gateway問題的排查與解決方法
最近在部署一個內(nèi)容服務(wù)時,遇到線上 502 Bad Gateway。這次故障非常典型:表面現(xiàn)象全部指向 Nginx 和 Docker 網(wǎng)絡(luò),最終根因卻在應(yīng)用代碼里的一把鎖。完整復(fù)盤如下。
一、項目背景
本次部署結(jié)構(gòu)很常見:
- 業(yè)務(wù)服務(wù)容器:
story-service(FastAPI + Uvicorn) - 網(wǎng)關(guān)容器:
nginx-gateway(反向代理) - 兩者都在同一臺服務(wù)器的 Docker 環(huán)境中
- 設(shè)計上通過同一個 Docker 網(wǎng)絡(luò)互通,Nginx 用容器名反代業(yè)務(wù)服務(wù)
部署完成后,用戶訪問接口返回 502,開始排查。
二、故障現(xiàn)象
1)宿主機(jī)探活失敗
curl -i http://127.0.0.1:8070/health
返回:
curl: (56) Recv failure: Connection reset by peer
2)容器內(nèi)探活也失敗
容器是精簡鏡像,沒有 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
結(jié)果:
http://127.0.0.1:8080/health->Connection refusedhttp://127.0.0.1:8070/health->Connection refused
這一步非常關(guān)鍵:容器內(nèi)回環(huán)都連不上,說明不是 Nginx 轉(zhuǎn)發(fā)層的問題,應(yīng)用根本沒監(jiān)聽端口。
三、排查命令與結(jié)論
1)查看容器狀態(tài)
docker ps -a --filter "name=story-service"
用于判斷容器是否在異常重啟或退出。
2)查看實(shí)時日志
docker logs -f story-service
日志顯示數(shù)據(jù)庫初始化成功,但啟動流程停在“確保管理員賬號”階段,沒有出現(xiàn) Uvicorn running on ... 監(jiān)聽日志。
3)核對端口映射
docker port story-service
確認(rèn)了映射關(guān)系,但由于容器內(nèi)端口本身未監(jiān)聽,映射正確也無效。
4)核對啟動參數(shù)
docker inspect story-service --format '{{.Path}} {{range .Args}}{{.}} {{end}}'
用于確認(rèn) uvicorn 啟動參數(shù)和監(jiān)聽端口是否一致。
5)核對網(wǎng)絡(luò)連通基礎(chǔ)
docker network inspect mynet
確認(rèn) Nginx 與業(yè)務(wù)容器都在同一網(wǎng)絡(luò)中,排除基礎(chǔ)網(wǎng)絡(luò)隔離問題。
四、根因定位
根因在數(shù)據(jù)庫初始化鏈路的鎖重入:
- 應(yīng)用啟動時執(zhí)行
ensure_admin_user() ensure_admin_user()內(nèi)部可能調(diào)用create_user()create_user()又會走到get_conn()- 多個函數(shù)嘗試獲取同一把鎖
- 代碼使用的是
threading.Lock()(不可重入)
結(jié)果是同線程重復(fù)加鎖,啟動階段發(fā)生死鎖。進(jìn)程沒有直接崩潰,但 startup 卡住,服務(wù)端口始終不監(jiān)聽,最終表現(xiàn)為 502。
五、修復(fù)方式
將數(shù)據(jù)庫模塊鎖改為可重入鎖:
threading.Lock()改為threading.RLock()
修復(fù)后重新部署,日志出現(xiàn)正常監(jiān)聽信息,容器內(nèi) health 可達(dá),Nginx 502 消失,服務(wù)恢復(fù)。
六、兩個排障細(xì)節(jié)
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是常態(tài)
不必先改鏡像,直接用 Python 內(nèi)置庫做 HTTP 探活即可完成核心定位。
七、復(fù)盤總結(jié)
這次故障最容易誤判為“網(wǎng)絡(luò)問題”,但真正根因是“應(yīng)用啟動死鎖”。
有效的排障順序是:
- 先看應(yīng)用是否真正監(jiān)聽端口(容器內(nèi)回環(huán)探活)
- 再看端口映射
- 最后看 Nginx 與 Docker 網(wǎng)絡(luò)
當(dāng) 502 出現(xiàn)時,先證明“后端服務(wù)真的活著”,再談代理層配置,效率最高。
到此這篇關(guān)于Docker遇到502問題的排查與解決方法的文章就介紹到這了,更多相關(guān)Docker遇到502問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
docker中使用mysql數(shù)據(jù)庫實(shí)現(xiàn)局域網(wǎng)訪問
在本篇內(nèi)容里小編給大家整理了關(guān)于docker中使用mysql數(shù)據(jù)庫實(shí)現(xiàn)局域網(wǎng)訪問相關(guān)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。2020-03-03
Docker創(chuàng)建運(yùn)行多個mysql容器的方法示例
這篇文章主要介紹了Docker創(chuàng)建運(yùn)行多個mysql容器,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10
用docker部署RabbitMQ環(huán)境的詳細(xì)介紹
這篇文章主要介紹了用docker部署RabbitMQ環(huán)境,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07
Docker使用Dockerfile來創(chuàng)建鏡像
本篇文章主要介紹了Docker使用Dockerfile來創(chuàng)建鏡像,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04
Docker 數(shù)據(jù)存儲路徑之解決默認(rèn)docker路徑位置磁盤空間不足的問題
本文指導(dǎo)如何通過修改daemon.json將Docker數(shù)據(jù)目錄從/var/lib/docker遷移至/mnt/disk/docker,保留所有鏡像、容器等數(shù)據(jù),無需調(diào)整docker.service,步驟包括停止服務(wù)、遷移數(shù)據(jù)、修改配置及重啟驗(yàn)證,需要的朋友跟隨小編一起學(xué)習(xí)吧2025-08-08
docker的overlay2中存的都是什么及如何清理/var/lib/docker/overlay2
docke roverlay2是Docker中的存儲驅(qū)動之一,用于管理鏡像和容器層的數(shù)據(jù),這篇文章主要給大家介紹了關(guān)于docker的overlay2中存的都是什么及如何清理/var/lib/docker/overlay2的相關(guān)資料,需要的朋友可以參考下2024-04-04

