2

引言

Kubernetes要求集群中的每个pod都具有唯一的,可路由的IP,Kubernetes本身不会分配IP,而是将任务留给第三方解决方案。

本次课题的目标是找到具有最低延迟,最高吞吐量和最低安装成本的解决方案。 由于我的负载对延迟敏感,因此我的目的是在相对较高的网络利用率下测量较高的百分比延迟,特别是最大负载的30%到50%之间的性能,因为我认为这是最能代表非过载系统的最常见用例。

竞争对手们

Docker with --net=host

这是我们的一个参考设置,所有其他竞争对手都会与此设置进行比较。--net=host选项表示容器继承其主机的IP,即不涉及容器网络。

根据先验原则,没有任何容器网络方案比其他的会有更好的表现,这就是为什么使用此设置作为参考的原因。

Flannel

Flannel是一个虚拟网络解决方案,由CoreOS维护,它是经过充分验证可立即投入生产的解决方案,因此安装成本最低。将带有Flannel的worker节点添加到k8s群集时,Flannel会做三件事:

  1. 使用etcd为新worker节点分配一个子网
  2. 在机器上创建虚拟网桥接口(称为docker0网桥)
  3. 设置数据包转发后端:

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的容器,你必须:

  1. 创建一个完全没有网络接口的容器
  2. 在默认网络名称空间中创建ipvlan接口
  3. 将此接口移动到容器的网络命名空间中

IPvlan是一个相对较新的解决方案,因此没有现成的工具可以自动执行此过程。 这使得很难在许多服务器和容器中部署IPvlan,即部署成本很高。
但是,IPvlan不需要桥接接口,而是直接将数据包从NIC转发到虚拟接口,因此期望它的性能优于Flannel。

负载测试方案

对于每个竞争对手,执行以下步骤:

  1. 在两台物理机上设置网络
  2. 在一台机器上的容器中运行tcpkali,让其以恒定速率发送请求
  3. 在另一台计算机上的容器中运行Nginx,让它以固定大小的文件响应
  4. 捕获系统指标和tcpkali结果

我们以每秒50,000至450,000个请求(RPS)的请求速率运行基准。
在每次请求时,Nginx都会响应一个固定大小的静态文件:350 B(内容为100 B,标题为250 B)或4 KB。

测试结果

  1. 结果显示IPvlan有着最低的延迟和最高的最大吞吐量,host-gw和aws-vpc的Flannel紧随其后,但是host-gw在最大负载下显示的结果更好。
  2. 使用vxlan的Flannel在所有测试中均显示最差的结果,但是,我怀疑99.999%ile测试结果的异常是由一个bug引起的。
  3. 4 KB响应的结果类似于350 B响应的结果,但有两个明显的区别:最大的RPS要低得多,因为在4 KB响应下仅需约270kRPS即可完全加载10 Gbps NIC。
  4. 吞吐量测试中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


image.png
IPvlan略优于host-gw和aws-vpc,但是在99.99 %ile这个指标上是最差的,host-gw的性能略优于aws-vpc。

250,000 RPS, 350 B


这种负载在生产中也很常见,因此这些结果尤为重要。
image.png
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


较大的响应会造成较高的网络使用率,但测试结果看起来与较小的响应几乎相同:image.png

150k RPS, 4 KB


Host-gw具有令人惊讶的99.999%ile,对于较低的百分位数也显示出良好的结果。
image.png

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)

EngineerLeo
598 声望38 粉丝

专注于云原生、AI等相关技术