【总结】Docker 容器重建后 Nginx 502 问题排查与解决

Docker 容器重建后 Nginx 502 问题排查与解决

问题现象

在 Docker 环境中部署了多个服务,使用 Nginx 作为反向代理。当通过拉取新镜像并用 Docker-Compose 重新创建前端容器后,访问页面出现 502 Bad Gateway 错误。

Nginx 容器本身运行正常,未重启、未修改配置,只是下游的前端容器被重建了,就出现了 502。

环境信息

  • 部署方式:Docker Compose
  • Nginx 容器名称:ai-xx-nginx
  • Nginx 配置中通过容器名代理转发,例如:
nginx 复制代码
location / {
    proxy_pass http://ai-xx-front/;
}

问题分析

根本原因:Nginx DNS 缓存

Nginx 在处理 proxy_pass 时,如果直接写的是域名(包括 Docker 容器名),只在启动或重载时解析一次 DNS,之后将解析结果缓存起来。

整个流程如下:
ai-xx-front 新容器 用户 ai-xx-front 旧容器 Docker DNS (127.0.0.11) Nginx 容器 ai-xx-front 新容器 用户 ai-xx-front 旧容器 Docker DNS (127.0.0.11) Nginx 容器 #mermaid-svg-I2VPeOpb785ZRNkJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I2VPeOpb785ZRNkJ .error-icon{fill:#552222;}#mermaid-svg-I2VPeOpb785ZRNkJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I2VPeOpb785ZRNkJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I2VPeOpb785ZRNkJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I2VPeOpb785ZRNkJ .marker.cross{stroke:#333333;}#mermaid-svg-I2VPeOpb785ZRNkJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I2VPeOpb785ZRNkJ p{margin:0;}#mermaid-svg-I2VPeOpb785ZRNkJ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-I2VPeOpb785ZRNkJ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-I2VPeOpb785ZRNkJ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-I2VPeOpb785ZRNkJ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-I2VPeOpb785ZRNkJ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-I2VPeOpb785ZRNkJ .sequenceNumber{fill:white;}#mermaid-svg-I2VPeOpb785ZRNkJ #sequencenumber{fill:#333;}#mermaid-svg-I2VPeOpb785ZRNkJ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-I2VPeOpb785ZRNkJ .messageText{fill:#333;stroke:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-I2VPeOpb785ZRNkJ .labelText,#mermaid-svg-I2VPeOpb785ZRNkJ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .loopText,#mermaid-svg-I2VPeOpb785ZRNkJ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-I2VPeOpb785ZRNkJ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-I2VPeOpb785ZRNkJ .noteText,#mermaid-svg-I2VPeOpb785ZRNkJ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-I2VPeOpb785ZRNkJ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-I2VPeOpb785ZRNkJ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-I2VPeOpb785ZRNkJ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-I2VPeOpb785ZRNkJ .actorPopupMenu{position:absolute;}#mermaid-svg-I2VPeOpb785ZRNkJ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-I2VPeOpb785ZRNkJ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-I2VPeOpb785ZRNkJ .actor-man circle,#mermaid-svg-I2VPeOpb785ZRNkJ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-I2VPeOpb785ZRNkJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段一:Nginx 启动,解析并缓存 DNS 缓存 ai-xx-front → 172.18.0.5(永不过期) 阶段二:Docker-Compose 重建前端容器 Nginx 未重启,仍使用缓存旧 IP 阶段三:用户再次访问,触发 502 阶段四:修复后(resolver + 变量) resolver 127.0.0.11 valid=5sproxy_pass $upstream(变量方式) 解析 ai-xx-front返回 IP: 172.18.0.5访问页面转发请求到 172.18.0.5200 OK正常返回页面旧容器销毁,IP 172.18.0.5 失效新容器启动,注册新 IP 172.18.0.8访问页面转发请求到 172.18.0.5(缓存)连接失败,容器已不存在502 Bad Gateway访问页面重新解析 ai-xx-front(缓存已过期)返回新 IP: 172.18.0.8转发请求到 172.18.0.8200 OK正常返回页面

关键点: Docker 容器重建后 IP 会变化,但 Nginx 仍然使用旧 IP 转发请求。

解决方案

需要同时做两处修改,缺一不可:

1. 配置 DNS 解析器

server 块顶部添加:

nginx 复制代码
resolver 127.0.0.11 valid=5s ipv6=off;
  • 127.0.0.11:Docker 内置 DNS 服务器的固定地址
  • valid=5s:DNS 缓存 5 秒过期,确保容器重建后能快速感知新 IP
  • ipv6=off:关闭 IPv6,避免不必要的解析

2. 使用变量触发运行时解析

proxy_pass 从直接写域名改为通过变量引用:

nginx 复制代码
# 修改前
location / {
    proxy_pass http://ai-xx-front/;
}

# 修改后
location / {
    set $upstream http://ai-xx-front;
    proxy_pass $upstream;
}

