记录一次tcp_timestamps 线上问题的排查

我们的情况和这个朋友遇到的有点类似:
https://blog.csdn.net/majianting/article/details/96476375
如我的域名是:yuming.api.com
如公网ip是:192.168.2.202
我线上的接口是:http://yuming.api.com/?s=init
业务架构:nginx+PHP+redis+mysql
域名有腾讯云clb转发进来内部rs 机器。

一、场景复现

研发偶尔会反馈说接口连不上的问题,报504。但是nginx没有记录到日志,PHP也没有。一般报504 的话是后端接口返回超时,这种情况比较多的是出现在上传大文件的时候。只要改下nginx或者PHP的限制超时参数即可。但是我们这个问题不是这个原因引起,因为研发反馈的时候,请求没到nginx。并且我们复现的时候也很难。因为这是偶现的,而且项目还没上线就出现了。

二、排查过程

1、查看nginx日志,PHP日志,系统message日志都没有看到和这个504异常的信息。即说明请求异常请求没有到达我们的nginx,更别说PHP了。
2、查看zabbix监控,关于内存、CPU、磁盘io和网络流量监控,发现异常时间附近会出现相对大一点的流量,但是不至于说返回504。其他业务机也是这样,算是正常情况。
3、怀疑是域名转发的问题,腾讯云开了一个负载均衡的ip,即clb,将域名绑定到负载ip上再转发进去真实ip,但还是有问题。看clb日志看到有到达clb,但是没有到达我们RS的nginx。
4、本来想通过zabbix的web监控这个接口,看下异常报错是否有规律可循。因为人工很难复现。通过zabbix添加web监控来监控这个接口和这个业务的后台,发现这个接口一直是504的状态,但是后台是200,说明zabbix访问这台机器异常了,即可以完美复现了研发说的偶现的504 情况,这也给了问题排查提供了关键的切入点。通过zabbix机器使用curl直接访问这个接口返回504。但是如果绑定host去访问的话就没问题,说明机器直达也是没问题的。但是去掉host就不行。说明通过域名转发进去的是有问题的,但是如果不经过域名那层nat 转发进来就没问题。问题也可能就出现在nat转发这里。
5、通过tcpdump在接口机抓包,抓zabbix机器的访问的包,按照按照tcp三次握手原理和http请求再对比其他正常的请求,发现zabbix机器到达了接口机,但是接口机没有返回,导致三次握手没有完成,更别说要做数据交换。三次握手没有成功,说明要么是数据包被接口机丢弃了,要么是tcp连接被接口机直接断开了。

三、解决方式

调整下面三个内核参数:
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_timestamps = 0

四、相关内核参数解析:

