K8s Ingress 返回 502 但后端日志正常:4 类上游断连与排查链

我不会起名字322 · 后端 / 算法 / 数据库

📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL

文章目录

  • [K8s Ingress 返回 502 但后端日志正常:4 类上游断连与排查链](#K8s Ingress 返回 502 但后端日志正常:4 类上游断连与排查链)
    • 一、先搞清流量路径
    • [二、第 1 类:keepalive 超时不匹配](#二、第 1 类:keepalive 超时不匹配)
    • [三、第 2 类:Pod 正在下线但 readiness 还没摘](#三、第 2 类:Pod 正在下线但 readiness 还没摘)
    • [四、第 3 类:Service iptables 规则更新延迟](#四、第 3 类:Service iptables 规则更新延迟)
    • [五、第 4 类:upstream 超时配置](#五、第 4 类:upstream 超时配置)
    • 六、排查顺序表
    • 七、总结

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 还没摘

滚动更新时有个经典竞态:

  1. Pod 收到 SIGTERM,开始停止;
  2. 但 Service 的 endpoints 更新是异步的(kube-proxy 需要时间刷新 iptables 规则);
  3. 在这段窗口里,Ingress 仍会把新请求转发到"正在关闭"的 Pod;
  4. 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。

七、总结

  1. 502 是网关报的,不是后端报的 ------ 后端日志正常恰恰是正常的;
  2. 4 类断连:keepalive 超时不匹配 (僵尸连接)、Pod 下线中 readiness 未摘 、Service 规则延迟 、upstream 连接超时;
  3. 最高频的是第 1 类:后端的 IdleTimeout 要 ≥ 网关的 keepalive_timeout(让网关先关);
  4. 滚动更新期的 502 用 preStop: sleep 解决;
  5. 区分 502 与 504:502=连不上/响应无效,504=已连上但读超时。

留个问题给你 :你们后端服务的 keep-alive 超时,和 Ingress 的 keepalive_timeout 是一致的吗?有没有在滚动更新时观察到偶发 502?

相关推荐
Eminem896 小时前
18、k8s配置管理中心Secret
k8s
我不会起名字3226 小时前
Pod 一直 Pending:调度器 5 类失败原因与 kubectl describe 的正确读法
云原生·kubernetes·调度·排查
我不会起名字3229 小时前
K8s 服务 DNS 解析偶发 5 秒超时:conntrack 与 ndots 的 3 个坑
kubernetes·k8s·dns·conntrack·coredns
七夜zippoe13 小时前
把 OpenClaw Agent 搬进 K8s:容器化部署全记录
ai·云原生·容器·kubernetes·openclaw
March.s1 天前
Kubernetes 全景导览:从部署演进到云原生底座
云原生·kubernetes·dubbo
熊出没1 天前
云原生架构落地实践:Kubernetes 生产级配置与 Service Mesh 流量治理
云原生·架构·kubernetes·istio
东风微鸣1 天前
K8s 最佳实践已更新:请使用 Node Swap!
kubernetes
励志师code1 天前
Kubernetes 集群节点生命周期管理实战:从扩容、调度管控到证书续期
运维·docker·容器·kubernetes·gpu算力·kubelet