引言
Kubernetes要求集群中的每个pod都具有唯一的,可路由的IP,Kubernetes本身不会分配IP,而是将任务留给第三方解决方案。
本次课题的目标是找到具有最低延迟,最高吞吐量和最低安装成本的解决方案。 由于我的负载对延迟敏感,因此我的目的是在相对较高的网络利用率下测量较高的百分比延迟,特别是最大负载的30%到50%之间的性能,因为我认为这是最能代表非过载系统的最常见用例。
竞争对手们
Docker with --net=host
这是我们的一个参考设置,所有其他竞争对手都会与此设置进行比较。--net=host选项表示容器继承其主机的IP,即不涉及容器网络。
根据先验原则,没有任何容器网络方案比其他的会有更好的表现,这就是为什么使用此设置作为参考的原因。
Flannel
Flannel是一个虚拟网络解决方案,由CoreOS维护,它是经过充分验证可立即投入生产的解决方案,因此安装成本最低。将带有Flannel的worker节点添加到k8s群集时,Flannel会做三件事:
- 使用etcd为新worker节点分配一个子网
- 在机器上创建虚拟网桥接口(称为docker0网桥)
- 设置数据包转发后端:
aws-vpc
在Amazon AWS实例表中注册机器子网,此表的记录数限制为50,即如果将Flannel布与aws-vpc一起使 用,则集群的节点数量不能超过50,此外,此后端仅适用于Amazon的AWS。
host-gw
通过远程主机IP创建到子网的IP路由。 需要运行Flannel的主机之间2层直连。
vxlan
创建一个虚拟VXLAN接口
由于Flannel使用网桥接口转发数据包,因此流量从一个容器到另一个容器的过程中,每个数据包都会经过两个网络栈。
IPvlan
IPvlan是Linux内核中的驱动程序,能够创建具有唯一IP的虚拟接口,而不必使用网桥接口。
要将IP分配给具有IPvlan的容器,你必须:
- 创建一个完全没有网络接口的容器
- 在默认网络名称空间中创建ipvlan接口
- 将此接口移动到容器的网络命名空间中
IPvlan是一个相对较新的解决方案,因此没有现成的工具可以自动执行此过程。 这使得很难在许多服务器和容器中部署IPvlan,即部署成本很高。
但是,IPvlan不需要桥接接口,而是直接将数据包从NIC转发到虚拟接口,因此期望它的性能优于Flannel。
负载测试方案
对于每个竞争对手,执行以下步骤:
- 在两台物理机上设置网络
- 在一台机器上的容器中运行tcpkali,让其以恒定速率发送请求
- 在另一台计算机上的容器中运行Nginx,让它以固定大小的文件响应
- 捕获系统指标和tcpkali结果
我们以每秒50,000至450,000个请求(RPS)的请求速率运行基准。
在每次请求时,Nginx都会响应一个固定大小的静态文件:350 B(内容为100 B,标题为250 B)或4 KB。
测试结果
- 结果显示IPvlan有着最低的延迟和最高的最大吞吐量,host-gw和aws-vpc的Flannel紧随其后,但是host-gw在最大负载下显示的结果更好。
- 使用vxlan的Flannel在所有测试中均显示最差的结果,但是,我怀疑99.999%ile测试结果的异常是由一个bug引起的。
- 4 KB响应的结果类似于350 B响应的结果,但有两个明显的区别:最大的RPS要低得多,因为在4 KB响应下仅需约270kRPS即可完全加载10 Gbps NIC。
- 吞吐量测试中IPvlan无限接近--net=host。
我们当前的选择是使用host-gw模式的Flannel,它没有太多的依赖关系(例如,不需要AWS或新的Linux版本),与IPvlan相比,它易于部署,并且具有足够的性能特性,IPvlan是备选方案,如果某个时候Flannel添加了IPvlan支持,我们将会切换到它。
尽管aws-vpc的性能比host-gw稍好,但其50台计算机节点的局限性以及将其硬连线到Amazon的AWS的事实对我们来说都是一个障碍。
50,000 RPS, 350 B
在每秒50,000个的请求速度下,所有候选者都表现出令人满意的性能,主要趋势是:IPVlan表现最佳,host-gw和aws-vpc紧随其后,vxlan表现最差。
150,000 RPS, 350 B
IPvlan略优于host-gw和aws-vpc,但是在99.99 %ile这个指标上是最差的,host-gw的性能略优于aws-vpc。
250,000 RPS, 350 B
这种负载在生产中也很常见,因此这些结果尤为重要。
IPvlan再次显示出最佳性能,但是在99.99和99.999 %ile这两个指标上aws-vpc更胜一筹,host-gw在95和99% %ile上优于aws-vpc。
350,000 RPS, 350 B
在大多数情况下,延迟接近250,000 RPS(350 B情况),但在99.5 %ile之后迅速增长,这意味着我们已经接近最大RPS。
450,000 RPS, 350 B
这是理论上能产生合理结果的最大RPS,IPvlan再次领先,延迟比--net-host差30%左右:
有趣的是,host-gw的性能比aws-vpc更好:
500,000 RPS, 350 B
在500,000 RPS下,只有IPvlan仍然有效,表现甚至优于--net=host,但是延迟是如此之高,以至于我们认为这对延迟敏感的程序毫无用处。
50k RPS, 4 KB
较大的响应会造成较高的网络使用率,但测试结果看起来与较小的响应几乎相同:
150k RPS, 4 KB
Host-gw具有令人惊讶的99.999%ile,对于较低的百分位数也显示出良好的结果。
250k RPS, 4 KB
这是在大的请求响应下的最大RPS,与小的请求响应测试用例不同,aws-vpc的性能比host-gw好得多,Vxlan再次从图中排除。
测试环境
背景
为了理解本文并重现我们的测试环境,你应该熟悉有关高性能的基础知识。
这些文章提供了有关该主题的有用见解:
- 如何每秒接收一百万个数据包 by CloudFlare
- 10Gbps以太网如何实现低延迟 by CloudFlare
扩展Linux网络堆栈 from the Linux kernel documentation
服务器规格清单
- 需要2台Amazon AWS EC2实例,系统版本为CentOS 7,实例规格为c4.8xlarge,两个实例均开启了增强联网功能。
- 每个实例都有2个处理器的NUMA,每个处理器有9个核心,每个核心有2个超线程,这实际上允许在每个实例上运行36个线程。
- 每个实例都有一个10Gbps网络接口卡(NIC)和60 GB内存。
为了支持增强联网功能和IPvlan,已经安装了带有Intel ixgbevf驱动程序的Linux内核4.3.0版本。
安装部署
现代NIC通过多个中断请求(IRQ)线提供接收方扩展(RSS),EC2在虚拟化环境中仅提供两条中断线,因此我们测试了几种RSS和接收数据包导向(RPS)接收数据包导向(RPS)配置,最终得到以下配置,部分由Linux内核文档提供:
IRQ
两个NUMA节点中的每个节点上的第一个核都配置为接收来自NIC的中断。
使用lscpu将CPU与NUMA节点匹配:$ lscpu | grep NUMA NUMA node(s): 2 NUMA node0 CPU(s): 0-8,18-26 NUMA node1 CPU(s): 9-17,27-35
这是通过将0和9写入/proc/irq/<num>/smp_affinity_list来实现的,其中IRQ编号是通过grep eth0 /proc/interrupts获得的:
$ echo 0 > /proc/irq/265/smp_affinity_list $ echo 9 > /proc/irq/266/smp_affinity_list
RPS
已测试了RPS的几种组合,为了提高延迟,我们仅使用CPU 1–8和10–17减轻了IRQ处理处理器的负担。与IRQ的smp_affinity不同,rps_cpus sysfs文件条目没有_list对应项,因此我们使用位掩码列出RPS可以将流量转发到的CPU:
$ echo "00000000,0003fdfe" > /sys/class/net/eth0/queues/rx-0/rps_cpus $ echo "00000000,0003fdfe" > /sys/class/net/eth0/queues/rx-1/rps_cpus
Transmit Packet Steering (XPS)
将所有NUMA 0处理器(包括HyperThreading,即CPU 0-8、18-26)设置为tx-0,将NUMA 1(CPU 9-17、27-37)设置为tx-12:
$ echo "00000000,07fc01ff" > /sys/class/net/eth0/queues/tx-0/xps_cpus $ echo "0000000f,f803fe00" > /sys/class/net/eth0/queues/tx-1/xps_cpus
Receive Flow Steering (RFS)
我们计划使用60k常驻连接,官方文档建议将其四舍五入为最接近的2的幂:
$ echo 65536 > /proc/sys/net/core/rps_sock_flow_entries $ echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt $ echo 32768 > /sys/class/net/eth0/queues/rx-1/rps_flow_cnt
Nginx
Nginx使用18个worker,每个worker都有自己的CPU(0-17),通过worker_cpu_affinity选项设置:
**workers** 18; **worker_cpu_affinity** 1 10 100 1000 10000 ...;
Tcpkali
Tcpkali没有内置的CPU亲和性支持,为了利用RFS,我们在任务集中运行tcpkali并调整调度程序,防止线程迁移的发生:
$ echo 10000000 > /proc/sys/kernel/sched_migration_cost_ns $ taskset -ac 0-17 tcpkali --threads 18 ...
与我们尝试过的其他设置相比,此设置能够更均匀地在CPU内核之间分配中断负载,并以相同的延迟实现更好的吞吐量。
CPU 0和9专门处理NIC中断,不处理数据包,但它们仍是最繁忙的:
RedHat的调整也与网络延迟配置文件一起使用,为了最大程度地减少nf_conntrack的影响,添加了NOTRACK规则,内核参数已调整为支持大量tcp连接:fs.file-max = 1024000 net.ipv4.ip_local_port_range = "2000 65535" net.ipv4.tcp_max_tw_buckets = 2000000 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_slow_start_after_idle = 0 net.ipv4.tcp_low_latency = 1
脚注
- Linux kernel documentation: RPS Configuration)
- Linux kernel documentation: XPS Configuration)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。