920 字
5 分钟
Nginx 在 Docker/K8s 中频繁 502 的根本原因与解决方案
起因
在 Docker 或 Kubernetes 环境中,经常遇到这样的情况:
- Nginx 突然报 502 Bad Gateway
- 后端服务明明正常运行,端口也通
- 重启一下 Nginx 就恢复正常了
- 过几天依赖的服务重启后,又 502 了
这个问题特别容易出现在以下时机:
- 机器重启时
- 依赖的服务自动重启或更新后
- K8s Pod 漂移或重新调度后
- docker compose 重启后
根本原因
Nginx 开源版默认的 DNS 解析机制存在缺陷:
IMPORTANT域名只在启动或 reload 时解析一次。
Nginx 启动时会查询后端域名的 IP 地址并缓存,之后就一直使用这个缓存的 IP,除非手动重启或 reload。
具体流程:
- Nginx 启动时解析域名
my-service→ 得到 IP10.0.0.1 - Nginx 将请求发往
10.0.0.1 - 后端容器重启,IP 变为
10.0.0.5 - Nginx 仍然往旧的
10.0.0.1发送请求 - 连接失败,返回 502
影响场景
这个问题在容器化环境中尤为突出:
Docker 环境
upstream backend { server my-service:8080; # my-service 是 Docker 内部域名}当 my-service 容器重启后,Docker 会重新分配 IP,但 Nginx 不知道,继续使用旧 IP。
Kubernetes 环境
upstream backend { server my-app.default.svc.cluster.local:8080; # K8s 内部域名}Pod 重建、滚动更新、节点调度都会导致 IP 变化,Nginx 无法感知这些变化。
典型场景
- 依赖服务崩溃后自动重启
- CI/CD 发版更新
- K8s 节点维护导致 Pod 迁移
- 容器资源限制触发重启
- Nginx重启比其他服务重启快,导致Nginx先恢复正常,后端服务后恢复正常
解决方案
从 Nginx 1.27.3 开始,开源版正式支持 resolve 参数,可以实现动态 DNS 解析。
NOTE这个功能是 Nginx 从商业版下放到开源版 1.27.3 的,发布于 2024 年 11 月。 Docker Nginx 镜像升级到 1.27.3+ 即可使用。
http { # 配置 DNS 服务器(也可以配置在 upstream 、server 块中) # Docker 内部 DNS 127.0.0.11 # K8s CoreDNS: 一般是 10.96.0.10,或通过 /etc/resolv.conf 获取 # valid=5s: 每 5 秒重新解析一次 # ipv6=off: 禁用 IPv6 解析(可选,容器环境通常只用 IPv4) resolver 127.0.0.11 valid=5s ipv6=off;
upstream my_backend { # 必须配置共享内存区,用于存储动态解析的服务器列表 zone backend_zone 64k;
# 添加 resolve 参数,启用动态 DNS 解析 # Nginx 会定期重新解析域名,IP 变化后自动更新 server my-service:8080 resolve; }
server { listen 80;
location / { proxy_pass http://my_backend; } }}关键配置说明:
-
resolver:指定 DNS 服务器地址- Docker:
127.0.0.11(Docker 内置 DNS 服务器,用于用户自定义网络) - Kubernetes: 默认通常是
10.96.0.10(kube-dns/CoreDNS 服务),具体地址请查看容器内的/etc/resolv.conf - 配置位置: 可以放在
http、server、location或upstream(1.27.3+)块中,推荐放在http块中全局生效 - 优先级:遵循 Nginx 的”就近原则”,内层配置会覆盖外层配置
- 参数说明:
valid=5s: DNS 缓存有效期 5 秒,覆盖 DNS 响应中的 TTL 值,到期后重新解析ipv6=off: 禁用 IPv6 解析(可选参数),容器环境通常只需要 IPv4
- Docker:
-
zone:必须配置共享内存区- 用于存储动态解析的服务器列表
- 大小一般 64k 足够,多个 upstream 可以使用不同的 zone 名称
-
resolve:核心参数- 添加到
server指令后,启用动态 DNS 解析 - Nginx 会根据
valid时间定期重新解析域名 - IP 变化后自动更新,无需重启或 reload
- 添加到
-
注意事项:
- 必须同时配置
resolver、zone和resolve三者 resolve参数只能用于域名,不能用于 IP 地址- 如果 DNS 服务器不可用,Nginx 会继续使用缓存的 IP
- 必须同时配置
参考
Nginx 在 Docker/K8s 中频繁 502 的根本原因与解决方案
https://www.jianyun.run/posts/nginx-dns-resolve/