502偶现故障的分析

2022-06-14 14:39:36 浏览数 (1)

背景描述

周五的上午时候,被业务方同学喊过去解决技术问题。问题表象是:业务偶发http 502, 且一次502就会导致上游业务方修数据,因此急需解决这个问题。之前开发方大题描述过业务架构,但是笔者从来没有登陆过该业务的生产环境,具体的运维部署架构不太清楚。仅在沟通过程中,了解到业务全部部署在k8s集群中,502是发生在apisix 和后端pod之间:

upstream 使用的是k8s集群的 service cluster ip 端口。顿时感觉这个问题可能很复杂,也可能比较简单。之所以说复杂,因为service cluster ip的引入,会将访问的路径拉长,LVS/iptables/协议栈/内核等技术问题都会引入进来,因为网络抖动,LVS网络问题,协议栈NAT机制,iptables , apisix本身等等因素都可以造成502现象。之所以说简单,如果有可靠的证据说明跟以上环节无关(LVS/iptables/协议栈/内核等),仅仅涉及到apisix 和 后端node应用, 这种情况下,排查的复杂度就大大降低。

分析过程

第一步:需要将范围缩小。由于apisix运行在pod中,想gdb debug 基本没有戏(需要从新编译)。尝试先在apisix pod中抓包。由于502是偶尔产生一次,静待一段时间后,抓到了一次502, 通过ELK日志 抓包,很快定位到出现问题的精确时间点。

502 产生的原因是 TCP连接被重置了。

先铺垫一下TCP相关的基础知识:

1. 端口未监听,收到TCP连接请求,协议栈会发送RST

2. 端口主动关闭,这种场景下,跟socket so_linger配置有关系(目的是延时gracefully或者暴力关闭socket), 默认情况下l_onoff ==0 , 即采用暴力关闭socket, 直接丢弃发送缓存区中的数据,这种情况会产生RST。

3. 向关闭的socket发送fin,也会发送RST (参考fin扫描的原理)

从抓包的分析结果来, 正常通信的socket突然被重置了, 这种情况下,第二种的原因的概率非常大,但是也有其他的原因,例如这个RST是中间环节的某个设备发出的【例如iptables防火墙之类的】。

周五当天也拿到k8s权限,登录容器中查看相关的情况(5个pod所在的node都是如此):

nodejs pod使用的是默认的pod scheduler, 因此nodejs pod仍然会在这几台宿主机中调度,pod所在的宿主机上协议栈的reset状态值侧面反馈出这个reset的问题存在很长的时间了,此刻应用层reset的嫌疑最大,但也有可能是k8s中间网络设备产生的。

第二步:进一步排除可能的干扰项,在pod端抓包,如果pod端抓包分析后没有出现RST的情况,那么这种排查就需要从k8s node LVS , iptables 等一个一个地抓包分析了。幸运的是,在后端node 的pod中抓包,出现了RST的情况,因此RST的原因,应该出现端对端的某个环节上,排除了中间链路环节,工作量大大降低。

第三步: 从底层往应用层上查找,定位问题所在。

   由于步骤1,步骤2, 已经将问题缩小到apisix(nginx) 和 node 应用层了,解析来就需要了解nginx和node相关的知识了。

从抓包中,可以看到是node端主动close socket所致。仔细分析抓包的报文,发现一个特点,相邻请求的间隔时间在4-5秒左右(在请求量少的时间段),仔细观察node response header里,看到一个 keep-alive_timeout=5, 很快就定位是keepalive的问题了。

查看了上游apisix的upstream keepalive_timeout配置,其值为默认值:60s。到此问题的原因已经清楚了。

apisix upstream keepalive默认已经开启,对于空闲时间keepalive_timeout >60s的连接,nginx的连接池会回收掉, node段对于空闲时间>5s的连接 也会回收掉。由于该业务是一个低并发的业务,请求量并没有那么大,因此要达到以下的场景的概率是比较低的:

nginx upstream 的idle长连接还在有效期(60s内),用户进来的请求在proxy过程中从连接池中用到了这个idle的连接,并且这个nginx upstream idle 连接在node端刚好idle时间超>5s , node端keepalive timeout机制触发close的动作。

解决办法

1. 调整上游apisix(nginx)的upstream keepalive_timeout的时间,让其小于

node端keepalive timeout的时间(次优方案)

2. 调整node端keepalive timeout时间,使其> 上游keepalive timeout时间:60s (优选方案)

虽然这个问题查起来比较简单且直接(幸好排查路径没有走到k8s网络,LVS,iptables等分支,否则全链路都得深入查一次),用到技术比较简单, 但是对于陌生环境下的技术问题需要清晰的分析思路以及靠谱的手段,否则耗时耗力还不一定能解决问题。

0 人点赞