一、问题描述

负载均衡网关(如LVS)配置FULLNAT模式时,使用wrk进行短连接测试,用于测试负载均衡网关的稳定性和每秒新建连接数。但是在测试的过程中发现了一个奇怪的现象:首次使用wrk进行压测时正常,多次使用wrk打流量后报错显示wrk: connect(): Cannot assign requested address。我们wrk压测命令如下:

#! /bin/bash

ulimit -n 819200
ulimit -i 819200

sysctl -w net.core.somaxconn=65535
sysctl -w net.ipv4.ip_local_port_range="1025    65535"
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_tw_recycle=1
sysctl -w net.ipv4.tcp_fin_timeout=1
sysctl -w net.netfilter.nf_conntrack_max=2097152
sysctl -w fs.file-max=1622794

while true; do
	./wrk -t16 -c20000 -d300 -H "Connection: Close" http://172.16.0.123:60000 &
done
# 多打几次流量后,给出报错信息
wrk: connect(): Cannot assign requested address

这就给测试人员一个奇怪的错觉,你的负载均衡网关是不是在短连接情况下很快耗尽客户端的端口,你的网关在短连接大并发下会出问题!


二、问题定位

2.1 FULLNAT下客户端端口重用失败

我们使用wrk短连接压测虚拟服务,端口占用48000左右。直接用wrk压测真实服务,端口占用在31000左右。统计客户端端口占用情况如下,VPORT=60000; netstat -ntp | grep $VPORT | wc -l,初步判断是客户端tcp_tw_reuse配置失效,没有重用端口。

那么问题来了,为什么客户端没有重用端口呢?这时我们使用wrk压测转发模式为NAT的虚拟服务,客户端tcp_tw_reuse生效了,并没有出现上述问题。那么我们排查问题的思路就变成了NAT模式和FULLNAT模式的转发区别了。

2.2 FULLNAT模式和NAT模式的区别

FULLNAT模式是由阿里开源的LVS提出的,用于解决NAT模式下真实服务和负载均衡器必须在同一个局域网的问题。由于FULLNAT使用了本地地址池,所以与NAT模式不通,FULLNAT模式下真实服务并不能得到客户端的真实IP。可以使用TOA、UOA等方式拿到客户端真实IP,本文并不详细讨论。

通过查看代码,我们发现FULLNAT模式相比较于NAT模式,在转发时去除了TCP的timestamp选项。这是因为不通的客户端连接时使用不通的时间戳选项,但是可能使用相同本地地址,如果不禁用时间戳选项,就会造成错误。

由此继续猜测,内核中TCP时间戳选项tcp_timestamps和端口重用选项tcp_tw_reuse是否相互影响?

2.3 tcp_timestampstcp_tw_reuse的关系

通过查看以下两篇博客,使能TCP端口重用必须开启TCP的时间错选项! 那么上述问题就可以说清楚了,因为FULLNAT禁用了时间戳选项,才导致了客户端无法重用端口。

文章链接:那些与TIME_WAIT有关的参数 此篇博客中关闭了tcp_timestamps选项,开启tcp_tw_reuse选项,在缩小端口范围后,很快出现与本文相同的错误信息Cannot assign requested address。作者还通过分析内核代码,发现tcp_tw_reuse需要等到最近一次时间戳一秒后,才快速重用TIME_WAIT状态的端口。

文章链接:好朋友 TIME_WAIT 此篇文章指出了为什么TCP端口重用必须开启TCP的时间错选项?使用 tcp_tw_reuse 跳过了 TIME_WAIT 阶段后,timestamp 时间戳选项可以防止旧连接的延迟报文段。当新连接建立后,时间戳更新为最新的时间,当延迟的报文段到达后,其时间戳是小于当前连接的最近有效时间戳的。这个时候 TCP 就可以将这个报文段直接丢弃了。
TCP 的四次挥手,只有主动断开连接的一方才会进入 TIME_WAIT 状态,并且只有重新发起连接的时候才会重用具有相同四元组的处于 TIME_WAIT 状态的 socket。这也就是说: tcp_tw_reuse 这个参数只对主动发起连接的客户端才奏效,只适用于 outbound 连接!


总结:还是需要扎实的网络功能,才能避免测试给你扣帽子 ( ╯□╰ )