解决网卡丢包问题,以及丢包问题解决后系统网络还是慢的问题
背景
线上的K8S集群有一台节点,装了Istio的遥测组件,这个组件,之前在哪个节点哪个节点就出问题。出现的问题主要体现在几个点:
①:服务器间歇性 NodeNotReady
②:服务器丢包极为严重
③:服务器网络非常慢,以至于在服务器上执行 curl youku.com 都要响应很久。
Istio的遥测组件,是一个服务,由Istio的Ingress以及SideCar发送数据给遥测服务,而且发送的是UDP的包,量非常非常大,能达到 500Mbps。UDP包量大应该是导致了网卡丢包以及网络缓慢的主要问题,所以,要解决这个问题,就需要坐下Linux网卡收包层面的优化。开始优化前,需要先明确一下步骤:
- 几个基本概念
- 网卡相关的基础知识
- 网卡收包问题排查
几个基本概念
NIC:NIC 是 Network Interface Card,即网络接口卡,也就是网卡。
IRQ:传统上,NIC会生成中断请求(IRQ),指示数据已到达。IRQ也是有多种的,有三种常见类型的IRQ:MSI-X,MSI和传统IRQ。
网卡相关的基础知识
网卡的队列数量
查看和设置网卡使用的队列数量(注意,是队列的数量,不是队列本身的大小,仅针对收包)
ethtool -l em2
Channel parameters for em2:
Pre-set maximums:
RX: 4
TX: 4
Other: 0
Combined: 0
Current hardware settings:
RX: 4
TX: 1
Other: 0
Combined: 0
从上面看,队列有几种:RX、TX、Combined 等。有些网卡,只支持 Combined 队列(发送和接收公用,这种叫做组合队列),有些可以单独设置 RX 和 TX 队列(RX 就是接收队列,TX 就是发送队列)。
设置网卡队列的大小
ethtool -L eth0 rx 8
注意:
①:并非所有网卡都支持这个操作命令,这得看网卡驱动是否支持 ethtool get_channels 操作,一般支持这个操作的是物理网卡,虚拟网卡、虚拟网桥一般不支持。
②:设置队列操作,会导致网卡关闭后再次启动,所以和这个网卡相关的链接就会关闭。
网卡的队列大小
查看和设置网卡的队列大小(注意是大小,不是数量)
查看网卡队列大小
ethtool -g em2
这个设置说明,网卡本身的队列大小,支持最高 2047,目前的设置是 200。
设置网卡队列大小
ethtool -G eth0 rx 4096
注意:
①:并不是所有网卡都支持通过 ethtool 查看和修改网卡队列大小。
②:这个操作,同样会关闭和启动网卡,所以,和这个网卡相关的连接也会中断。
网卡的队列权重
调整网卡队列的处理权重(仅针对收包)
查看网卡队列的权重
ethtool -x em2
说明:如上,有4个队列,队列编号是:0、1、2、3。散列值为2的数据,要发往2号队列,散列值为3的,要发往3号队列,散列值为8的,要发往0号队列。
设置网卡队列权重
// 大写 X 为设置
// 设置权重均分,equal
ethtool -X eth0 equal 2
// 单独设置权重
// 下面这个操作,是为网卡的0号队列设置权重为6,网卡的1号队列设置权重为2。这样一来,队列0会处理更多的数据
ethtool -X eth0 weight 6 2
针对网卡队列的网卡收包的哈希策略
调整网卡上收包(RX)过程用于哈希的字段(仅针对收包)
首先,对数据包哈希的意义,是用于放入不同的队列。哈希可以选择数据包中的不同字段来哈希。
查看用于哈希的字段:
// 小写 n 查看
ethtool -n eth0 rx-flow-hash udp4
上面的输出说明,计算接受UDP包的哈希字段,是 IP地址的源地址和目的地址。
配置用于哈希的字段:
// 大写的 N 为设置
ethtool -N eth0 rx-flow-hash udp4 sdfn
其中 sdfn 是一个字符串,可以通过 man ethtool 来查看。
网卡 RX的 ntuple 过滤功能(仅针对收包)
有一些网卡,是支持“ntuple过滤”的功能。这个功能用于过滤硬件中的传入网络数据并将其排入特定的RX队列。这个可以做到很多过滤特性,比如:
①:用户可以指定发往特定端口的TCP数据包应该发送到RX队列1。换句话说,让发往80端口的是数据包,指定到某个队列。
②:让发往某个端口的数据,由某个CPU处理。
这个功能,有不同的叫法,比如说,Intel 的网卡管这个叫做 Internet Ethernet Flow Director。
举一个此功能的使用场景:提供Web服务的响应效率,步骤如下:
①:让80上运行的Web服务器固定在CPU 2上运行(也就是对进程的CPU绑定)。
②:RX队列(收包队列)的IRQ(请求中断)被分配为由CPU 2处理。也就是,让某个收包队列,由某个CPU处理。
③:发往端口80的TCP流量使用ntuple“过滤”到CPU 2。
正题,查看网卡是否支持 ntuple 过滤:
// 小写 k 查看
ethtool -k eth1
如上,nutple-filters 为 off ,表示 ntuple 过滤功能没有开启。
开启 nutple 过滤:
// 大写 K 设置
ethtool -K eth0 ntuple on
查看 ntuple 过滤规则:
// 小写 u 查看规则
ethtool -u eth0
如上,Total 0 rules 表示此设备没有 ntuple 过滤规则。
添加 ntuple 过滤规则:
// 大写 U 设置规则// 添加 ntuple 过滤规则,让目标端口80的TCP流发送到RX队列2
ethtool -U eth0 flow-type tcp4 dst-port 80 action 2
注意:可以使用ntuple过滤在硬件级别丢弃特定流的数据包。比如,丢弃特定IP地址的传入流量。
软中断(softirq)
中断简介:
中断:外部设备,需要通过中断机制(中断信号),来通知CPU处理响应的任务。这样CPU就响应中断信号对应的处理动作即可。避免CPU盲目等待某一个任务而无法处理其他任务的情况。中断是由外部设备引起的,计算机能够接受的外部信号,非常的有限,因为不可能为每一个外部的设备都定义好信号的格式,所以,计算机给外部信号,只约定了一种信号格式,这种信号就是中断信号,这套信号的接受和处理共同组成中断机制。
计算机的中断,可以看做一个过程,它在看电视,然后水烧开了(中断信号),基于计算机去处理烧水的事情。但是计算机不具备连续性,处理完了烧水的事儿,就不知道怎么回到原来的过程再去看电视。计算机要完成上面的看似具备逻辑性的事情,就需要硬件+软件协同,实现处理中断的全部过程。
中断需要尽可能的快,软中断(softirq)的意义就是,处理中断没有处理完的事情。
举例来说:比如在网卡的驱动程序里,在中断环境里,只是把包放到一个队列里,然后由软中断来把包传递给进程,或者转发包等。
Linux 里边的软中断不可重入,在一个CPU上,一次只能有一个软中断在执行。多个CPU的话,可以有多个软中断。
查看当前机器的系统中断
// 第一步
top
// 第二步
按1
// si 列,就是系统中断,si 就是 softirqs
查看软中断统计信息:
cat /proc/softirqs
可以看到,不同的 CPU 处理关于网卡收包的软中断次数不一样(速度不一样),并不均匀,这说明有其他的因素,影响了这个速度。
IRQ coalescing(中断合并)
这个东西,允许中断前缓存数据包,如果简单来说,一个包就会触发中断,那么它可以做到10个包触发一次中断,有效降低中断次数,提高系统响应效率。
启用自适应RX / TX IRQ合并的结果是,当数据包速率较低时,将调整中断传送以改善延迟,并在数据包速率较高时提高吞吐量。
查看网卡的中断合并是否已开启
ethtool -c eth0
其中,Adaptive 显示了当前 RX 和 TX 是否开启了中断合并。
设置中断合并
ethtool -C eth0 adaptive-rx on
IRQ(中断)的CPU亲和性
对于支持多队列的网卡来说,可以尝试让特定的CPU来处理NIC生成的中断。通过设置特定的CPU,可以分割将用于处理哪些IRQ的CPU。
不过,如果要调整IRQ亲和力,首先应该检查是否运行 irqbalance 守护程序。 这个守护进程尝试自动平衡IRQ到CPU,所以,在开启了 irqbalance 的情况下,它可能会覆盖你原有的设置。 如果正在运行 irqbalance,则应该禁用 irqbalance 或将 –banirq 与 IRQBALANCE_BANNED_CPUS 结合使用,或者,让 irqbalance 知道它不应该触及你想要自己分配的一组 IRQ 和 CPU 。
查看是否开启了 irqbalance 服务
systemctl status irqbalance
如果没有开启,可以将其开启,开启后,网卡硬中断所绑定的CPU,将由 irqbalance 来自动调配。
netdev_max_backlog
inux 内核从网卡驱动中读取报文后可以缓存的报文数量,默认是 1000。积压在 backlog 的数据包,就是由软中断进行处理的。
调整方式:
sysctl -w net.core.netdev_max_backlog=2000
注意:检查你网卡的驱动程序,如果它调用 netif_receive_skb 并且你没有使用 RPS,那么增加 netdev_max_backlog 将不会产生任何性能提升,因为没有数据会进入 input_pkt_queue。
套接字接收队列内存
可以通过下面的方式,调整 socket 接收队列的缓冲区大小
// 8388608 就是 8M 左右, 26214400 就是25M 左右
sysctl -w net.core.rmem_max=8388608
sk_rcvbuf 从 net.core.rmem_default 值开始,所以,也可以通过设置sysctl来调整 net.core.rmem_default
sysctl -w net.core.rmem_default=8388608
我们的应用程序,也可以调用 setsockopt 的时候,配置 SO_RCVBUF 来决定套接字接收队列的内存大小,不过,你最大能设置的值,无法超出 rmem_max (注意:这个说法不完全准确,因为可以通过调用 setsockopt 并传递 SO_RCVBUFFORCE 来覆盖net.core.rmem_max限制,但是,需要你具备 CAP_NET_ADMIN 的能力)
网卡收包排查
先解决丢包问题
要排查问题,说明已经存在问题,存在的问题比如,服务失败率变高(HTTP服务接口失败率高)->进而发现网卡流量太大->进而发现网卡丢包。
先从丢包问题入手,因为有些场景下,解决丢包问题即是第一步,可能这个问题解决了,服务响应问题也就解决了。
查看丢包问题
ifconfig eth0
查看其中的 dropped 是否为0,以及是否递增。
通过下面几种操作,往往可以解决丢包问题:
// 增大socket缓冲区上限
sysctl -w net.core.rmem_max=8388608
// 增大socket缓冲区默认大小
sysctl -w net.core.rmem_default=8388608
// 调整 Linux 系统的网卡缓冲区大小
sysctl -w net.core.netdev_max_backlog=2000
可以通过监控系统进行观测,或者ifconfig进行查看。
虽然网卡不丢包了,系统还是慢,甚至一个 curl youku.com 都能明显感觉卡顿,这种情况,就需要进一步探究。
排查网卡软中断过高的问题
首先,网卡相关的事件,是会发生硬中断和软中断。
①:硬中断,网卡收包就会触发。
②:软中断,网卡收包后,系统将其后续处理,比如发往进程,或者进行包转发等。
软中断,一般是一个或者多个CPU来处理,现在很多网卡都是支持多队列网卡,这种情况,每一个队列都可能绑定一个CPU做中断处理。所以,我们先要确认一下,我们在使用的网卡,有几个队列,每个队列由哪个CPU在处理
// 查看网卡 RX 的队列数量
ethtool -l em1
// 下面是输出,说明收包丢列(RX)有4个。
Channel parameters for em1:
Pre-set maximums:
RX: 4
TX: 4
Other: 0
Combined: 0
Current hardware settings:
RX: 4
TX: 1
Other: 0
Combined: 0
// 查看系统中断统计
cat /proc/interrupts
找到对应的网卡,比如 eth0 网卡对应的行(有可能多行,因为网卡可能使用的是多队列),找出来其中中断次数多的那一列对应的CPU,比如我找到的是:CPU9、CPU21、CPU58、CPU68
然后,我们需要验证,是不是这几个CPU在处理中断,并且繁忙
// 执行 top 命令,然后按1,看一下 si 列的值,是不是被动较大,或者一直比较高
top
// 基本上能看到,就是那几个CPU的si列,一直占用比较高
后续的处理,分2个方向:
①:为硬中断绑定CPU
②:为软中断分散CPU压力
为硬中断绑定CPU(不推荐)
首先,这个操作,并不推荐。因为硬中断和软中断的目的不一样,上边说过了,我们可以先从修改软中断的修改看效果,如果Ok,那就没有必要做硬中断的修改了。最主要的是,修改硬中断,需要停止系统的某个服务,而修改软中断不需要。
下面是修改硬中断的CPU绑定操作
给网卡绑定CPU了,要么手动,要么自动,要么两者结合。之所以这么说,是因为,系统可能存在 irqbalance 服务,这个服务起来后,会自动处理中断和CPU的绑定关系。我们如果手动修改,比较麻烦,手动修改的值,会被 irqbalance 冲掉。所以,我们得关闭这个服务,才能
好了,我们先执行手动操作,执行手动操作之前,先要知道,我们要操作谁。Linux 中,关于中断与CPU亲和性绑定关系的,在 proc/irq/ 目录。每一个子目录,都是一个 中断信号的编号。所以,我们要知道网卡中断对应的编号是什么,并进去修改。所以,第一步,我们如何知道网卡软中断的信号编号是什么
首先,停止 irqbalance 服务
systemctl stop irqbalance
然后,查看网卡对应的中断信号
// 还是这个操作,看统计,找到网卡,最前面的那一列,就是中断信号!
cat /proc/interrupts
比如,我们拿到的是 120、121、122、123 。
然后,修改中断信号和CPU的绑定关系
echo 9 > /proc/irq/120/smp_affinity_list
echo 10 > /proc/irq/121/smp_affinity_list
echo 11 > /proc/irq/122/smp_affinity_list
echo 12 > /proc/irq/123/smp_affinity_list
为软中断分散CPU压力
修改硬中断其实并不够彻底,因为硬中的处理虽然分散到了上面几个核,但是硬中断之后,软中断才能开始工作,且,也在同一个核上,一个核即做硬中断处理,也做软中断处理,还是会大大降低硬中断和软中断的处理速度。
通过设置 rps 以及 rfs ,可以将软中断,均匀分配到很多个CPU的核上。
// 让em1每个队列的处理,均分到32个CPU核上。
echo ffffffff > /sys/class/net/em1/queues/rx-0/rps_cpus
echo ffffffff > /sys/class/net/em1/queues/rx-1/rps_cpus
echo ffffffff > /sys/class/net/em1/queues/rx-2/rps_cpus
echo ffffffff > /sys/class/net/em1/queues/rx-3/rps_cpus
解释:
①:为什么是8个f,一个f,是4个1,1111,8个f,就是32个1,就是代表32个核。
②:为什么是 rx-0 到 rx-4,因为上面说过了,这个网卡,有4个队列。
③:为什么不设置更多个核,是因为设置多了,系统会终止这个操作:“bash: echo: write error: Value too large for defined data type”说明:
收尾
经过上面的操作,网卡丢包问题基本解决,另外,系统网络缓慢问题也解决。
上面的过程,也并非所有的内核参数都尝试做了调整,也有一些并没有调,总结来说,对 netdev_max_backlog 以及 rmem_max 的调整,可以有效的减少丢包。通过调节软中断并对其分散到多个CPU来减少CPU的中断压力,可以有效提高系统网络的RX效率。
另外,前面提到,有些网卡支持 “中断合并” 特性,这个特定对于缓解中断压力有好处,并且默认情况下这个特性是关闭的,对这个服务器的网卡,设置 “中断合并” 操作,并没有生效,设置完成后也不报错,通过 ethtool -C eth0 也依然看到是 Off 状态。
文章参考
- https://blog.packagecloud.io/eng/2016/06/22/monitoring-tuning-linux-networking-stack-receiving-data/#irqs
- http://ju.outofmemory.cn/entry/104879
- https://www.cnblogs.com/shenlinken/p/6657931.html?utm_source=itdadao&utm_medium=referral