文章目录
译注
本文翻译自论文《An Analysis and Empirical Study of Container Networks》
摘要
容器,作为一种轻量级的虚拟化形式,提供了一种分割用户间硬件资源和加快应用部署的方法。和虚拟机(VMs)相比,容器开销(overhead)更小,并且允许更高的固结比(consolidation ratio)。容器网络作为容器虚拟化的一个重要组件,至今仍没有被人们完全理解。虽然已经有很多技术被开发出来用于提供单机(single host)或多机(multiple machines)容器之间的连接,但缺少关于它们在云上各自的优点、局限和性能的分析。
本文全面研究了典型的容器网络。我们先定性地比较了它们的适用场景、安全隔离级别和开销,然后定量地评估了吞吐量、延迟、可扩展性以及各种容器网络在真实云环境中启动的开销。我们发现容器中的虚拟网络相比起物理网络来说开销要大的多。性能的下降程度根据网络协议的类型和网络包(packet)的大小有所不同。我们的实验表明没有明显的性能最优者,用户需要根据要求和任务(workloads)的特征选择一个合适的容器网络。
I 引言
随着越来越多的应用被部署到云上,虚拟化的开销已经引起了人们的重视。传统虚拟机在虚拟的硬件上进行操作,并且运行了一个完整的操作系统,这种重量级的虚拟化方式限制了单个主机能够运行的虚拟机数量,并且较长的OS启动时间也导致在虚拟机跑短生命周期(short-lived)的应用效率很低。容器虚拟化通过在应用之间共享宿主机的OS库和内核解决了这个问题,每个应用运行在一个隔离的命名空间中。研究显示容器的开销很小并取得了接近原生的性能,并且占用了更少的空间。容器可以在一秒内启动,是运行事件驱动的微服务的理想选择。因此,一台机器上可以运行成百上千个频繁启动和终止的容器。例如,谷歌搜索每秒钟启动7000多个容器;一个关于八百多万个容器的调查显示27%容器的生命周期少于5分钟,11%少于1分钟
在动态的云环境中,为大规模的短生命周期容器提供网络连通性是有很大挑战的。首先,OS层虚拟化的灵活性允许一个容器连接外部世界的方式有多种多样。例如,有四种网络模式用于连接单机上的Docker容器,以及各种各样连接多机容器的方法。很难为一个特定的容器任务确定一种合适的网络;第二,单台服务器上高密度的容器要求单个连接以及扩展到大量连接时的网络性能损耗都要尽可能地小;第三,容器在连接建立前是不可访问的,尽管容器可以在一秒内启动,但是建立网络连接的时间会给容器的启动时间增加不少的延迟,这种延迟对按需的、短生命周期的容器应用(如事件触发的无服务器代码)是有很大影响的;最后,容器比虚拟机提供了更少的安全隔离,因此,大多数云提供商会在虚拟机里运行容器,容器网络和底层虚拟机网络之间的交互还没有被完全了解。
为了研究云上容器化的开销,我们比较了两种配置的不同类型的应用的性能。基线(baseline,下文有时也译作对照组)是直接运行应用在虚拟机上时的性能,而容器化在VM上的Docker容器运行应用。我们使用了综合的CPU测试工具(benchmark)、Stream和SockPerf分别测量了CPU、内存和端到端的TCP/UDP吞吐量。图1显示了容器在计算密集型和内存密集型任务中性能没有下降。然而,当应用通过容器网络通信时,相比起直接通过虚拟机网络,吞吐量却显著地下降了。注意这里我们使用Docker的默认网络模式进行对比测试。在对照组设置中,客户端和服务器分别是独立的进程,在单台虚拟机上通过回环网络通信,在多台虚拟机上通过虚拟机的IP地址通信。与此形成对比的是,当客户端和服务器运行在虚拟机上分开的容器上时,它们之间的通信通过一个额外抽象的容器网络,这引起了大幅度的网络性能下降。
因为网络性能对于容器应用的用户体验来说至关重要,为容器选择正确的网络也至关重要。不幸的是,这并不是一件简单的事,因为很多技术已经被开发出来用于虚拟化宿主机OS上的网络栈,从而为容器创建出隔离的网络。表I列出了三大云厂商可用的网络。一般来说,容器网络归为两大类:单机网络(Single-Host)和多机网络(multi-Host)。因为同个宿主机上的通信实际上是通过共享的内存,单机网络主要提供一个网络接口给容器化应用。多机网络集中于提供IP地址服务来使不同宿主机上的应用互联,如NAT,overlay网络和路由等。
本文中,我们呈现了一个详细的Docker容器网络的分析,进行了关于它们性能的实证(empirical,基于实验数据的)研究。我们先探索了单机容器网络并分析了它们的不同和使用场景(Ⅱ)。然后我们分析了多机容器网络,并分析了它们的技术,各自的优点和缺点(Ⅲ)。我们使用典型的测试工具来评估性能,可扩展性和容器网络的开销(Ⅳ)。并且review了相关工作(Ⅴ)。最后,我们对本文进行总结。
总而言之,本文有以下发现:
- 在单机网络中,性能和安全隔离难以权衡。使用共享的网络空间会取得好的性能,但安全性要求使用隔离的网络空间。
- 多机网络有不同的方面需要权衡。Overlay网络由于网络包的封装和解封导致了很大的开销,但允许更灵活和安全的网络管理。NAT和主机模式网络取得较好的性能但安全性较差。Routing网络性能也不错但需要额外的支持。
- 容器网络本身就有特定的开销,由于容器网络和虚拟机的虚拟网络栈之间复杂的交互,将容器部署在虚拟机上时还会有额外的吞吐量下降和延迟增加。
- 容器网络有一个需要考虑的重点在于启动时间,短生命周期和延迟敏感的应用在选择一个网络时应该考虑容器网络的启动时间。
II 单机上的容器网络
本小节,我们会介绍4种单机容器网络模式,比较它们的不同,讨论它们的使用场景。其中一些模式是多机网络的基础。
None mode
这种容器只有一个loopback接口,不能连接到本机或其他网络上的容器。然而它可以取得很好的隔离和安全性,适合不需要网络连接的服务,如:离线数据计算,批处理,备份任务等
桥接模式
桥接模式的Docker容器在单机上默认网络模式。当dockerd守护进程启动时,Docker就会在宿主机的OS上创建一个名为docker0的网桥。当一个新容器启动时,会有一对vthe端口被创建出来把容器连接到docker0上。所有连接到docker0的容器属于同个虚拟子网,使用私有IP地址相互通信。单独的桥接模式不能把容器连接到外部网络,必须依赖其他服务,如NAT和overlay。桥接模式允许每个容器都拥有一个隔离网络命名空间和IP地址,所有容器间的通信需要经过docker0网桥。同个宿主机上的包传播也需要调用隔离网络命名空间路径来发送和接收,导致了很多的网络开销。另外,把容器放在独立的网络空间让安全性稍微下降了一点。
容器模式
如图2所示,容器模式涉及到多个容器,它们共享同一个网络命名空间。在一组里,一个容器被指定为代理配置成桥接模式,其他组内的容器通过通过代理的vthe接口连接到外网。因此所有组内的容器共享同一个网络,整个组只指定了一个IP地址,组内的单个容器通过组IP加上一个端口号来辨认。容器模式被广泛地采用在许多容器管理框架上。例如,k8s上的一个Pod是一组容器,它们共享着相同的网络空间和地址。使用容器模式网络的容器可以使用标准的进程间通信(IPC)来相互通信。容器模式比桥接模式花费更少的通信开销并取得中等水平的安全。虽然不同组的容器是隔离的,但同组的容器之间没有任何安全隔离。因此,容器模式最适合属于同个用户的容器。然而,相比起桥接模式,它没有为每个容器分配一个IP地址。但一个大规模伸缩容器部署跨多台主机时,用户就要忍受同时实现IPC和IPs混合通信的负担。
Host模式
运行通过宿主机上的所有容器共享宿主机OS的网络命名空间,同样的,所有容器彼此可见并且容器之间通过进程间通信。Host模式的安全级别在四种模式中最低,因为所有用户同个宿主机的IP地址和命名空间。
总结
表II总结了四种单机网络模式的特点。从上到下网络变得逐渐高效,但安全隔离级别逐渐下降。
III 多机上的容器网络
Host模式
如第二小节讨论的,host模式下的容器共享宿主机的网络栈和命名空间。因此,不同主机上两个host模式的容器可以很容易地通信,就像两个主机上使用IP通信的两个进程一样。尽管Host模式的网络配置起来简单,但只有两个Host模式的容器能相互通信。例如,一个桥接模式的容器可以使用目的主机的IP发包给一个不同主机上的Host模式的容器,但反过来行不通。而且,Host模式对通过主机上的容器不进行安全隔离。
NAT模式
这个Docker 1.9之前最常用的多机容器网络技术。NAT在NAT表中映射一个容器的私有IP地址到它的端口号。通信必须使用主机的公有IP地址加上端口号来确定一个特定的容器。当容器发出一个包时,源主机把容器的私有IP重新映射成主机的公有IP地址,并改变包的头。目标主机使用接收到的包的端口号来映射到本机上容器的私有IP地址。发送端和接收端的地址转换都是在docker0这个网桥上完成的。NAT是一个实现不同主机上容器连通性的简单方法。它不需要复杂的配置和第三方软件支持。另外,因为NAT允许容器使用其宿主机的IP地址,所以在大规模容器部署中不需要大量的公有IP地址。然而,对每个发送和接收的包进行地址转换会有一些开销和性能下降。这些开销在依赖NAT的高密度容器中或者容器化的应用对性能敏感的情况下可能需要重视一下。另外一个限制是NAT模式下,容器的公有IP地址和它们的宿主机的IP绑定,导致很难实现动态网络。在一个短生命周期的容器的动态网络中,使用NAT模式避免端口冲突仍然是一个挑战。
Overlay网络
一个overlay网络运行在另外一个网络之上来建立节点之间定制化的连接。常用的overlay网络模式包括IPIP、vxlan、vpn等等。docker容器有很多overlay网络,虽然它们的实现各异,但核心思想都是类似的。容器在KV(key-value)存储中保存了它们私有地址和宿主机IP的映射,KV存储可以被所有主机访问到。容器使用虚拟子网中的一个私有IP地址相互通信。overlay在宿主机的网络栈中插入了额外的一层。当容器发一个包时,overlay层在KV存储中使用目标容器的私有IP地址来寻找目标主机的公有地址,然后创建一个新的包,使用目的主机的IP作为目的地址,使用原始包作为新包的装载。这个过程称为封包。一旦封装好的包到达目标主机,主机的网络栈会解开这个包得到原始包,把这个包传递给目标容器的私有IP地址。比起NAT,overlay网络提供了隔离的地址空间,并允许容器使用私有的地址进行通信。它更容易管理并能够更有弹性地修改网络的拓扑。但是overlay有两个缺点,一是封包和解包是耗时的操作,并延长了网络栈;而是封包时改变了原始包的大小,当底层网络限制了最大传输单元(MTU),封包时空间的开销可能会导致要发送的包的数量增加,因为新的包超过了MTU的限制。Docker在1.9以后有原生的overlay网络可以用,并且是多机网络默认的解决方案。
Docker原生overlay网络
如表III所示,Docker的overlay网络采用了vxlan桥来连接同个主机上的容器。overlay网络是构建在libkv和libnetwork之上的,使用vxlan来实现主机间容器的连通性。overlay网络是Docker swarm的默认网络,并且Docker提供了扩展API用来支持对overlay进行配置。在Docker提供overlay原生支持前,很多第三方解决方案已经提出来,这里我们讨论一下最常用的解决方案。
Weave
Weave是由Weavework开发的私有网络解决方案。Weave在每个Docker主机上部署weave路由器容器,weave网络由这些路由器组成。weave路由器之间的通信可以使用NaCI加密库加密来增强数据安全。Weave在每个主机上创建一个vethwe-bridge网桥,来连接容器和路由器。在发送端,当一个容器发送一个包时,该主机上的Weave路由器会使用pcap的混杂模式把这个包拦截下来,转发到远程主机的weave路由器上。在接收端,weave路由器也使用pcap来把包转交给网桥,然后把包转发给目标容器。weave路由器作为一个用户层的服务,处理包的封装和解封。同样的,使用weave构建一个overlay的开销包括了把包在用户态和内核态之间拷贝的开销。
Flannel
Flannel由CoreOS开发的一种虚拟网络。在Docker原生的overlay中,宿主机上的Docker守护进程独立地给容器分配私有虚拟IP地址。但不同主机上的多个容器被放到同一个overlay网络时,就可能发生IP地址冲突。因此,Docker原生overlay网络需要先创建好,然后再把容器加入到这个overlay网络中来避免IP冲突。这种方式的一个主要缺点是,无法给已经运行的容器动态地创建overlay,因为没有办法处理潜在的IP冲突。Flannel通过确保所有在不同主机上的容器拥有不同的IP地址来解决这个问题。为了做到这一点,flannel限制了所有在同台主机上的容器只能使用通过子网,并且为不同主机分配不同的子网,因此可以动态地配置overlay网络而不会有IP冲突问题。Flannel维护了一个分布式的KV存储,etcd来存储overlay的信息和它们的地址映射。它会在docker0和物理设备之间插入一个虚拟网络接口flannel0。包的封装和解封在flannel0中进行。Flannel支持通过TLS来加密通信。
Calico
Calico是由Tigera开发的可扩展和高效的虚拟网络。它为跨主机的容器连接提供了两个选项:IPIP overlay和边界路由协议(Border Gateway Protocol ,BGP) routing。后面一种会在后面的routing网络中讨论。不像前面提到的那些使用UDP或VXLAN来封装包的overlay网络,IPIP在网络(IP)层进行包的封装。原始的IP包会在一条网络隧道(如tunl0)的源端被包含在另一个IP包中,在隧道的目的端被解封。Calico也有昂贵的封装和解封包的开销,并且还强加一个额外的限制,它需要底层的基础设施支持IPIP协议。然而,许多云厂商,并没有支持IPIP协议,如微软的Azure。
Routing网络
overlay网络通过在另外一个网络(underlay)之上创建一个虚拟的网络层,为容器之间提供了逻辑的网络连接。除了开销很大,overlay网络还让网络包的监控非常困难,因为真正的网络包被封装起来了。为了解决这个问题,Calico为跨主机通信提供了一种可选的方式。它在主机的内核中实现了一个虚拟路由器,使用BGP来进行包的路由。作为一个网络层的解决方案,Calico相比起NAT和overlay网络来开销没有那么大。但它也有一些限制,首先,Calico只支持部分网络协议,如TCP,UDP,ICMP,适用性有限;其次,数据中心网络还没有广泛地支持BGP网络,例如,在公有云上,BGP不能跨越zone的边界;再次,路由表的大小限制了一个容器网络的规模;最后,在短生命周期的容器组成的动态网络中,更新BGP中的路由信息也是开销很大的。Calico支持TLS来加密etcd集群和Calico组件之间的通信。
总结
表IV总结了可用的多机容器网络。Host模式网络简单快速,但牺牲了安全和隔离。NAT简单易用,但容器的IP和主机的IP绑定在一起,端口管理复杂并且限制了网络管理的灵活性。表III总结了各种overlay网络的结构。overlay网络提供了很好的隔离和安全,但在包处理花费了大量的开销。另外,除了weave,文中讨论的overlay网络都是基于一个集群范围的KV存储来进行私有地址和公有地址的映射。这不仅变成一个单点故障,也增加了容器启动时间的延迟。Routing网络比overlay网络更高效,但只支持部分网络协议并且需要底层基础设施支持BGP。
IV 容器网络的性能
本节展示了本文中讨论的容器网络的性能评估。首先评估使用不同协议和不同包大小的单个和多个VM。然后研究虚拟化对容器性能的影响。最后评估不同容器网络如何应对干扰、CPU计算的开销和可扩展性。如果没有特殊说明,本文使用运行在VM上的容器来模拟公有云上的容器。
实验设置
硬件
2台DELL PowerEdge T430服务器,用千兆网卡连接。每台服务器有20核的Intel Xeon E5-2640 2.6GHz处理器,64G内存和2T的7200RPM SATA硬盘。Simultaneous multithreading (SMT) 被禁用掉来减少多次实验运行结果的不同。
软件
使用Linux内核4.9.5和Ubuntu 16.10作为宿主机和虚拟机的OS。Hypervisor使用KVM 2.6.1,Docker使用社区版1.12。虚拟机分配了默认的rtl8139网卡接口。Overlay网络软件:etcd 2.2.5, weave 1.9.3, flannel 0.5.5 and calico 2.1。
基准
使用了下面的基准来衡量容器网络的性能:
- Netperf:一个网络基准工具,提供单向吞吐量和端到端的延迟测量。本文使用2.7.0版本。
- Sockperf:为测试高性能系统的性能(吞吐量和延迟)而设计的网络基准工具,使用socket api。本文使用2.8版本。
- Sparkyfish:开源的网络带宽和延迟测试工具。使用TCP流来测试批量下载和上传的性能。版本1.2。
- OSU benchmarks:一个测量消息传递接口(Message Passing Interface ,MPI)应用的工具。版本5.3.2。
实验结果
单个虚拟机上的容器
首先我们比较了单个虚拟机上点对点的网络性能。虚拟机的配置是4核4G。作为对比的参考基线(baseline,为了便于理解,下文有些地方也翻译成对照组),我们在同个虚拟机上分别运行客户端和服务器进程(记为:w/o container)。图3(a)显示了使用Sparkyfish测量的批量上传和下载带宽的性能。除了桥接模式外,所有容器网络都取得了接近参考基线的性能。相比起来,桥接模式的上传和下载分别有18%和30%的性能损失。图3(b)显示了不同模式下TCP和UDP的吞吐量,使用Sockperf进行测量。包的大小设置为1024字节。类似的,桥接模式花费了更多的开销,而其它模式的开销不大。然后,我们使用osu_bw和osu_latency来测试在不同包大小的情况下网络的吞吐量和延迟。同样的,容器模式和Host模式在所有包大小中都取得了接近参考基线的性能,而桥接模式造成了10倍的吞吐量下降和延迟增加。
一个共性是,参考基线、容器模式和Host模式的容器都共享同个网络命名空间。参考基线和Host模式共享主机OS的网络栈,而容器网络模式的容器共享的是代理容器的命名空间。同样的,这些模式中容器间的通信是在loopback接口中进行的,实际上是通过IPC实现的。相比之下,容器模式拥有独立的网络命名空间,所有通信都需要经过网桥docker0,网络栈中增加了额外的一层引入了两个开销。一是它延长了网络包处理的路径,网络包需要通过docker0传播,导致了发送端和接收端遍历了网络栈,同时在接收端引起了额外的软中断。二是它消耗了更多的CPU资源,当CPU成为瓶颈时可能导致排队延迟。
多个虚拟机上的容器
为了评估跨主机容器网络的性能,我们使用了两台运行在同个物理机上的虚拟机,比起千兆以太网卡,这让虚拟机之间有更快速的网络连接。每台虚拟机的配置是4核4G。使用Sockperf作为测量功能,网络包的大小在TCP吞吐量测试中设置为256字节,在网络延迟测试中设置为16字节。如图4所示,和参考基线(w/o container)比起来,Host模式取得了接近参考基线的吞吐量和延迟。NAT和Calico有较大的性能损失,这是由地址转换和网络包路由的开销导致的。而所有的overlay网络都有非常大的性能损失。例如,Docker原生overlay模式有82.8%的吞吐量下降和55%的延迟增加。总的来说,网络包的封装和解封比起地址转换和路由来说开销更大。如前面讨论的,桥接模式引入了额外的网络处理,而所有的overlay都需要容器连接到网桥上,并且网络包的处理需要反复遍历网络栈。例如,在宿主机OS在传输层(4层)处理一个overlay网络包并且发现它是一个封装过的网络包的时候,就需要启动包的解封过程。解封完的网络包会被丢到以太网接口上,从接收中断处理程序处重新开始网络包的处理。然后,当网络包通过网桥传给容器时,又得重新走一遍协议栈。
网络包大小
网络包的大小在网络性能方面有至关重要的影响。假定数据发送的速率不变,大的网络包需要传输的网络包数量更少;假定固定的发包速率,大的网络包会导致更高的数据发送速率。我们使用Sockperf来衡量同个宿主机上两个虚拟机的TCP和UDP的吞吐量。Sockperf在发送端利用开环流量控制(open loop control flow)发送固定数量的网络包。因此,增加网络包的大小可以增加数据发送的速率。图5图6显示,参考基线和Host模式在TCP和UDP下数据速率都有较大的提高。而NAT和Calico(BGP模式)有一些开销但总的吞吐量也有增加。相比之下,所有的overlay网络都有大量的吞吐量性能损失。在TCP模式下,overlay随着包的增大吞吐量并没有增大。在UDP模式下,overlay网络的吞吐量有所上升但有较大的性能损失。如前面讨论的,网络包的封装和解封需要更多的处理资源。我们测量了接收端虚拟机的CPU消耗,单个流TCP和UDP的测试使虚拟机上的一个CPU超载了。然而SMP虚拟机无法使用多个vCPU来并行进行网络包的处理。一个overlay网络包需要多个步骤的处理,包括包的解封、解封了的包的中断处理和docker0上包的转发。但是网络栈在一个特定的协议栈检查到overlay网络包之前并不知道计算量。这导致了VM上的包处理无法进行有效的负载均衡。另外,随着数据速率的增加,在VM层面虚拟网络IO的开销逐渐增加。因此,VM上虚拟IO和包处理的总CPU需求会超过单个CPU的能力,导致overlay网络包的排队延迟和吞吐量的下降。这也解释了UDP测试中overlay吞吐量的下降。
网络协议
已有的研究显示,选择合适的网络协议对取得期望的服务质量(quality-of-service ,QoS)至关重要。图5和图6显示容器网络在TCP和UDP下性能有所不同。总的来说,TCP在所有网络中比UDP取得了更高的吞吐量(包括参考基线)。TCP实现了2个机制来高效地利用物理链接。首先,滑动窗口协议通过动态自适应数据的发送速率来避免网络拥塞;第二,TCP使用Nagles算法把小的网络包合并成一个大的网络包来提高链接的效率。网络包会先存储在内核的发送缓冲队列中,而不是从用户态收到一个包就马上发送出去,但缓冲队列满时才会把它们作为一个包发送出去。通常建议TCP比UDP更值得拥有因为可靠的传输和经过优化的吞吐量。然而,虚拟机上的容器网络让我们面临一个选择网络协议的新的挑战。如图5和图6所示,除了Host模式外,所有的跨主机容器网络在使用TCP时都比UDP有更高的吞吐量下降。如具有代表性的256字节的包大小,NAT和Calico (BGP)对比起参考基线TCP吞吐量分别下降了36%和39%,而overlay网络平均有82%的TCP吞吐量下降。相比之下,NAT只有24%的UDP吞吐量下降而Calico (BGP)的UDP吞吐量甚至还上升了,overlay网络的UDP吞吐量下降也小得多,平均38%。因为TCP的确认机制涉及更多操作和计算资源消耗,跨主机容器网络比起UDP会有更大的开销。
虚拟化的影响
容器可以部署在虚拟机或物理机上。有四种类型的容器部署:(1)同个物理机上;(2)同个物理机的不同虚拟机上;(3)不同物理机上;(4)不同物理机的不同虚拟机上。到目前为止,我们研究了(1)和(2)两种情况,而这次评估,我们使用(3)和(4)方式来部署容器网络。我们使用Sockperf来比较容器在物理机和虚拟机上的性能。结果如图7所示。对于那些没有达到带宽现在的网络包,硬件虚拟化和容器网络都会造成吞吐量下降。例如,相比起物理机上的性能,Sockperf直接在虚拟机上执行和在虚拟机上容器的overlay网络执行时,TCP吞吐量的下降分别为42%和50.3%。容器网络的托管和虚拟机上的虚拟网络栈加重了性能下降。这个现象在大的网络包中更明显。如图7所示,所有网络当使用1024字节的包并且在物理机上部署容器网络时,吞吐量下降都很小。和小包类似,虚拟机由于引入了多层网络虚拟化层,导致了大幅度的吞吐量下降,尤其在overlay网络中。
干扰(Interference)
备注:Interference指一个容器在进行通信时,同个主机上有其他容器来和它争用资源,导致该容器的通信受到影响,通信的性能会有所下降。
因为容器网络是容器基础设施一个至关重要的服务,研究它如何应对干扰是一件很有趣的事情。我们创建了一个受控制的环境,来评估来自同个虚拟机上其他容器的干扰。我们在4核4G的服务器上模拟了两种干扰:a)5个容器,每个执行一个循环,来评估有持续需求的任何 ;b)每个运行一个程序周期性的请求10%的CPU,来模拟容器频繁启动和终止。图8显示了干扰对Sockperf TCP吞吐量的影响。和没有干扰相比,所有情况在有干扰时的TCP吞吐量都下降了,包括参考基线,这是因为参考基线上的服务器进程被竞争的其他容器影响,分配到的CPU资源减少。同样的,容器网络通常包括一个用于管理网络流量的用户态的守护进程和一个用于路由和包风中的内核组件。用户态的守护进程是很容易受到干扰的,而内核受到的影响则较小,因为内核的处理相比起用户态的计算而已,是有严格的高优先级的。例如,Host模式使用宿主机的内核网络栈来处理包而Calico(BGP)在内核中实现了一个虚拟路由器,因此Host模式和Calico(BGP)都可以取得接近参考基线的性能,因为容器网络服务没有被干扰影响。与此相反,使用用户态守护进程的第三方overlay受到干扰的影响最大。这个实验告诉我们,在一个高度共用的环境中,将容器网络服务从其他用户任务中隔离出来或者在主机的OS内核中实现这个服务是比较明智的选择。
CPU负载
为了评估各种网络的CPU负载,我们在两个物理机上创建了两个1核4G的虚拟机。我们使用Netperf UDP_RR来测量CPU的使用量。客户端和服务端的容器配置成相同的网络模式。表V显示了CPU的使用量和各种多机网络的服务需求(demand)花费的时间。由于Netperf不能测量NAT的事务率(transaction rate),所有下表中没有包含NAT的结果。CPU的使用量包括用户态、内核态和等待IO花费的时间,用百分比来测量。服务需求是完成一次UDP_RR事务的时钟时间(wall-clock time)。在所有网络中,Host模式消耗的CPU和w/o container差不多,Calico (BGP)的CPU开销更多一点,因为它的虚拟路由器会有一些额外的计算。所有overlay网络的CPU开销由于包的封装和解封则要多得多。服务需求显示了容器网络在请求层面的花费,和CPU花费的趋势类似,CPU花费越多的网络,完成一个事务的时间就更长。
可扩展性
许多应用都是由多个组件组成,是理想的容器化使用场景。例如,web服务通常包括内存缓存,负载均衡和后端的数据库。大数据框架,如YARN,启动大量容器来并行计算一个任务。因此,探索多个容器之间并行通信的性能是非常有意义的。在MapReduce的shuffle阶段的多对多(all-to-all)通信就是一个很好的例子。我们使用OSU工具上的osu_alltoall程序来测试当容器网络动态伸缩时多对多通信的延迟。包大小设置为1024字节,使用2台配置为8核4G的虚拟机。图9(a)显示了单机容器网络的可扩展性。我们仅测试桥接模式,因为它是默认的配置并且是多机容器网络的基础。参考基线直接在VM上运行MPI程序,而w/ contailer在单独的容器中运行MPI程序。如图9(a)所示,桥接网络的延迟在容器数量增加时急剧地增加了。相比之下,参考基线在进程数量增加时能很好地扩展。很明显,当多个容器连接到docker0网桥时,该网桥会成为瓶颈。
图9(b)和9©显示了多机(multiple VMs)容器网桥的延迟。我们评估了分布在两个VM上的两个容器。在图9(b)中,我们固定了一个容器或一个进程在一个VM上,然后把其他容器或进程都放在另外一台VM上,在图9©中,我们把容器或进程平均分到两个VM上(不能被2整除时取近似的整数值),同个VM上的容器使用桥接模式通信,不同VM上的容器使用Docker的原生overlay网络通信。如图9所示,我们有几个发现。首先,跨VM多对多通信的延迟是单个VM的10倍,意味着跨VM是延迟增加的主要原因。容器网络中的延迟要高于w/o contailer(意味着跑在VM上也会增加一些延迟)。图9©表明,当VM通信越多地通过overlay时,w/ container 和 w/o container的性能差距就越大。这意味了在弹性伸缩时,跨VM的overlay是主要的瓶颈。本次实验提供了一个容器放置的建议,在overlay网络中,应该尽量把相互通信的容器放在同个主机上来避免高昂的通信开销。
网络启动时间
建立容器网络的时间对于短生命周期或延迟敏感的任务来说是至关重要的。我们使用none模式的容器的启动时间作为参考基线,表VI列出了配置了不同网络的容器的启动时间。这里的启动时间是指创建容器命令发出和容器回应一个ping之间的时间,结果使用了10次运行结果的平均值。在单机上,除了容器模式外,所有的网络的启动延迟都能够接近一个docke镜像的启动时间。容器模式的启动时间甚至比参考基线还更快,这是因为代理容器已经初始化好一个网络命名空间,把一个额外容器连接到代理上要比重新启动一个新的容器要快。
在多机网络中,Host模式和NAT模式网络的启动时间和单机上的差不多。但是,启动一个overlay或者初始化BGP路由表的时间要比对照组长4.5到23倍。例如,启动一个Docker overlay需要超过10秒,大多数的时间是在注册容器到KV存储中。Calico (BGP)启动也很慢,因为它需要花几秒钟用来传播路由表。用户应该在选择合适的网络时好好考虑不同网络的延迟,从而满足应用的延迟要求。
V 相关工作
容器网络标准
随着容器在许多领域被采用以及容器化应用的规模持续增长,我们需要一个标准的容器网络规范。目前,容器网络接口有两种标准。容器网络模型(Container Network Model ,CNM)是Docker官方提出的标准,它包括了许多模块,如沙盒、网络和endpoints。CNM以及被VMWare和Weave等采用。容器接口模型(Container Network Interface ,CNI)是谷歌和CoreOS为通用容器网络提出的社区标准。和CNM不同的是,CNI的设计用起来更简洁和灵活。CNI以及被Apache Mesos, Kubernetes等支持。许多开源项目,如Calico,同时支持两种标准。
容器网络优化
随着容器的流行,越来越多的研究开始关注解决容器网络效率低的问题。Hu等人描述了容器中典型NFV任务的特征并且观察到共享主机OS的网络栈是主要的瓶颈。他们提出了NetContainer,一个为容器化的NFV平台实现了细粒度硬件资源管理的软件框架。为了解决容器性能低下的问题,Yu等人提出了FreeFlow,使用共享内存和RDMA来实现容器间高吞吐量和低网络延迟。工业界还有很多探索,例如把Docker和SR-IOV或DPDK整合起来,从而加速容器网络。这些工作和我们是正交的。
VI 观点(insight)和结论
观点
为一个任务选择一个合适的网络是很有挑战性的,需要考虑许多因素。如果用户在单机上运行容器,他们需要在性能、安全和隔离之间权衡。如果安全和隔离是最重要的,桥接模式是最好的选择。如果容器需要频繁和其他容器相互通信,并且一些容器需要访问其他容器的命名空间来监控和管理,容器模式应该是最好的选择。Host模式有解决原生的性能,虽然没有提供任何隔离。如果用户在多个主机上构建容器网络,从性能考虑Calico (BGP) 是最好的选择,NAT是第二选择,如果架构不支持BGP。所有的overlay网络性能类似,引入了较大的开销。overlay网络对于不需要高性能网络但会频繁改变拓扑的任务来说是最好的选择。
除了容器的性能,在选择合适的网络时任务的特征也是至关重要的。例如,对于使用小包进行通信的任务(如消息服务)而言,所有的多机解决方案的性能类似;对于大流量的任务,Calico (BGP)性能更好。另外,TCP任务比UDP的开销要大得多。虚拟化也带来了额外的开销,特别是对于overlay网络而言。
为了提升容器网络的性能, 需要解决不同容器网络的瓶颈。宿主机OS上共享的网络栈、集中式的网桥docker0、Weave和Calico中的软件路由、overlay网络中包的封装和解封都有可能成为一个特定任务的瓶颈。为了提高单机上的容器网络性能,通信可以通过共享内存实现以及避免包在用户态和内核态之间拷贝。为了提高多机容器网络的性能,开销高贵的网络包封装和解封操作可以通过硬件来加速。如第IV节讨论的,有许多的因素影响着网络的性能,选择正确的网络是一件很有挑战性的事。机器学习之类的技术可以用于容器网络的自动选择。
结论
在本文中,我们展示了一份关于单机和多机容器网络的详细分析。我们对虚拟化环境中的各种容器网络做了一次全面的实践研究。据我们所知,本文是目前为止第一次对容器网络的多方面进行探索。我们有一些发现可以帮助用户为他们的任务选择合适的网络,以及指导已有容器网络的优化方向。