我不会起名字322 · 后端 / 算法 / 数据库
文章目录
- [K8s Ingress 返回 502 但后端日志正常:4 类上游断连与排查链](#K8s Ingress 返回 502 但后端日志正常:4 类上游断连与排查链)
K8s Ingress 返回 502 但后端日志正常:4 类上游断连与排查链
昨晚用户群炸了:偶发 502。我第一件事是翻业务 Pod 的日志------一条错误都没有。
这就是 Ingress 502 最反直觉的地方:502 是网关说的,不是后端说的。它意味着"网关在转发时,后端连接出了问题",而这个问题可能发生在连接建立、复用、或关闭的任意一环,后端应用甚至不知道有请求来过。
这篇沿流量路径,拆 4 类上游断连,每类给"看哪里"的判据。
一、先搞清流量路径

502(Bad Gateway)由 Ingress Controller 返回,含义是"我从上游(Service 后面的 Pod)收到的响应无效或根本连不上"。所以排查的战场是 Ingress 到 Pod 这一段,不是客户端到 Ingress。
bash
# 定位是哪个 Ingress Controller 在报 502
kubectl get pods -n ingress-nginx
# 看它的错误日志(这是 502 的第一手证据)
kubectl logs -n ingress-nginx <controller-pod> --tail=200 | grep -E "502|upstream|connect"
二、第 1 类:keepalive 超时不匹配
这是最高频 的一类。Nginx 与上游之间用 keepalive 连接池复用连接,但两边超时时间不一致:
- Nginx 侧
keepalive_timeout:假设 75 秒; - 后端(Go 的
http.Server)IdleTimeout:假设 60 秒; - 结果:后端 60 秒没收到请求就把连接关了,但 Nginx 的连接池还以为这条连接可用;
- 下一个请求复用这条"僵尸连接" → 写失败 → 502。
判据 :nginx-ingress 日志里有 upstream prematurely closed connection 或 recv() failed。
nginx
# 关键配置(Ingress 的 ConfigMap)
upstream-keepalive-timeout: "60" # 要与后端 IdleTimeout 匹配
upstream-keepalive-requests: "100" # 单连接最多复用次数
keep-alive-requests: "1000"
go
// 后端 Go 服务,显式设置
srv := &http.Server{
Addr: ":8080",
IdleTimeout: 75 * time.Second, // > Nginx 的 keepalive_timeout
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
记忆口诀 :后端的空闲超时要 ≥ 网关的空闲超时,让网关先关。反过来就是僵尸连接。
三、第 2 类:Pod 正在下线但 readiness 还没摘
滚动更新时有个经典竞态:
- Pod 收到 SIGTERM,开始停止;
- 但 Service 的 endpoints 更新是异步的(kube-proxy 需要时间刷新 iptables 规则);
- 在这段窗口里,Ingress 仍会把新请求转发到"正在关闭"的 Pod;
- Pod 已经不再 accept,连接被拒 → 502。

修法 :加 preStop 钩子,让应用在收到 SIGTERM 后先睡一会儿再退出,给 endpoints 传播留时间:
yaml
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
terminationGracePeriodSeconds: 30
同时,应用应该先停止接受新连接 (srv.Shutdown),再处理完在途请求。
判据 :502 集中在滚动更新那几十秒内出现。
四、第 3 类:Service iptables 规则更新延迟
这一类和上一类相关但不同:即使是稳态,Service 的 iptables/IPVS 规则也可能有短暂的规则缺失或不一致。
- kube-proxy 以
syncPeriod(默认 30 秒)定期全量同步规则; - Pod 增删时,规则更新是增量的,可能短暂出现"指向已消失的 Pod"的规则。
判据:
bash
# 看 Service 的 endpoints 是否与实际 Pod 一致
kubectl get endpoints <svc> -o yaml | grep -A10 subsets
# 看 kube-proxy 的模式和同步周期
kubectl get cm kube-proxy -n kube-system -o yaml | grep -E "mode|syncPeriod"
在 Pod 频繁重建的场景(比如 CrashLoop),这类 502 会明显增多。
五、第 4 类:upstream 超时配置
如果后端处理慢,超过 Nginx 的 proxy_read_timeout,Nginx 会主动断开并返回 504 (注意是 504 不是 502)------但如果连接在建立阶段就超时,则是 502。
nginx
# Nginx 超时参数(Ingress 通过 annotation 或 ConfigMap 设置)
proxy_connect_timeout: "5" # 连后端超时 → 超时报 502
proxy_read_timeout: "60" # 读响应超时 → 超时报 504
proxy_send_timeout: "60" # 发请求超时
区分 502 和 504 是关键判据:
| 状态码 | 含义 | 发生阶段 |
|---|---|---|
| 502 | 上游返回无效响应 / 连不上 | 连接建立或响应无效 |
| 504 | 上游响应超时 | 已连上但读超时 |
所以看到 502,先别看"业务慢不慢",要看"连接建不建得上"。
六、排查顺序表
| 顺序 | 看什么 | 命令/位置 | 命中则 |
|---|---|---|---|
| 1 | Ingress Controller 错误日志 | kubectl logs -n ingress-nginx <pod> |
看具体错误文本 |
| 2 | 是否集中在滚动更新期 | 对比 502 时间与发布时间 | 是→第 2 类(preStop) |
| 3 | upstream prematurely closed |
日志关键词 | 是→第 1 类(keepalive 不匹配) |
| 4 | 502 还是 504 | 日志状态码 | 502=连接问题,504=慢 |
| 5 | endpoints 是否与 Pod 一致 | kubectl get endpoints |
不一致→第 3 类 |
| 6 | Nginx 超时配置 | ConfigMap | 过短→第 4 类 |
最小复现:用压测工具边打流量边触发滚动更新,观察 502 是否复现:
bash
# 一个终端持续打流量
while true; do curl -s -o /dev/null -w "%{http_code}\n" http://<ingress>/; sleep 0.1; done &
# 另一个终端触发滚动重启
kubectl rollout restart deployment/<name> -n <ns>
如果 502 只在重启瞬间冒出来,就是第 2 类。
一个可带走物 :preStop: sleep 10 + IdleTimeout ≥ keepalive_timeout 这两个配置,能挡掉大部分偶发 502。
七、总结
- 502 是网关报的,不是后端报的 ------ 后端日志正常恰恰是正常的;
- 4 类断连:keepalive 超时不匹配 (僵尸连接)、Pod 下线中 readiness 未摘 、Service 规则延迟 、upstream 连接超时;
- 最高频的是第 1 类:后端的 IdleTimeout 要 ≥ 网关的 keepalive_timeout(让网关先关);
- 滚动更新期的 502 用
preStop: sleep解决; - 区分 502 与 504:502=连不上/响应无效,504=已连上但读超时。
留个问题给你 :你们后端服务的 keep-alive 超时,和 Ingress 的 keepalive_timeout 是一致的吗?有没有在滚动更新时观察到偶发 502?