net.ipv4.tcp_tw_reuse = 0    表示是否开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0 关闭。1 为开启。
net.ipv4.tcp_tw_recycle = 0  表示是否开启TCP连接中TIME-WAIT sockets的快速回收,默认为0 关闭。1 为开启。
上面两个参数必须是timestamps 开启的情况下才会生效!
net.ipv4.tcp_timestamps = 0     是否开启是时间戳 0 关闭,1开启(默认开启)
net.ipv4.tcp_fin_timeout = 60  表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间(可改为30,一般来说FIN-WAIT-2的连接也极少

五、描述

搜索该参数相关的资料,发现同时启用tcp_tw_recycle和tcp_timestamps后有可能在NAT环境下导致客户端始连接失败,抓包表现为:客户端一直发送SYN报文,但服务端不响应。
也就是说服务器同时开启tcp_timestamps 和tcp_tw_recycle:可以缓存每个连接最新的时间戳,后续请求中如果时间戳小于缓存的时间戳,即视为无效,相应的数据包会被丢弃。意思就是同一个源IP来连接同一个目的端口的数据包时间戳必须是递增的
比如我们用lvs做负载均衡,lvs会将用户请求转发到rs机器,实际上中间就是nat过去的。也就是说lvs访问服务器是同一个ip,而且是nat过去的,可能会被认为是同一个连接,加之不同客户端的时间可能不一致,所以就会出现时间戳错乱的现象,于是后面的数据包就被丢弃了,那么上面的问题就很可能会出现。
也就是说:服务器在建立三次握手的时候会校验同一个源ip 发送过来的ack数据包的时间戳,如果时间戳比系统记录的时间戳小的话,才会丢弃这小时间戳的链接,直接不会SYN+ACK包了。

六、案例说明:

LVS地址:222.222.222.222
web服务器地址:111.111.111.111
客户端C1地址:100.100.100.101
客户端C2地址:100.100.100.102
假如出现这种情况
13:23:02这个时间点
C1发出的TCP数据包源IP和源端口为100.100.100.101:6332,目标地址和端口是 222.222.222.222:80
13:23:05这个时间点
C2发出的TCP数据包源IP和源端口为100.100.100.102:52223,目标地址和端口是 222.222.222.222:80
经过LVS的full nat。
假如在13:23:06时刻LVS收到C2的数据包
C2的数据包被转换为
222.222.222.222:52223 ---->111.111.111.111:80
假如在13:23:07时刻LVS收到C1的数据包
C1的数据包被转换为
222.222.222.222:6332 ---->111.111.111.111:80
假如web服务器开启了tcp的tcp_timestamps和tcp_tw_recycle这两个参数。web服务器根据数据包的时间戳确认。
C1的数据包由于时间戳小于目前系统登记的此源IP连接的时间戳,被认为是重传数据,C1的数据包就被丢弃了。

上面参数可能会引起下面几个问题:

1、机器ping得通,端口Telnet通,但是使用curl访问页面的时候不通。
2、抓包的时候发现客户端的包到了服务器,但是服务器没有返回,也就是说三次握手的时候客户端发了SYN给服务器,但是服务器没有返回SYN+ack包给客户端,这样建立三次握手失败。

七、对内核参数tcp_syncookies 理解:

了解SYN Flood 攻击
TCP连接建立时,客户端通过发送SYN报文发起向处于监听状态的服务器发起连接,服务器为该连接分配一定的资源,并发送SYN+ACK报文。对服务器来说,此时该连接的状态称为半连接(Half-Open),而当其之后收到客户端回复的ACK报文后,连接才算建立完成。在这个过程中,如果服务器一直没有收到ACK报文(比如在链路中丢失了),服务器会在超时后重传SYN+ACK。

三次握手图示

在这里插入图片描述

如果经过多次超时重传后,还没有收到, 那么服务器会回收资源并关闭半连接,仿佛之前最初的SYN报文从来没到过一样!如下图,服务器不断的重发SYN+ACK报文,但是没有客户端接收,这样就浪费了服务器资源了。
在这里插入图片描述

这看上一切正常,但是如果有坏人故意大量不断发送伪造的SYN报文,那么服务器就会分配大量注定无用的资源,并且从backlog的意义 中可知,服务器能保存的半连接的数量是有限的!所以当服务器受到大量攻击报文时,它就不能再接收正常的连接了。换句话说,它的服务不再可用了!这就是SYN Flood攻击的原理,它是一种典型的DDoS攻击。
它的原理是,在TCP服务器接收到TCP SYN包并返回TCP SYN + ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。这个cookie作为将要返回的SYN ACK包的初始序列号。当客户端返回一个ACK包时,根据包头信息计算cookie,与返回的确认序列号(初始序列号 + 1)进行对比,如果相同,则是一个正常连接,然后,分配资源,建立连接。
Linux中的/proc/sys/net/ipv4/tcp_syncookies是内核中的SYN Cookies开关,0表示关闭SYN Cookies;1表示在新连接压力比较大时启用SYN Cookies,2表示始终使用SYN Cookies。

Guess you like

Origin blog.csdn.net/MYF12/article/details/118581814