为什么两者必须同时修改?

只加 resolver 只用变量 两者都加
无效。Nginx 对直接写域名的 proxy_pass 只在启动时解析一次,不会触发 resolver 报错 no resolver defined to resolve ai-xx-front 正常工作
  • 变量是触发运行时 DNS 解析的开关
  • resolver 告诉 Nginx 用哪个 DNS 服务器、缓存多久

完整配置示例

nginx 复制代码
server {
    listen       443;
    server_name  localhost;

    # 配置 Docker 内部 DNS,缓存 5 秒
    resolver 127.0.0.11 valid=5s ipv6=off;

    ssl on;
    ssl_certificate /nginx/cert/server.crt;
    ssl_certificate_key /nginx/cert/server.key;

    # 前端服务
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        add_header Access-Control-Allow-Origin *;
        set $upstream http://ai-xx-front;
        proxy_pass $upstream;
    }

    # API 网关(带路径重写)
    location ^~/api/ {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        add_header Access-Control-Allow-Origin *;
        set $upstream http://gateway:30080;
        rewrite ^/api/(.*)$ /$1 break;
        proxy_pass $upstream;
    }
}

注意 :当 proxy_pass 使用变量后,Nginx 不再自动处理 URI 路径替换。如果原始配置中 proxy_pass 的路径替换了 location 前缀(如 proxy_pass http://gateway:30080/; 会去掉 /api/ 前缀),需要通过 rewrite 手动实现相同的路径重写逻辑。

微前端子应用的代理方式前后对比

修改前:

nginx 复制代码
    location /h-xx-system {
        add_header 'Access-Control-Allow-Origin' '*';
        proxy_pass http://h-xx-system/;
    }
    
    location /system-child {
        add_header 'Access-Control-Allow-Origin' '*';
        proxy_pass http://h-xx-system/system-child;
    }

修改后:

nginx 复制代码
    location /h-xx-system {
        add_header 'Access-Control-Allow-Origin' '*';
        set $upstream http://h-xx-system;
        rewrite ^/h-xx-system/?(.*)$ /$1 break;
        proxy_pass $upstream;
    }
    
    location /system-child {
        add_header 'Access-Control-Allow-Origin' '*';
        set $upstream http://h-xx-system;
        rewrite ^/system-child(.*)$ /system-child$1 break;
        proxy_pass $upstream;
    }

性能影响

valid=5s 是否意味着每次请求都要解析 DNS?

不会。 DNS 解析结果会被缓存 5 秒,在这 5 秒内的所有请求直接使用缓存结果:

复制代码
时间轴:
0s  ──> 解析 DNS,缓存 5 秒
1s  ──> 请求到达,使用缓存 ✓(不解析)
2s  ──> 请求到达,使用缓存 ✓(不解析)
3s  ──> 请求到达,使用缓存 ✓(不解析)
5s  ──> 缓存过期
6s  ──> 请求到达,重新解析 DNS,再缓存 5 秒

最多每 5 秒才做一次 DNS 查询,对性能影响可以忽略不计。

验证方法

修改配置后,按以下步骤验证:

bash 复制代码
# 1. 检查 Nginx 配置语法
nginx -t

# 2. 重新加载配置
nginx -s reload

# 3. 重建下游容器
docker-compose up -d --force-recreate ai-xx-front

# 4. 立即访问页面,确认不再出现 502
curl -k https://localhost/

总结

项目 说明
问题现象 Docker 容器重建后 Nginx 返回 502
根本原因 Nginx 启动时缓存 DNS 解析结果,容器重建后 IP 变化但 Nginx 未感知
解决方案 配置 resolver + proxy_pass 使用变量
性能影响 几乎无影响,DNS 缓存 5 秒内复用
相关推荐
qq_356408662 小时前
Kubernetes Rook-Ceph 高可用存储部署文档
ceph·容器·kubernetes
Plastic garden2 小时前
Docker(3)Docker 镜像 & Dockerfile
运维·docker·容器
“码”力全开2 小时前
解耦异构算力与多协议接入:基于Docker与源码交付的开源GB28181/RTSP边缘计算AI视频管理平台架构深度解析
人工智能·docker·开源
m0_740859622 小时前
Docker安装常见数据库命令汇总(2026)
数据库·docker·容器
taiguisheng2 小时前
Docker中编译esp32
windows·docker·esp32
IT策士2 小时前
第16篇 实战:用 Docker Compose 编排 WordPress 与 MySQL
mysql·docker·容器
“码”力全开2 小时前
解耦流媒体与AI推理:基于Docker与GB28181/RTSP的边缘计算中台,全量源码交付如何帮集成商节省95%开发成本?
人工智能·docker·边缘计算
Plastic garden2 小时前
Docker(2)数据挂载
运维·docker·容器
Plastic garden2 小时前
Docker(4) Compose
运维·docker·容器