解决kubernetes网络在高负载下抖动

背景

业务团队反馈说k8s的网络在高压下不稳定。平时都很正常,但是高压下延迟会升很高。我们的基础设施都很新,操作系统和k8s以及CNI都是当前的最新版。

排查过程

追溯历史

先看基础指标,查看node-exporter的指标,看到虽然机器的确高压,但是也没到100%占用。再看cilium的连接信息,虽然有大量短连接占用资源,但是也不至于瓶颈。历史指标看不出特别奇怪的,再简单地测试工况下,网络质量都很好,想办法制造现场重现。

重现

让业务团队配合提供高压环境, 然后我们持续监控网络的质量,在不确定是哪边出问题的情况下,只能添加更多的实验组。检测方式就采取最简单的长ping,我们都在这儿持续盯着,抖动的时候特征也比较明显,所以这儿也不需要更复杂的工具。 先后进行过两次测试。

第一次测试

实验组 特征
my pod -> public IP 稳定
业务 pod -> public IP 抖动

当时也感觉挺奇怪,也怀疑是否是不同的容器的cgroup的CPU throttling,当然也可能是抖动没有被观测到,毕竟有很大的随机性。

第二次测试

第二次测试全程在高负载环境下运行,果然复现了抖动,大多数时候延迟都< 1ms, 但偶尔就是会飘高到数十毫秒。 第二次测试着重观察了 pod -> pod 的情况,并且开始怀疑节点差异,于是手工构造了my pod -> pod on another node 实验组用于对照,果然发现,跨节点的pod就会抖动。

实验组 特征
my pod -> public IP 抖动
业务 pod -> public IP 抖动
pod -> pod on same node 稳定
pod -> pod on another node 抖动

为了避免其他的干扰, 直接进入node,在node上ping pod和其他node, 都复现了抖动。

实验组 特征
node -> pod on another node 抖动
node -> another node 抖动

这儿就能确定,网络抖动发生在节点网络上,与CNI都无关了。进一步发现只要是高压的节点,无论访问什么都抖动。

进一步排查节点之间的网络问题。我们的k8s节点是PVE上的虚拟机,节点间的网络就有比较长: k8s node -> bridge on PVE -> switch -> bridge on another PVE -> another k8s node。 为了排查瓶颈是否出在switch,虚拟网桥还是k8s节点上,设计了三组实验。通过k8s node -> bridge on PVE -> vm, 这样可以避开硬件干扰。再测试同一个PVE上的其他vm之间的网络,也就是other vm -> another vm on the same PVE,这样可以测试虚拟网桥。再测试跨物理机的其他vm之间的网络,也就是other vm -> another vm on the another PVE, 可以测试硬件switch。

实验组 特征
k8s node -> vm on the same PVE 抖动
other vm -> another vm on the same PVE 稳定
other vm -> another vm on the another PVE 稳定

结果显示,other vm -> another vm on the another PVE 跨物理机稳定,问题不在硬件交换机上。发现other vm -> another vm on the same PVE的网络畅通,虚拟网桥不太可能有瓶颈,同一个PVE上的其他节点之间都没问题。 k8s node -> vm on the same PVE的网络会抖动,而这一过程中的网络完全在同一台物理机上。最终得出只是k8s node自身的网络有问题。

出问题的节点虽然压力挺高,但是CPU使用率没到100%,怀疑是CPU的网络中断不均衡。

处理IRQ均衡

# cat /proc/softirqs | grep NET_
      NET_TX:       0                38  ...
      NET_RX:       0        9746189470  ...

看到NET_TX分布极不均衡, 我们的node有上百个CPU,网络操作却集中于一两个CPU上。虽然消耗资源的是soft irq, 但是背后的根本原因是hard irq, 因为软中断默认在硬中断所在的CPU上运行。

# cat /proc/interrupts
           CPU0       CPU1        ...
 34:          0          0        ...  PCI-MSIX-0000:06:xx.0   0-edge      virtio1-config
 35:          0          0        ...  PCI-MSIX-0000:06:xx.0   1-edge      virtio1-input.0
 36:          0 9835320695        ...  PCI-MSIX-0000:06:xx.0   2-edge      virtio1-output.0

看到网卡设备的IRQ基本集中于某个CPU. 而MSIX会把IRQ绑定到某个核心上,所以要进行CPU的负载均衡就需要增加IRQ。确认一下设备.

# lspci -s 06:xx.0
06:xx.0 Ethernet controller: Red Hat, Inc. Virtio network device

是virtio网卡设备,看一下网卡的队列

# ethtool -l eth0
Channel parameters for eth0:
...
Current hardware settings:
RX:		n/a
TX:		n/a
Other:		n/a
Combined:	1

网卡只有一个队列,怪不得压力都到那一两个CPU上了。解决方式也很简单,将网卡配置上Multiqueue即可。配置之后

# ethtool -l eth0
...
Current hardware settings:
RX:		n/a
TX:		n/a
Other:		n/a
Combined:	8

大功告成

修改PVE上node的virtio虚拟网卡,根据node的CPU数量,设置多个队列分散网络压力。