K8s Ingress 返回 502 但后端日志正常怎么办

Ingress 报 502,后端 service 的访问日志里却查不到这次请求——大概率不是后端服务的问题,是请求在 Ingress 和 upstream 之间的 TCP 连接上就被截断了。常见的几种情况是 HTTP 版本不一致导致 keep-alive 复用失败、响应体把缓冲区撑爆、滚动更新时 endpoint 同步存在窗口期、本机代理把请求拦在集群外。第一嫌疑先放在 HTTP 版本上:Ingress-nginx 默认对 upstream 走 HTTP/1.1,但很多后端只支持 HTTP/1.0,第一个请求能成,第二个请求被服务端直接 RST 掉 [3]。先抓包确认 RST 是 Ingress 先发还是上游先发,就能把范围缩到一两种配置上。

一段相对典型的抓包场景是这样:Ingress 和 uwsgi 之间的 TCP 连接被复用,HTTP/1.1 客户端在同一条连接上发了第二个请求,服务端回了 RST,Ingress 立刻把 502 抛给上游 [3]。这种情况下 GET 几乎看不到 502,但 POST 稳定报错——GET 被 Nginx 当幂等请求处理,proxy_next_upstream 默认对错误做重试 [3],所以用户层感知不到;POST 不能重试,502 直接进了调用方日志。这个差异反过来就是排查入口:GET 没事、POST 有事,几乎可以锁定在连接复用这一层。

HTTP/1.1 和 HTTP/1.0 不匹配就是这类问题的根因。Ingress-nginx 默认对 upstream 用 HTTP/1.1,但很多应用比如 uwsgi 的 http-socket 模式、某些老版本 WSGI 框架只跑 HTTP/1.0,不支持 keep-alive。官方 ingress-nginx 的注解里 proxy-http-version 可以强制指定 Ingress→upstream 这一段的 HTTP 协议版本 [3],最直接的修法是把它写回 1.0:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-http-version: "1.0"

改完之后回抓包看,原本第二个请求被 RST 的那一帧应该消失,GET 和 POST 的错误率会拉平。

另一种常见 502 是响应体太大塞不进 Ingress 默认的缓冲区。Ingress-nginx 默认 proxy_buffer_size 是 4k、proxy_buffers 是 4 个 4k [6],超过之后 Ingress 主动断开上游连接。keycloak 这类应用比较典型,调到 proxy-buffer-size: "16k" 配合 proxy-buffers-number: "8" 就基本不出 502 [6]。滚动更新期间出现的偶发 502 又是另一种机制:Pod 进入 Terminating 后 endpoint 控制器同步存在延迟,Ingress 还会把请求往旧 Pod 的连接上送,旧 Pod 已经不接受新请求直接拒掉。这里有个常见误区——在 preStop 里 sleep 想延迟关闭其实没用,Pod 在 preStop 期间对外还是显示 Running,Ingress 的 endpoint 不会因此延迟摘除 [2];真正延迟关闭只能在应用进程收到 SIGTERM 信号之后自己做 [5]。

还有一类 502 根本不在集群里。Ingress、Service、Pod 都通,但本机浏览器或者 curl 一直 502,先别动集群——把本机的系统代理关掉,或者把域名加进系统代理绕过列表就行。这个坑在 Windows 主机上最常见,Windows 默认所有请求走代理,WSL 直连反而正常 [1][2]。这种情况的特征是抓包根本看不到包离开过主机。

抓包命令本身不复杂,关键是选对节点——Ingress Controller Pod 调度到哪个 Node,就在哪个 Node 上抓:

# 先确认 Ingress Pod 跑在哪个节点
kubectl -n ingress-nginx get pod -o wide
# 在那个节点上执行,过滤对端 Pod IP 和 Service 端口
tcpdump -i any -nn -A host <backend-pod-ip> and port <service-port> -w /tmp/502.pcap

拿到 pcap 之后用 Wireshark 看 RST 由谁先发:RST 来自 Ingress,问题大概率在配置(HTTP 版本、缓冲区);RST 来自后端,问题就是协议不匹配或连接被服务端主动关掉 [3][4]。

碰到「后端日志干净却 502」的时候,第一嫌疑先放在 HTTP/1.1 连接复用上——尤其是那种 GET 看起来没事、POST 稳定报错的形态,GET 重试会把这类问题盖住,POST 暴露出来基本一抓一个准。

参考来源

[1] k8s ingress 配置后访问返回502的问题 – 大师兄啊哈 – 博客园

[2] K8s ingress 域名解析时报502错误_ingress 502-CSDN博客

[3] 线上K8sIngress访问故障排查思路,看这一篇就够了

[4] K8s 网络排障:从抓包开始,一步步定位诡异”502″-CSDN博客

[5] 如何解决容器化K8s滚动更新时旧工作进程残留导致五零二-php中文网

[6] k8s~Ingress转发出现502的问题 – 张占岭 – 博客园

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容