记录一次lvs请求负载不均问题的排查

2,205 阅读6分钟

问题发现

最近线上业务经常因为cpuidle过低报警,检查发现线上服务集群节点的请求量非常不均匀,具体表现甚至能差到最大单机流量是最小单机流量的4-5倍。

这个业务是一个流量回放的服务(后续简为服务A),流量来源主要有两部分,一块是从控制台过来的前端请求流量,另一部分是DDMQ推过来的常驻回放流量,前一块流量很小可以简单忽略,后一块流量是大头,预估3k~4kqps左右。

前一块控制台的流量为通过域名解析到麒麟后直接转发到rs。

后一块mq推的流量为ddmq请求lvs转发至rs。

为排除控制台过来的影响实验期间暂时关闭域名转发到rs的流量,仅保留ddmq的流量。

提出猜想

第一反应是是不是因为弹性云页面配置的节点容器权重不同,导致各机器负载不一致检查发现节点机器权重都是默认值100,并无异常,因此此条路排查完毕。

第二直觉因为在分析ddmq推过来的请求报文发现使用了keepalive,假设ddmq httpclient对下游http请求连接池负载合理的情况下,认为qps可能和服务上游(ddmq)对服务建立的tcp连接数成正相关,这里先假设服务机器TCP连接总数与上游对本服务建立的TCP连接数成正相关。

但是观察tcp连接监控却发现qps最低的0号机维护着最多的长连接,qps最高的4号机维护的tcp连接数并不算非常突出。

于是上0号机器一探究竟,发现连接数大多是本地的service mesh进程chorus(部门自研service mesh代理)对本机业务服务建立的tcp连接,外部对本机service mesh端口建立的tcp连接仅有6条,而4号机器虽然总tcp数量比较少,但是外部对本机servicemesh端口简历的tcp连接有24条,因此暂时推翻服务机器TCP连接总数与上游对本服务建立的TCP连接数成正相关的假设,tcp连接数监控图暂时丢到一边不管。

发现负载请求量最大的4号机维持了24条外部与入口端口建立tcp连接,但是发现现象看似的确与猜测的第二个结论 qps可能和服务上游(ddmq)对服务建立的tcp连接数成正相关 相符。

问题排查到这里,感觉负载不均可能是因为lvs的在初次转发未建立会话的syn包时对下游机器分发负载不均,导致tcp连接建立不均的问题,因此横向对比部门内部另一个流量差不多,且同样是依赖lvs暴露服务的服务B作对比,观察发现服务B虽然也有流量略微的不均问题,但是并没有服务A表现的那么极端,观察服务B近一小时的流量数据,发现 机器最大qps/机器最小qps 最差情况也只是在1.3~1.4这个范围,比服务A的负载不均情况表现好很多。

观察外部建立tcp连接数,发现服务B因为上游较多,因此tcp连接也建立较多,因此负载不均的情况比服务A好很多。

求证

咨询lvs(dgw)相关负责人表示,lvs(dgw)四层负载负载的原理是

所谓四层网络负载均衡:用户client访问dgw的vip:vport(tcp udp端口), dgw根据相同会话(client_ip->client_port->dgw_vip→dgw_vport)调度到相同的rs_ip:rs_port(real server,真实的业务服务器)。不同会话调度到不同的rs,实现负载均衡。

dgw四层负载均衡和七层代理的区别:四层负载的原理是替换client的数据包的IP包头和tcp+udp包头内容后把数据包转发给rs,dgw不和rs建立链接,真实的tcp udp链接协商还是client↔server建

由此可知,lvs实现负载的主要方式是在会话未建立时,对下游rs轮询建立会话,并且转发syn数据包以达到负载均衡,看上去服务A负载不均的主要原因也是因为建立会话时的负载不均而导致的,进一步咨询后lvs负责人表示dgw集群一般一个集群有24个服务器节点负责负载lvs流量,并且默认的调度策略为权重模式且基于cpu-core数调度,这个值默认被设置为8,也就是说一台lvs机器对下游的负载极端情况下可能会差8个tcp链接,24台lvs会将这个数量进一步放大24倍,因此在tcp连接数比较少的情况下,负载不均的极端情况可能会导致业务不同的rs流量相差较多。

以上场景也正好对应了服务A的上游场景,ddmq推的模式由于使用了keepalive且对下游维护了http连接池,因此如果服务的上游仅有ddmq的情况下,对下游建立连接数有限,在dgw默认调度权重策略下是会导致tcp连接数不均,以导致请求量不均匀的问题。也对应了因为服务B的上游场景较为复杂,虽然也存在连接数不均的问题,但是因为维持的连接数较多,所以请求负载不均的问题比服务A要好很多。

改进方案

方案一、尝试强行降级上游keepalive的http请求,主动断开上游的http长连接,以让上游重新对下游建立连接,以此让lvs多次对下游重新负载,以达到负载均衡的效果,发现dgw诚不欺我。负载均衡效果的确比较理想。

但是这种方法显然十分高能耗,也并不被推荐,因此尝试将lvs对下游负载的权重模式改为轮询模式(待补充,sre还没改,暂时认为这种方法会更好)

方案二、将默认的权重模式改为轮询模式

方案三、mq推改拉,不在本次主题讨论范围内,略了

方案三、mq推改拉,不在本次主题讨论范围内,略了

得出结论

在上游请求量较大,但是TCP连接时间长且总连接较少的场景下(比如当且仅当上游流量只由ddmq推的形式到达下游)。不建议服务通过dgw对外接入流量,如果非得通过dgw接入的话建议由默认的权重模式改为轮询模式。