Kubernetes Ingress诡异的502、503、504等奇葩问题(二)
公众号原文:https://mp.weixin.qq.com/s/nEBMG8zdW1O-3XvygMSRVQ
上一篇《Kubernetes Ingress诡异的502、503、504等奇葩问题(一)》简要说明了使用基于 haproxy 的 ingress 时,遇到 503 的问题,这一篇记录使用基于 nginx 的 ingress 时,遇到的 502 的问题。
启用 keep-alive,502 响应增加
nginx-ingress-controller 0.20 之前的版本存在一个 bug,配置模版 nginx.tmpl 中没有在启用 keep-alive 时清除请求中的 “Connection: close”,反而是为所有转发给 upstream 的 http 请求加上了该请求头:
应该是
因为该 bug 的存在, 0.20 之前的版本即使在 config-map 中配置了 keep-alive,nginx 与 pod 之间的 http 通信依旧是短连接,每产生一个请求就建立一个连接。连接利用效率低,并且大量连接处于 time-wait 状态,浪费了数量有限的本地端口。
发现该问题后,我们进行了及时修复,然后问题来了。
nginx 的请求日志显示,keep-alive 真正生效以后,502 响应的数量有所增多,大部分业务系统对此不敏感,但有个别系统因 502 响应而频繁告警,这些系统在 keep-alive 没有生效之前,没有遇到 502 响应。
调查工作开始。
502 响应占比很少很少,并且出现的时机不明,一开始毫无头绪,通过翻阅 nginx 的日志和分析抓取的部分报文发现,出现 502 响应时,nginx 向 upstream 转发请求后,立即收到了 RST 报文:连接被 pod 断开了。
Nginx 转发给 Pod 的请求中指定了 keep-alive,连接也持续了较长时间,来回有过多次请求了,为什么会突然被 Pod 断开连接呢?
各种胡思乱想之后,将目标锁定为在 Pod 中运行的业务系统。在 Pod 中运行的业务系统是一个 tomcat 服务,tomcat 本身就是一个请求代理软件。
事实上,客户端发起的请求经过了两次代理,第一次由 nginx 代理到 Pod 中的 tomcat,第二次由 tomcat 代理到处理该请求的进程。
翻看 tomcat 的配置手册时,发现 tomcat 有几个和 nginx 类似的配置,它们分别指定了长连接的闲置超时时间(keepAliveTimeout)和长连接中的请求数量上限(maxKeepAliveRequest)。
很巧的是,这两个配置项的默认值和 nginx 中类似配置的默认值相同,都是连接闲置 60s 秒后断开,以及连接中请求数量达到 100 后,断开连接。那么问题来了,nginx 和 tomcat 谁会先断开连接?
从捕获的报文来看,有时候是 tomcat 先断开的,nginx 后知后觉依旧转发请求,结果收到了 RST 大礼包,继而回应 502。502 错误码含义正是:上游返回了非预期的数据。
随后我们分析了另一个有同样问题的 python 服务,这个 python 服务也不是直接监听端口处理请求的,而是用 Gunicorn 代理。查了一下 Gunicorn 的默认配置,更夸张,它的连接闲置超时时间是 2 秒!
分析该 python 服务的报文时还在纳闷,为什么它的连接很快就断开?一度让我们怀疑前面的假设,直到发现它的默认超时时间是 2 秒,才释然:报文显示,连接正是在闲置 2 秒之后,被 Pod 端主动断开的!
要避免 Pod 先断开连接,方法其实很简单,让 nginx 中的相关配置小于 Pod 中的代理软件的相关配置即可。譬如tomcat 的 maxKeepAliveRequest 是 100,那么 nginx 中配置成 99,确保连接是被 nginx 主动断开的。
这里只大概说明一下原委,查看当时的调查记录,点击「阅读原文」。
另外还有一个 504 的问题,504 其实是 kube-apiserver 的配置错误导致的,因为配置错误导致 endpoints 没有及时更新,继而导致 nginx 的配置文件没有更新。
Nginx 的 upstream 配置中的 IP 地址早已不存在,或者已经分配给其它 Pod,从而导致客户端收到 504 响应或者得到的是另一个服务的响应。
因为时间紧任务多的缘故,我们把配置修复以后,没有对此进行深入追究,如果有了进一步分析,再补发吧。感谢关注,感谢转发!
上一篇:Kubernetes Ingress诡异的502、503、504等奇葩问题(一)
关注返回作者微信,直接加,不闲扯
(除非特殊情况,不帮忙解决问题)