话说kubernetes网络疑难杂症
大家好,我是来自什么值得买的前台业务架构师owenliang,近期正在推动业务向kubernetes迁移,在这个过程中遇到了一些棘手的问题,在此逐一向大家分享。
在真正进入到问题之前,我会先对我司kubernetes现状以及kuberntes原理做一些基本介绍。
kuberntes技术栈介绍
公司业务主要以PHP、JAVA、Python为主,调用方式均为短连接HTTP方式。
我们基于Django开发了一套Devops发布系统,能够方便的完成镜像构建与Kubernetes应用发布。
Kubernetes集群直接采用Ucloud提供的UK8S集群,版本为1.15,docker版本是18.09。
Kubernetes的Node采用的是4.19.0内核,16核32G配置,kube-proxy采用的是iptables模式(也许有人会问为什么不用IPVS?答:原因后面会讲),iptables版本是1.4.21。
UK8S实现了CNI,提供了underlay扁平网络结构,POD与VM的IP可以互通,网络性能优异。
Kuberntes应用通过LoadBalancer类型的Service向集群外暴露访问地址,Ucloud会为Service创建集群外的ULB负载均衡入口,出于复杂度&管理一致性的考虑,我们现在并没有使用Ingress收敛集群流量入口。
集群内采用Coredns解析域名,我们通过hosts插件配置域名解析service的ULB IP(也许有人会问为什么要解析到ULB而不是Cluster IP ,不怕流量绕出集群么?答:原因后面会讲),并通过template插件响应NXDOMAIN给IPV6查询请求,以此优化DNS查询速度。另外,我们在容器里启用了nscd本地DNS缓存,目的是减轻对Coredns的查询压力。
kubernetes网络原理
以UK8S集群为例,我们需要充分理解其网络原理,才能对网络问题加以分析与优化,这个过程涉及到比较综合的网络理解能力。
CNI原理
首先,UK8S自定义了CNI支持扁平网络结构,这意味着VM和POD的IP地址属于同一个网段,可以直接互通,并不需要通过Nodeport来做流量转发。
下面是宿主机node的IP地址:
Ucloud在SDN层面已经实现了对POD IP的路由,因此当集群外访问POD IP时,流量将会送入到POD所在Node的网卡。
流量进入node后,CNI已经接管了node路由表的管理,它在node上配置了如下的规则:
第1条规则即SDN默认网关10.42.0.1,第2条规则表示node所处网段是10.42.x.x,后续规则是该node上每一个POD的流量递送规则。
当请求POD IP 10.42.13.2的流量被SDN送入到该node后,经过本机路由表确认需要发往与POD相连的本机虚拟网卡(veth pair),从而流量进入到POD。
从POD内来看,其路由表将node作为默认网关:
因此POD回包的目标MAC地址会ARP解析为node的物理网卡地址,进而包经过SDN处理后再次回到node,进而走node路由表默认网关FOWARD送往SDN网关。(CNI并没有在node上建立docker bridge子网,而是直接依托于SDN的扁平网络做ARP响应)
kube-proxy原理
kubernetes基于iptables或者ipvs实现service的负载均衡功能,为了理解service必须理解iptables/ipvs,而理解iptables/ipvs则必须理解它们共同的底层内核机制netfilters。
这里我先发一张netfilters的流量处理流程,翻遍大家对照理解:
为了让大家理解netfilters的原理,我将以实际的3种场景来给大家做对照说明。
场景1)pod1(node1) -> service(cluster ip/ULB ip) -> pod2I(node2)
即集群内POD1通过service负载均衡调用到POD2。
在上面我分析过,POD1发往任意IP的流量其目标MAC地址都会指向node1,进而由node1完成流量转发。
node1收到POD1发来的流量,其目标MAC地址是node1,因此流量被欣然接收。
接着流量经过netfilter的PREROUTING阶段,在此阶段会经历nat表的规则计算,以便匹配流量是否发往cluster ip/ULB ip,我们看一下规则:
这是由node1上的kube-proxy下发的iptables规则,用于实现对访问service的地址改写。
我们继续跟随target跳转到下一个链上的规则:
我截取了某个service的2条规则:
- 第1条匹配目标IP地址是10.0.132.186的流量,这个IP其实是service的cluster ip,命中则跳转KUBE-SVC-NUA4LXHYE462BSW2链。(可见kube-proxy已经给这条规则写了注释)
- 第2条匹配目标IP地址是10.42.185.165的流量,这个IP其实是service的ULB ip,命中则跳转KUBE-FW-NUA4LXHYE462BSW2链。
如果我们POD1调用service使用的是Cluster ip则命中第1条规则,如果调用ULB ip则命中第2条规则,之前我也介绍过我司会将域名解析为ULB ip,所以我们就看一下第2条规则的后续行为:
这里又出现了3条规则,第1条规则是使用mark模块给这个连接打了1个标记位(我们暂时不进去看),重要的是第2条规则,它完成了目标IP地址改写(即DNAT):
因为我只有1个后端POD2,所以这里最终追溯到只有1条DNAT规则,将目标IP地址从ULB ip改为这个POD2的IP:10.42.145.194。
那么经过PREROUTING nat改写后,根据netfilters流程图将判断node路由表决定这个包的去向,目标POD2 IP会命中node的默认网关(当然也有概率POD2就在这个node上,那么就如同之前所说直接从虚拟网卡送入POD2),那么流量经过FORWARD阶段->POSTROUTING阶段,最终发往SDN默认网关,进而流入POD2所在的node2。
在流量离开POD1之前还会经历一次POSTROUTING的nat表规则,这里因为在KUBE-SERVICE链命中时对连接标记了mark,所以这里会做一次SNAT改写源IP地址为node1的IP,也就是说这个连接对于POD2来说其源地址并不是POD1而是node1。(实际上在扁平网络结构里,这一步SNAT我认为可以不做)
我们就不继续描述流量到达node2时候如何进入POD2的过程了,留给大家思考。
场景2)pod1(node1) -> redis/mysql(集群外)
即POD1请求部署在集群外虚拟机上的redis,mysql等。
POD1发出的流量经过POD内路由表送往默认网关node1,进入node1的PREROUTING nat阶段,因为其目标IP地址不属于任何service,所以没有进行任何地址改写,查找node1路由表决定发往SDN默认网关,进而走FORWARD和POSTROUTING离开node1,因为没有命中KUBE-SERVICE规则所以没有mark标记,所以POSTROUTING nat也并不会做SNAT,因此在redis/mysql看来源IP是我们的POD1 IP。
场景3)集群外VM -> ULB -> node1 -> pod2(node2)
该场景其实是场景1)的升级版,集群外应用调用ULB,由ULB负载均衡到kubernetes集群某node1的对应nodeport,进而经过node1转发给node2,进入pod2。
ULB会将流量转发到service的nodeport,也就是每个node都会为这个service暴露同一个端口。
在POSTROUTING阶段的KUBE-SERVICES规则末尾,其实有一条针对nodeport端口识别的规则链:
1 |
2 120 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL |
因为ULB转发给kubernetes集群时目标IP就是node的IP,端口是service对应nodeport端口,所以这条规则特意match了目标IP地址是local(也就是本机IP)的流量。
我选中的2行规则是针对该service的匹配,它识别nodeport是46794的service,然后给mark标记它是调用service的流量,然后走DNAT规则改写(我就不给大家展开了)。
另外,不知道是UK8S的CNI还是kube-proxy默认行为,会把ULB的IP地址也加到KUBE-SERVICE的匹配规则中,因此POD通过ULB调用另外1个POD,实际流量是直接被iptables改写的,并不会真的离开集群绕道ULB。(这回答了文章开头的一个问题)
至此基本的netfilters流程就差不多了,后面我们还会涉及到更深入的一点补充。
网络疑难杂症分析
对网络流量有了基本理解之后,我们再来分析问题就会清晰很多(如果对先前的内容不是很理解,可能对网络欠缺比较多,可以自行学习这些关键字:iptables、netfilters、路由表、交换机,网桥)。
先说一些结论性的东西:
问题基本集中在4层,主要是TCP,分析问题需要端到端完整的链路分析。
重点在于,流量在node转发过程中不会过TCP协议栈,只会经过node的netfilters,最终POD才是TCP协议栈的处理者。
问题定位与优化,需要从node和POD分别思考,找到瓶颈所在。
下面是我们遇到的N个主要问题与定位,如果大家对技术细节不关心只想找到解决方法,那么下面的信息对你也很重要。
node上conntrack大量INVALID
现象
压测时,在集群node上观察conntrack -S发现大量INVALID报文。
conntrack俗称流表,或者连接跟踪表,它属于netfilters框架,在之前的netfilters图片中大家可以看到在PREROUTING mangle之前以及OUTPUT mangle之前都会经过一个connection tracking的表。
conntrack是实现nat地址转换的灵魂,一个连接仅在首次经过netfilters链条时会计算nat表,一旦conntrack记录下这次的改写关系,后续无论是去程包还是回程包都是依据conntrack表进行改写关系的处理,不会再重复执行nat表中的DNAT/SNAT规则。
因此,在node的conntrack表中记录了大量的记录,每条记录包含2部分:
- 改写前的源IP和目标IP
- 改写后的源IP和目标IP
conntrack是一个内核里的hash表,使用conntrack命令行工具可以查看,给大家看一个例子:
所以说,node上的conntrack在跟踪流过它的每一条连接,包括其连接状态,改写关系,因此conntrack将是kubernetes优化的一个重点对象。
我无法在一篇文章中展开给大家讲述conntrack的原理,希望大家可以在额外学习一下。
分析过程
有问题先看看内核日志,因此输入dmesg,将会看到大量的如下日志:
nf_conntrack: table full, dropping packet
说明是压测期间,node上的conntrack哈希表打满了,我们只需要对其优化一下内核参数即可。
解决方法
在node上利用sysctl调整内核选项:
- nf_conntrack_max
- nf_conntrack_buckets
- nf_conntrack_tcp_timeout_time_wait
具体方法我在文章末尾统一给出,想了解这些参数自己谷歌一下学习一下conntrack吧。
POD内大量TCP重传
现象
压测时,在受压POD内观察到大量TCP重传,在受压POD所在node观察到大量conntrack INVALID报文,其他层面(IP层,链路层)网络指标无异常。
受压POD所在node上的其他应用均出现调用方超时,即某应用受压影响了其他应用的网络状况。
我们知道TCP重传是因为对方回复ACK慢导致的,也许是网络慢或者干脆丢失了。
分析过程
一开始出发点是研究node上conntrack大量INVALID报文的原因,通过打开conntrack INVALID日志可以看到大量报文INVALID,但是看不出原因。
因为压测链路从ULB开始,中间环节较多,还需要向下怀疑到云厂商的ULB/SDN之类的,所以定位时间比较长。
在POD内tcpdump抓包也没有明显线索,只是知道有大量重传,利用mtr探测中间链路没有丢包。
经过几天几夜的奋战,最终开始怀疑虚拟机等方面的底层问题,所以决定脱离kubernetes集群,单纯在2个虚拟机之间压测程序看一下是否同样有大量重传,结果还是有大量重传出现!
在逐渐升高压力的过程中,发现链路层PPS包量无法继续上升,虚拟机资源充足,此刻意识到是不是虚拟机包量限制,经过与运维&Ucloud确认,我司使用的机型包括UK8S的默认机型都是低包量的,其PPS瓶颈是6万左右/秒(in+out总共),真的是相当尴尬的结局。
解决方法
升级网络增强型的虚拟机,其PPS可以高达百万/千万。
替换机型后,我重新做了一下node之间的短连接压测极限CPS(connection per second):
每秒新建短连接:约10万次
每秒包量:约80万次
SDN限制包量是相当于中间链路丢包,对虚拟机上没有直接的链路层反馈,非常难查。
此时,POD内重传恢复正常,node上conntrack的INVALID问题也因此消失,可见中间链路丢包导致conntrack跟踪TCP状态出现了很多乱序问题,导致其统计了大量的无效包。
大家在虚拟机环境下一定要注意包量问题,购买更高PPS的主机来支持高并发场景。
node上conntrack少量insert failed报文
现象
压测时,宿主机conntrack -S报少量insert failed报文。
分析过程
谷歌了一下,这是已知问题。
这是linux内核做SNAT时的时序竞争bug,2个连接并发做SNAT时候有一定概率分配到同一个本地源端口,如果2个连接的目标IP+port恰好也一样,那么相当于出现了2个连接的5元祖(协议,源IP/PORT,目标IP/PORT)完全相同,将会导致连接插入/更新conntrack表失败,因此记录为insert fail。
解决方法
先说结论,这个问题在高压下也不严重,不一定非要解决。
如果要解决,需要做如下的事情:
- 升级iptables到1.6版本,它支持一个叫做–random-fully的端口选择策略,可以通过更加随机的方式选择端口,降低冲突率。
- 升级kubernetes集群到1.16版本,从该版本开始kube-proxy下发SNAT规则时才会携带–random-fully选项。
这个问题还是国内的一个团队发现与修复的,给大家一些参考链接:
- 国内团队的说明:https://mp.weixin.qq.com/s/VYBs8iqf0HsNg9WAxktzYQ
- 官方的issue:https://github.com/kubernetes/kubernetes/pull/78547
- kube-proxy代码:https://github.com/kubernetes/kubernetes/commits/master/pkg/proxy/iptables/proxier.go
node上conntrack大量INVALID(再次)
现象
后续我为了得到一些极限性能数据,在2个kubernetes node之间直接进行了短连接压测,目的是对node的内核参数进行优化。
我使用netperf的TCP_CRR模式压测2个node之间极限的短连接压力表现,这里并没有涉及到POD,但是因为node有kube-proxy下发的nat规则,因此conntrack依旧会跟踪我的压测连接,就在此时我发现受压node的conntrack -S报大量INVALID报文,与压力成正比关系。
分析过程
之前已经对conntrack进行了性能优化,唯一的区别就是之前都是利用工具压测HTTP接口,而这次是利用netperf工具压测TCP短连接,主要区别是工具的改变。
遇到conntrack INVALID问题可以通过打开log观察INVALID报文的信息来定位问题,所以我打开了conntrack的INVALID log,结果发现虽然INVALID狂涨,但一条日志都没有滚动,一度怀疑是不是我打开conntrack INVALID log的方式有问题,直到偶尔蹦出一条日志才让我打消了怀疑。
所以,我开始怀疑是不是conntrack在某些INVALID场景下不会打印调试日志呢?
于是我决定看一下linux kernel关于conntrack的源码。
也就是说,当收到新来的SYN时,conntrack表里还有上一次的TIMEWAIT记录,此时conntrack的逻辑时删除旧的插入新的,并且返回一个NF_REPEAT(repeat表示重复)的负值。
但是这个逻辑并没有打印日志,而其他返回负值的地方都会打印INVALID log。
2)调用该函数的逻辑则认为返回负值就是INVALID,所以会增加一次计数,但是对于返回-NF_REPEAT的情况,会goto跳到代码最上面重新处理一遍这个包:
但此时我还是有一点疑惑,为什么服务端会遇到TIME_WAIT,通常不都应该是客户端主动关闭连接吗?
于是我在2个node上使用netstat -tanlp|grep TIME_WAIT统计了一下TIME_WAIT连接数量,竟然发现受压node有大量TIME_WAIT,而netperf发压方几乎没有一条TIME_WAIT,这说明netperf的行为是服务端主动关闭连接!
我又在受压node上看了一下netstat -s,观察到海量的TIME_WAIT连接溢出被丢弃,这个问题也就基本定位了:
解决方法
暂时不需要解决,因为返回-NF_REPEAT的话conntrack会goto跳转二次进入处理方法,将该包最终按照正常包处理,最终不会是INVALID包,因此这个INVALID计数也算作conntrack实现的一个”bug”吧。
滚动发布时调用方超时
在最初,我们kubernetess集群采用的是IPVS模式,因此也遇到了一个大坑。
现象
我们使用deployment发布应用,但是发现每次滚动发布都会引发调用方超时,匪夷所思。
分析过程
我首先详细复习了一下Deployment和Service的联动流程,明确要做如下几点调整:
- POD一定要有livenessProbe和readinessProbe两个健康检查,否则service就会把流量打到一个还没准备好的新POD上。
- 程序一定要有优雅退出逻辑,kubernetes会首先从service中摘除旧POD的转发规则,然后才会给POD发送SIGTERM退出信号,此时不会有新流量进入,但是程序必须把现有连接服务完毕再退出。
- 一定要给POD足够的优雅退出时间,通过YAML中设置terminationGracePeriodSeconds实现,建议给比较大的时间窗口(比如30秒),避免流量较多的时候处理残余流量耗时较长,因为一旦超时就会SIGKILL掉程序,导致部分调用就失败了。
- kubernetes默认行为是给程序发送SIGTERM信号,然后等待terminationGracePeriodSeconds秒后如果程序还没退出则发送SGIKILL。然而NGINX的优雅退出信号并不是SIGTERM,所以我们可能需要借助preStop的Hook,自定义向Nginx发送对应的优雅退出信号,这样kubernetes就不会发送默认的SIGTERM信号了,而且terminationGracePeriodSeconds依旧有效。
做了如上调整后,滚动发布仍旧引发了调用方一连串的报错,QPS越高则越严重。
于是重新思考了一下,要么是上线新POD引发超时,要么是下线旧POD引发的超时,而滚动发布又同时在做下线&上线,怎么区分定位一下呢?
于是决定先将POD从5个扩容程10个,发现没有报错,这说明新POD的健康检查是有效的,流量的进入时机也没问题。而从10个缩为5个POD则引发了报错,因此缩小范围是下线POD引起的。
可是kubernetes下线POD的过程是先摘除service转发,才发送SIGTERM信号给POD,程序也已经是优雅退出的,为啥会有问题呢?
于是开始怀疑是不是其他问题,所以首先怀疑到一个kubernetes工作机制:
deployment会标记1个POD为terminating状态,然后发生了2个并发的事情:
- kubelet收到etcd变化事件,会立即给POD发送SIGTERM,程序开始优雅退出。
- kube-proxy收到etcd变化事件,,会立即摘除node上对应pod的iptables转发规则。
kubelet和kube-proxy感知到etcd事件的先后存在时间差,如果先发送SIGTERM再摘除规则就会导致部分新连接在竞争窗口内受损,因为SIGTERM已经让程序关闭监听了。
为了验证这个想法,我们在preStop里首先sleep了2秒再发送优雅退出信号:
– lifecycle:
preStop:
exec:
command:
– /bin/sh
– -c
– sleep 2;pkill xxx;
sleep的目的是为了让iptables规则先下线,再停止程序监听。
然而,这个也没有修复问题。
最终,还是谷歌帮助了我们,明确这是IPVS下线规则时候的一个bug,会导致新连接发送到已经下线的POD内。
解决方法
具体原因大家就看看官方issue吧:https://github.com/kubernetes/kubernetes/issues/81775。
目前有2个解决方案:
- 忍着,每次滚动发布都要报错,等待未来的官方方案。
- 替换iptables方案。
我们选择了后者,因为涉及到HPA弹性的需求,我们不可能容忍每次伸缩都引发调用方超时。
iptables我们维护起来更加熟悉,它在service规模超过1000+的情况下才会出现性能下降,对我们来说也足够了。
其他影响不大的问题-1(不明显的问题,但可能帮助到大家的场景)
还是与conntrack有关,下面的选项需要生效到所有node:
/proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_be_liberal=1
它令conntrack在跟踪TCP连接时对TCP滑动窗口的sequence校验宽松一些,否则TCP包乱序到达时conntrack会认为包超出了自己已知的窗口范围,会将包标记为INVALID,进而导致被kubernetes下发的一条INVALID DROP规则丢弃,这样会引发调用方超时重传。
大家感兴趣可以看一下官方说明:https://kubernetes.io/blog/2019/03/29/kube-proxy-subtleties-debugging-an-intermittent-connection-reset/
其他影响不大的问题-2(不明显的问题,但可能帮助到大家的场景)
ucloud实现的CNI在POD里下发了一条没有用的SNAT规则,这将会触发POD内conntrack机制,导致POD内也要面临conntrack性能问题:
我们目前是通过privileged特权的init-container,在POD启动之前执行了iptables -t nat -F删除了该规则,后续ucloud也会修正这个历史遗留问题。
优化参数
上面讨论了那么多问题,最终其实就是几行优化参数,吃快餐的同学可以直接拿走。
node调参
node瓶颈是conntrack,它跟踪所有流量,但自身又不处理协议栈:
1 2 3 4 5 6 |
# conntrack优化 net.netfilter.nf_conntrack_tcp_be_liberal = 1 net.netfilter.nf_conntrack_tcp_loose = 1 net.netfilter.nf_conntrack_max = 3200000 net.netfilter.nf_conntrack_buckets = 1600512 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30 |
pod调参
pod内conntrack不开启,主要瓶颈在TCP协议栈处理短连接部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# socket buffer优化(好像意义不大) net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.tcp_rmem = 4096 87380 6291456 net.ipv4.tcp_mem = 381462 508616 762924 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 # timewait相关优化 net.ipv4.tcp_max_tw_buckets = 131072 # 这个优化意义不大 net.ipv4.tcp_timestamps = 1 net.ipv4.tcp_tw_reuse = 1 # 仅对客户端有效果,可以复用TIME_WAIT端口 net.ipv4.tcp_tw_recycle=0 # 仅存在低版本内核中,需要关闭,因为在NAT变换IP的场景下用时间戳做校验会有回退问题,需禁用 net.ipv4.ip_local_port_range="5120 65000" # 端口范围 net.ipv4.tcp_fin_timeout=30 # 缩短TIME_WAIT时间,加速端口回收 # 握手队列相关优化 net.ipv4.tcp_max_syn_backlog = 10240 net.core.somaxconn = 10240 net.ipv4.tcp_syncookies = 1 |
一键node/port通用
如果想快速验证一下所有参数,在node以及privileged的POD内生效如下命令即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/bin/bash sysctl -w net.netfilter.nf_conntrack_tcp_be_liberal=1 sysctl -w net.netfilter.nf_conntrack_tcp_loose=1 sysctl -w net.netfilter.nf_conntrack_max=3200000 sysctl -w net.netfilter.nf_conntrack_buckets=1600512 sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=30 sysctl -w net.ipv4.tcp_timestamps=1 sysctl -w net.ipv4.tcp_tw_reuse = 1 sysctl -w net.ipv4.tcp_tw_recycle=0 # 高版本内核已经删除了该垃圾选项 sysctl -w net.ipv4.ip_local_port_range="5120 65000" net.ipv4.tcp_fin_timeout=30 sysctl -w net.ipv4.tcp_max_syn_backlog=10240 sysctl -w net.core.somaxconn=10240 sysctl -w net.ipv4.tcp_syncookies=1 |
用到的工具
输出conntrack INVALID日志
生效如下命令:
1 2 3 4 5 |
iptables -t mangle -A FORWARD -m conntrack --ctstate INVALID -j LOG --log-prefix 'FORWARD -> ' --log-level=debug --log-tcp-sequence --log-tcp-options --log-ip-options iptables -t mangle -A INPUT -m conntrack --ctstate INVALID -j LOG --log-prefix 'INPUT -> ' --log-level=debug --log-tcp-sequence --log-tcp-options --log-ip-options iptables -t mangle -A OUTPUT -m conntrack --ctstate INVALID -j LOG --log-prefix 'OUTPUT -> ' --log-level=debug --log-tcp-sequence --log-tcp-options --log-ip-options iptables -t mangle -A PREROUTING -m conntrack --ctstate INVALID -j LOG --log-prefix 'PREROUTING -> ' --log-level=debug --log-tcp-sequence --log-tcp-options --log-ip-options iptables -t mangle -A POSTROUTING -m conntrack --ctstate INVALID -j LOG --log-prefix 'POSTROUTING -> ' --log-level=debug --log-tcp-sequence --log-tcp-options --log-ip-options |
观察dmesg。
netperf压测
服务端启动命令(会启动到后台,会打印一条warinng日志没关系):
netserver
客户端短连接压测(-l持续秒数,-m请求应答的大小):
netperf -t TCP_CRR -H 目标服务器 -l 1200 — -m50,100 &
需要启动多个netperf客户端到后台,每一个客户端会令netserver服务端启动一个独立端口,避免单个TCP监听队列的瓶颈问题,从而可以试探出整机的TCP短连接极限。
网络分析
随便想了一些常用的命令:
- netstat -s
- conntrack -S
- sar -n ETCP 1
- sar -n DEV 1
- ifconfig
- mtu
- tcpdump
- netperf
- iptables
- top
- vmstat
- iostat
- iotop
- ss
- netstat -tanlp
结束语
问题遇到的越早越好,如果你没有遇到问题,说明你的问题更大。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~

1