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 官方推荐的做法

总结

问题错误码原因解决
容器重启后挂了502nginx 缓存旧 IP使用变量 + resolver
变量 + rewrite 报错500set 在 rewrite 后面把 set 移到 rewrite 前面

两个小坑,排查起来都挺费时间。希望这篇文章能帮你节省一些。


参考