Docker 环境下 Nginx 502/500 错误的排查与解决
DNS 缓存与 rewrite 变量顺序问题
January 8, 2026·2 min read·Yimin
#Docker#Nginx#运维#502#500
容器重启后网站挂了,nginx 返回 502。问题不在容器,而在 DNS 缓存。
问题现象
某天发现网站无法访问,nginx 返回 502 Bad Gateway。
检查发现:
- 后端容器正常运行 ✅
- 从 nginx 容器内部
curl backend:3000返回 200 ✅ - 但外部请求仍然 502 ❌
问题根因
查看 nginx 错误日志:
connect() failed (111: Connection refused)
upstream: "http://172.18.0.x:3000/"
但后端容器当前的 IP 已经变了。
原因:Docker 容器重启后会重新分配 IP 地址。nginx 在启动时解析 DNS 并缓存结果,之后不再更新。
容器启动顺序决定 IP:
- 第 1 个启动 → .2
- 第 2 个启动 → .3
- ...
后端容器重启后,原来的 IP 可能被其他容器占用
nginx 还在用缓存的旧 IP → 502
解决方案
在 nginx.conf 中配置 Docker 内置 DNS:
http {
resolver 127.0.0.11 valid=30s ipv6=off;
# ...
}
然后在 proxy_pass 中使用变量而不是直接写服务名:
# ❌ 错误:启动时解析,缓存 IP
location / {
proxy_pass http://backend:3000;
}
# ✅ 正确:使用变量,启用动态解析
location / {
set $upstream backend:3000;
proxy_pass http://$upstream;
}
关键点:
resolver 127.0.0.11— Docker 内置 DNS 服务器valid=30s— 每 30 秒重新解析一次- 使用
set $upstream— 触发动态解析
注意:与 rewrite 配合的坑
如果你的配置中使用了 rewrite,要注意 set 指令的位置:
# ❌ 错误:set 在 rewrite 后面 → 500 Internal Server Error
location /api/service/ {
rewrite ^/api/service(/.*)$ $1 break;
set $upstream backend:3000;
proxy_pass http://$upstream;
}
# ✅ 正确:set 在 rewrite 前面
location /api/service/ {
set $upstream backend:3000;
rewrite ^/api/service(/.*)$ $1 break;
proxy_pass http://$upstream;
}
错误日志会显示 invalid URL prefix in "http://" — 这是因为 rewrite ... break 后,变量解析出现问题。
性能影响
几乎没有:
- Docker DNS 在本地运行,延迟微秒级
- 每 30 秒才解析一次,不是每个请求都解析
- 这是 Docker 官方推荐的做法
总结
| 问题 | 错误码 | 原因 | 解决 |
|---|---|---|---|
| 容器重启后挂了 | 502 | nginx 缓存旧 IP | 使用变量 + resolver |
| 变量 + rewrite 报错 | 500 | set 在 rewrite 后面 | 把 set 移到 rewrite 前面 |
两个小坑,排查起来都挺费时间。希望这篇文章能帮你节省一些。