一.问题现象:
同事反馈容器少量节点出现高si导致cpu使用率高
二.问题排查:
1. 抓取perf信息并结合代码分析热点主要在处理timewait socket上:
2. 因为node上运行这多个业务的Pod,首先找到导致软中断高的业务Pod, 会导致kernel处理timewait频繁一般就是产生的timewait数量多或者频率高。
(1) 首先对每个Pod的排序看下是哪个Pod产生的timewait数量比较多.
查看nf_conntrack看到每个net namespace下timewait数量都并不多:
cat /proc/net/nf_conntrack | awk '{print $7}' | cut -d, -f2 |sort |uniq -c |sort -n
(2) 排查下哪个Pod流量最大:
执行sar -n DEV 1 5查看网络流量
通过路由表对应的IP进而找到相应的Pod:
查看该业务Pod所在的node都存在si高的问题,与业务沟通后重建其中一个Pod进一步确认了si高是由该业务Pod引起的。
3. 排查Pod导致kernel处理timewait导致si高原因:
(1)进入容器内通过netstat统计Pod产生的timewait socket的数量并不多:
# netstat -tpne | grep -w TIME_WAIT | wc -l
3764
(2) 为了进一步排查问题,与业务沟通后把net.ipv4.tcp_max_tw_buckets设置为0让容器内不再产生新的TIME-WAIT socket。观察一段时间后发现有几百个TIME-WAIT socket一直没有释放,理论上TIME-WAIT超时后会自动回收掉
(3)排除端口存在复用:
ss命令查看其中一个一直为time-wait状态的连接定发现定时器一直有在60min和59sec之间跳变,是否为端口复用?
ss -o state time-wait dst 9.144.184.3:21861
临时将net.ipv4.tcp_tw_reuse net.ipv4.tcp_timestamps设置为0后time-wait数量仍然没有减少可以排除是端口复用导致的time-wait不释放。
如果是端口复用那么抓包应该是要可以看到三次握手和四次挥手过程的,对tcp port 21861抓包可以看到双端一直在重复发送相同的ack报文,并未有看到三次握手和四次挥手的过程,因此可以排除掉是端口复用导致的time-wait不释放。
(4)排查time-wait不释放原因
系统tcp_tw_recycle设置为0没有开启快速回收,timewait的数量也没有超过net.ipv4.tcp_max_tw_buckets设置的值正常情况下time-wait数量没有超过net.ipv4.tcp_max_tw_buckets设置的值并且没有开启快速回收time-wait功能时,time-wait状态的socket会在到达TCP_TIMEWAIT_LEN(60s)后被回收,那么这里time-wait 状态不回收的原因是什么呢?
结合前面perf信息以及抓包看到的双端都在一直互发ack报文,分析内核代码后可以确认内核是进入下面的处理流程,当连接进入time-wait状态后,如果继续收到对端发送过来的ack报文,那么本端会回复一个ack报文并且重启timewait超时定时器,定时器被重置导致定时器无法触发超时回收timewait socket,这也是为什么ss可看到socket连接的timewait定时器时间一直在60s和59s之间切换的原因。
tcp_timewait_state_process函数相关代码段:
4. 找出一直与服务端Pod互发ack的客户端IP:
因为业务采用的是ingress+service工作模式,所以在服务端Pod上看到的客户端IP 9.144.184.3:21861实际是一个nodeIP:nodePort ,并不是真正的客户端ip:port
#netstat -tpne | grep -w "9.144.184.3:21861"
tcp 0 0 9.144.148.219:80 9.144.184.3:21861 TIME_WAIT 0 0
如何找到真正的客户端IP呢?
(1) 找到服务端Pod对应的service port端口:
(2) 登陆service IP :node 9.144.184.3 ,通过/proc/net/nf_conntrack或者抓包找到访问服务端Pod 9.144.148.219:80的数据包源IP:PORT为9.6.196.220:21861
(3)
从上面nf_conntrack以及抓包可以知道数据流走向如下:
9.6.196.220:21861数据将包发送到nodeIP 9.144.184.3 的nodePort:30491
9.6.196.220:21861 <-> 9.144.184.3:30491
nodeIP 9.144.184.3 的nodePort 30491收到包后再经过iptable转换后由nodeIP 9.144.184.3 tcp port 21861端口转发到F服务端Pod所在node 9.144.184.11 端口eth0,经由路由表转发到服务端Pod容器9.144.148.219:80
9.144.184.3: 21861 <-> 9.144.148.219:80
简单画了个图便于理解:
5. 找出导致一直发送无效ack的原因:
需要进一步分析出最早一个无效ack是服务端发出的还是客户端,那么就需要抓到timewait socket交互的全过程。幸运的是这个问题在特定业务下只要跑一段时间就会出现少量的不释放timewait socket:
(1) 进入服务端Pod对服务端口80进行长时间抓包直到用netstat查看出现长时间未释放的timewait socket:
tcpdump -i any tcp port 80 -S -s0 -nn -w port80.cap
netstat查看哪些time-wait socket链接没有释放:
(2) 过滤抓包报文后进行分析
因抓包文件太大,因此过滤出其中一个不释放的timewait socket 对应的43762端口报文:
tcpdump -nn -vv -S tcp port 43762 -r port80_43762.cap -c 100 -w 9.144.184.26.43762.cap
#tcpdump -nn -vv -S tcp port 43762 -r port80_43762.cap > 9.144.184.26.43762.txt
# cat 9.144.184.26.43762.txt | head -100
从抓包信息可以看到四次挥手结束后,service node:9.144.184.66又发出了一个无效的ack报文,由前面的分析可以知道service node只是作为中转站,通过conntrack表找到其对应的客户端ip:
cat /proc/net/nf_conntrack获取上游客户端IP 9.6.201.26:
9.6.201.26是客户端机器,由于客户端用的非linux系统,本人也不太了解其工作原理,这里就不进一步讨论客户端产生重复ack的原因了。
社区增加了一个tcp_invalid_ratelimit 参数来规避这类问题:
https://mailarchive.ietf.org/arch/msg/tcpm/ThEU4LYfUY_9IBxn0CfMYUQRo-Q/
https://access.redhat.com/solutions/2725481
参考:
https://www.wisdomjobs.com/e-university/linux-tutorial-277/tcp-time-wait-state-1168.html
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。