导读
可用性和稳定性对于eBay的网络站点非常重要。
本文分享了CAL(集中式应用程序日志框架)中最近发生的一个有趣的TCP链接问题,这一问题会严重影响CAL的稳定性,进而影响eBay网络站点的良好运行,解决问题迫在眉睫。
来看看CAL团队如何使用TCPDUMP和systemtap工具,通过简单高效的四个步骤,找出问题的根本原因的。
CAL运行架构
CAL是eBay的集中式应用程序日志框架。
CAL的主要目的是集中收集应用程序服务器的本地日志,并据此给出所收集数据的报告。CAL的数据报告提供了对eBay站点重要领域的深入洞察,这对于eBay制定业务的各个方面都非常宝贵。
那么,CAL是如何运行的呢?
如上图所示,在CAL的运行架构中,CAL客户端会尝试与CALVIP(虚拟IP地址)建立TCP长连接。由于CAL的流量非常巨大(每秒几十GB),单对负载均衡器(LoadBalancer)无法负载,因此有数对LB负责流量的传输,不同的LB上有不同的VIP。CAL客户端先从DNS查询中随机选择一个VIP尝试连接,如果连接超时,客户端会选择另一个VIP重试,直到成功建立连接。
问题描述
最近,CAL团队遇到了一些奇怪的问题,这让他们有些困扰。
如上图NetCat输出所示,VIP会间歇性地返回连接超时,而且这一问题频繁而毫无规律地出现,这会严重影响CAL的稳定性,进而影响eBay网络站点的良好运行。
问题修复迫在眉睫,一起来看看CAL团队是如何进行故障定位的吧!
故障定位
对故障的定位与处理主要分为以下四个步骤。
Step 1.
在LB上运行TCPDUMP
明明VIP和LB的配置看起来都是正确的,为什么VIP还会连接超时?
我们首先查看TCPDUMP,试图找到连接超时的原因。
如上方的Wireshark截图所示,当连接超时发生时,LB主动向客户端发送了RST包,根据F5的插件给出的原因是流量到期(Flowexpiration)。
我们把其中一个TCP流拿出来单独看。
如上方截图所示,LB的SNAT(SecureNetworkAddressTranslation)IP发送了3次SYN包,但是服务器端无视了这些包。大约15秒以后,LB的SNATIP认为连接超时,主动发送了一个RST包。
Step 2.
检查SYN队列和Accept队列
为什么服务器会忽略LB发来的SYN数据包?
由于当时我们正在做CAL流量的迁移,绝大部分CAL流量正在从老版本代码向新版本迁移。因此我们猜测可能是新版本代码的性能问题。
一个合理的解释是新版本性能不够强,无法及时处理巨大的流量,导致SYN队列堆积满溢,新的SYN包无法进入队列而被丢弃。
下图解释了SYN队列以及Accept队列的工作方式。
乍一看,证据似乎支持了前面的假设。
但是,我们写了一个Systemtap的探针脚本(如下图)尝试去捕捉SYN队列满溢情况,却没有得到任何输出。
因此,对于新版本代码性能问题导致SYN包被丢弃的猜测被证实是错误的。
Step 3.
用netstat -s重新检查网络状态
由于之前的猜测被推翻,我们用netstat-s重新检查了网络层面的状态。
通过检测,一条新的线索浮出水面(如下图),我们发现,大部分的SYN包被丢弃的原因是时间戳(timestamp)。
那么,时间戳是如何导致SYN包丢失的?
原来,‘Passive connections rejected because of time stamp'有一个专门的计数器LINUX_MIB_PAWSPASSIVEREJECTED,它在内核代码中只出现在一个地方[1]。当函数tcp_peer_is_proven(req,dst, true)返回false时,Linux内核会丢弃SYN包并记录在LINUX_MIB_PAWSPASSIVEREJECTED。
[1]https://elixir.bootlin.com/linux/v3.10/source/net/ipv4/tcp_ipv4.c#L1555
这段代码片段如下:
如上图所示,当tcp_tw_recycle打开时,内核记录了每个peer最后一个ACK的时间戳,当新的连接满足以下3个条件时,SYN包会被内核直接丢弃。
据此,我们怀疑,是LB SNAT与Linux内核参数net.ipv4.tcp_tw_recycle一起导致了这个问题。在验证这一猜想之前,我们简单介绍一下SNAT是如何在LB端工作的。
如上图所示,在LB的内存中包含了一个连接表,它记录了Client-VIP连接和SNAT-PoolMember连接之间的映射关系。在F5的FastL4模式下,LB仅更改了src_ip,src_port,dst_ip和dst_port,然后将其发送到另一个连接端。
TSval记录了每个TCP包的时间戳,由于TSval是基于客户端CPU时间的(客户端CPU时间不同),因此来自不同客户端的不同数据包具有不同的TSval。经过LB的SNAT转发后,就有可能发生TSval较大的包先到,从而使服务器端丢弃TSval较小的包。
Step 4.
用systemtap和TCPDUMP验证问题
我们编写了一个新的systemtap脚本,同时在CAL服务器上运行TCPDUMP,得到的结果均可以验证前文的猜测。
脚本运行结果如下图所示:
如下方TCPDUMP运行截图所示,在CAL服务器端口1120上运行的应用程序向LBSNATIP发送了(FIN,ACK)包,然后,LB发送了TSval为517740536的ACK包,随后,连接关闭了。
几十秒后,LB使用相同的SNATIP尝试建立到同一目标IP和端口的新连接。因为先前的ACK包在18s发送,而新的SYN包在60s接收,所以间隔时间是42秒,小于60s,(u32)get_seconds()-tm->tcpm_ts_stamp
如下图所示,比较tm-> tcpm_ts(517740536)和req-> ts_recent(481284815),正如systemtap脚本的输出结果所示,两者之差大于TCP_PAWS_WINDOW。因此函数tcp_peer_is_proven返回false,内核丢弃此SYN包。
总结
由于TSval是基于客户端CPU时间运行的,不同客户端的CPU时间可能并不相同,因此如果流量通过负载均衡器,内核可能会意外丢弃某些连接。为了避免这一问题,应在SNAT网络中禁用net.ipv4.tcp_tw_recycle。
后续:自Linux4.1以来,该功能已在内核中完全删除。
Reference
1.Dropping of connectionswith tcp_tw_recycle
2.RFC 1323
3.[net-next,2/2] tcp:removetcp_tw_recycle
4.net.ipv4.tcp_tw_recycle hasbeen removed from Linux 4.1 - kernel git
5.Coping with the TCPTIME-WAIT state on busy Linux servers
6. SYN packet handling in the wild
7. K7820:OverviewofSNATfeatures
您可能还感兴趣: