FreeFlow: Software-based Virtual RDMA Networking for Containerized Clouds
Abstract
许多流行的大规模云应用为了更高的资源利用效率和轻量级的隔离,正在越来越多地使用容器化。在并行领域,许多数据密集型应用(例如数据分析和深度学习框架)为了更好的网络性能正在适应或寻求适应RDMA。业界趋势认为这两方法正在发生不可避免的冲突。本文我们展示了FreeFlow,一个为容器云设计的基于软件的RDMA虚拟化框架。FreeFlow用纯粹基于软件的方法,使用商业的RDMA网卡实现了虚拟RDMA网络。不同于现有的RDMA虚拟化解决方案,FreeFlow完全满足云环境的要求,包括多租户隔离、容器迁移的可移植性以及控制平面和数据平面策略的可控性。FreeFlow对应用透明,提供了接近裸金属RDMA的网络性能并且CPU开销很低。我们在TensorFlowhe和Spark上的评估,FreeFlow提供了几乎和裸金属同样的性能。
1 Introduction
大规模云应用的开发者不断地寻求更好的性能,更低的管理成本,和更高的资源利用效率。这导致了两种技术被越来越多地采用:容器化和RDMA(Remote Direct Memory Access)网络。
容器提供了轻量级的隔离和便利性,这降低了部署和管理云应用的复杂度(和成本)。因此,容器是现在管理和部署大型云应用的实际方法。
RDMA网络相比起标准的TCP/IP网络提供了更高的吞吐量,更低的延迟和更低的CPU使用。因此,许多数据密集型应用正在采用RDMA。不幸的是,这两个趋势在根本上就是互相矛盾的。容器化的核心价值是提供一个高效和灵活的应用管理方式。为了达到这个目的,容器化的云需要容器在网络方面有以下3个特性:
- 隔离。每个容器必须有其专属的网络命名空间(包括端口,路由表和接口等)来避免和其他同个宿主机上容器的冲突
- 可移植性。一个容器必须使用虚拟网络来和其他容器通信,它的虚拟IP保持不变,无需关注它将迁入或迁出哪台宿主机
- 可控性。编排工具可以实施控制平面策略(如许可控制,路由等)和数据平面策略(如QoS,计量等)
这些特性对云来说是必要的,它们让云可以自由地放置和迁移容器,以及控制每个容器能够使用的资源。为了这个目的,在基于TCP/IP的操作中,网络是通过一个软件(虚拟)交换机完全虚拟化的。
然而,很难完全虚拟化基于RDMA的网络。RDMA通过把网络处理流程交给硬件网卡,越过内核软件协议栈来达到高性能。在共享的云环境上,修改硬件上的控制平面(如路由)是非常困难的。控制数据路径也同样困难,因为流量直接通过PCIe总线在RAM和NIC直接流动。
因此,几个数据密集型应用(如TensorFlow,CNTK,Spark,Hadoop)已经开始同时采用这些技术,当且仅当运行在专有的裸金属集群上时使用RDMA,当运行在共享的云上时,它们不得不牺牲掉RDMA带来的性能提升。自然地,使用专属的集群来运行应用在成本上是不划算的,不管对于服务提供者来说还是对于客户来说。
因此,我们在本文中的目标很简单:我们想要基于云的容器化应用能够像在专有裸金属集群上一样高效地使用RDMA,同时取得在容器云上的隔离、可移植性和可控制性的要求。
当前,还没有成熟的容器RDMA虚拟化解决方案。表1总结了一些可能被扩展成支持容器的选项,尽管它们无法满足一些关键的要求或者不得不导致大量的开销。
例如,基于硬件IO的虚拟化技术如SR-IOV有可移植性限制,因为它们需要重新配置硬件网卡和交换机来支持容器的迁移。控制路径虚拟化解决方案,如HyV,只能操控控制平面命令用于隔离和可移植性,不能提供数据流量的视图或控制。因此它们不能灵活地支持云提供商需要的数据平面策略。软件模拟的RDMA,如SoftRoCE通过运行RDMA在UDP网络协议栈之上以及使用现有的虚拟IP网络解决方案,可以很容易地取得隔离、可移植性和可控制性,但它的性能受限于UDP。
本文中,我们展示了FreeFlow,一个用于容器云的基于软件的虚拟RDMA网络框架,它同时取得了隔离、可移植性和可控制性,并提供了接近裸金属RDMA的性能。FreeFlow的核心是一个运行在每个服务器上的软件虚拟交换机,它用于在商业RDMA网卡上虚拟RDMA。FreeFlow不需要任何专业的硬件或基于硬件IO虚拟化。软件虚拟交换机拥有完全的权限来访问容器间通信的控制路径(如地址、路由)和数据路径(如数据流量)。这个设计哲学类似于现有的用于TCP/IP网络的软件虚拟交换机,如Open vSwitch,尽管FreeFlow的实际设计由于RDMA的特性与OVS大不相同。
FreeFlow的设计解决了两个关键的挑战。首先,我们想要FreeFlow对应用完全透明,这是一个挑战因为RDMA需要一个网卡来操作内存buffer和文件描述符,然而容器里的应用由于网络虚拟化并不直接和网卡交互。我们解决这个挑战的关键思路是:容器本质上是一个进程,它们可以很容易地和FreeFlow共享资源(如内存和文件描述符)。如果FreeFlow和容器共享相同的内存和文件描述符,任何在底层物理RDMA网卡上的操作都会自动地在容器里生效。存在的一个问题是,假定应用并没有配合地创建用于共享的资源,透明地和应用共享资源并不是直接的。我们设计方法用于转换非共享的资源为共享资源,而不修改或很少量的修改应用的代码。
其次,FreeFlow必须提供相当于裸金属RDMA的吞吐量和延迟。我们定位出吞吐量和延迟的性能瓶颈分别是内存拷贝和进程间通信。我们利用了一种0拷贝的吞吐量设计。以及一个共享内存的进程间通道。我们也优化了FreeFlow用于bounding的CPU负载。
我们使用标准的基准工具和真实世界的数据密集型应用(Spark和TensorFlow)评估了FreeFlow的性能。FreeFlow达到了和裸金属相当的性能,并且没有太多CPU开销。我们显示了FreeFlow提高了真实世界应用的性能,吞吐量提高了14.6倍,延迟降低了98%,相比起传统的的TCP/IP虚拟网络。FreeFlow已经引起了人们的兴趣。其开源代码存放于: https://github.com/Microsoft/Freeflow
背景
本节简要地介绍一下容器和RDMA网络的背景,已让我们了解容器基于软件的RDMA虚拟化的必要性。
容器和容器网络: 容器已经是打包和部署数据中心应用的事实选择。容器使用了诸如chroot的机制,将一个应用的可执行文件和依赖绑定在一个独立的命名空间中。由此来提供轻量级的虚拟化和可移植解决方案。
大部分容器化应用使用微服务架构,并且包含多个容器。例如,Spark上的每个mapper和reducer节点都是一个独立的容器;TensorFlow上的每个parameter服务节点和worker节点也都是独立容器。容器通过网络解决方案来交换数据。网络解决方案的设计影响着隔离和可移植性的等级。
例如,在host模式网络中,容器使用它们的宿主机的IP和端口,像宿主机OS上的普通进程一样通信。这个模式的隔离性(例如会有端口冲突问题)和可移植性(迁移时改变IP和端口)都很差。
因此,有很多应用使用虚拟模式的网络。在这种模式下,容器的网络命名空间是完全独立的,容器间通信通过一个虚拟(overlay)的网络,这个网络包含了一些安装在宿主机上的软件虚拟交换机。容器的虚拟IP是高度可移植的,到该虚拟IP的路由可以通过虚拟机交换机进行控制。因为所有数据流量必须通过虚拟交换机,它们可以访问这些流量,从而对容器的网络进行完全的控制。这种隔离和可移植性给编排工具在容器部署和迁移时带来极大的灵活性,而控制性则给云提供商提供了在控制平面和数据平面实施策略的权力。
确实,像K8S之类的编排工具强制要求使用虚拟网络模式。大量的软件解决方案用于为容器提供虚拟网络,如Weave和Docker Overlay。
RDMA网络: 许多现代应用(如深度学习和数据分析框架)已经开始采用RDMA网络来获得更高的吞吐量和更低的延迟。RDMA通过将大多数网络功能越过OS内湖直接交给NIC来取得这样的优化。
表2显示了使用RDMA用于一个深度学习应用:训练一个RNN(Recurrent Neural Network)语音模式识别时的性能提升。我们以在单机8GPU上的结果为基准,当应用运行在2台机器上16个GPU时,传统的TCP/IP网络成为了瓶颈,性能反而下降了。当使用RDMA时,额外的GPU则让性能提升了。
原因是这个RNN训练任务包括了成百上千个步骤,每一个步骤,所有GPU必须重置训练模型的参数,总流量在100MB到10GB之间。花费在通信上的时间浪费了GPU的时间,因为GPU在重置参数时是空闲的。TCP在这种频繁和突发式的任务表现很差,而RDMA可以在每次重置开始的时候立即跑满带宽。
基于软件的RDMA虚拟化需求: 我们注意到虚拟网络模式对容器应用的好处:隔离、可移植性和可控制性。我们也注意到RDMA可以为许多微服务架构的应用提供重要的性能提升。
问题是,我们如何让要求使用虚拟模式网络的容器应用使用RDMA网络呢?尤其是在云环境上。
RDMA网络,依赖于将大多数网络功能交给NIC。一种可能的“虚拟”RDMA网络的方法是使用基于硬件的解决方案,如SR-IOV。然而,这会限制虚拟模式网络提供的可移植性。如图1(a)所示,在SR-IOV中,NIC运行了一个简单的2层交换机,这个交换机仅仅完成VLAN转发功能。因此,所有进出一个虚拟网络的数据包都会直接在底层物理网络被路由。因此迁移容器C1到Host2上,需要重新配置物理交换机到C1的数据包的路由改为到Host2而不是Host1。在生产上,物理交换机需要维护一个巨大的路由表来管理所有虚拟网络中容器的路由,这在大规模的云环境中是不可行的。
因此,我们相信为容器虚拟RDMA的正确方法是使用一个软件交换机,就像虚拟化传统TCP/IP网络的做法一样。如图1(b)所示,物理网络仅负责转发数据包到不同的宿主机上,虚拟网络的路由完全由每个宿主机上的软件交换机来实现,和物理网络是独立开的。软件交换机可以控制所有的寻址和路由,因此可以为控制平面提供好的隔离和可移植性。它也可以被用于实现数据平面上的网络功能,如QoS和计量。
3 概述
FreeFlow的目标是在每个容器中提供一个虚拟接口,让应用可以通过这个虚拟接口之上的虚拟网络使用RDMA,而不需要进行修改。理想情况下,这个虚拟网络的性能应该接近裸金属RDMA,并且数据平面和控制平面上的策略应该能灵活地纯粹通过软件进行配置。在本节,我们展示了系统的架构和FreeFlow设计的关键挑战。
3.1 总体设计
在原生RDMA中,如图2(a)所示,应用利用RDMA API直接发送命令给硬件NIC用于所有控制和数据路径的功能。FreeFlow拦截应用和物理NIC之间的通信,在软件FreeFlow路由器中执行控制平面和数据平面的策略,该路由器作为另外一个容器运行在宿主机上。特别的,为了控制数据路径,FreeFlow路由器仅允许物理NIC直接读写其自己的内存(图2(b)上的shadow memory)和负责从应用内存拷贝数据或拷贝数据到应用内存中。
3.2 Verbs: the “narrow waist” for RDMA
有许多中拦截应用和物理NIC直接通信的方法,但我们必须选择高效的一种。今天有许多支持RDMA的商业技术可用,包括Infiniband,RoCE和iWarp。应用可能会使用几种不同的高层API来访问RDMA特性,如MPI和rsocket。如图3所示,这些API事实上的“narrow waist”是IB Verbs API(Verbs)。因此,我们有意识地选择在FreeFlow中支持Verbs,这样我们可以自然地支持所有高层API。
Verbs在数据传输中使用了“队列对”(queue pairs, QP)的概念。对于每个连接,两个endpoint都会有一个发送队列(send queue, SQ)和一个接收队列(receive queue,RQ),加起来就叫QP。发送队列持有关于要发送的内存缓冲的信息,而接收队列用于接收数据的缓冲的信息。每个endpoint有一个单独的完成队列(completion queue, CQ),NIC使用CQ来告知endpoint关于发送或接收请求的完成。Verbs库和相关的驱动允许应用去读、写和监控这三个队列。数据的实际传输,包括打包和错误恢复都由NIC处理。
为了透明地支持Verbs,FreeFlow在虚拟NIC中创建了虚拟的QP和CQ,并将它们和物理NIC上真正的QP和CQ关联起来。
3.3 FreeFlow架构
FreeFlow的架构如图4所示。我们修改或引入的容器网络栈的三个组件如灰色部分所示:
- FreeFlow网络库(the FreeFlow network library,FFL)
- FreeFlow软件路由器(the FreeFlow software router,FFR)
- FreeFlow网络编排工具(the FreeFlow network orchestrator,FFO)
FFL位于容器中,是让FreeFlow对应用透明的关键。从应用的角度看,它和标准的RDMA Verbs库没什么区别。所有构建在Verbs API之上的应用和中间件都无需进行修改(或者极少量的修改)就能运行。FFL和FFR配合工作。
FFR在每个宿主机上运行一个实例,为同一宿主机上的所有容器提供虚拟网络。在数据平面,FFR和同宿主机上的容器共享内存缓冲区,并且为不同容器在该缓冲区上提供隔离。FFR通过NIC发送和接收共享内存中的数据,依赖FFL来同步应用的私有数据缓冲区和共享内存缓冲区之间的数据。FFR通过控制容器和FFR之间的共享内存通道,实现了数据平面的资源策略,如QoS。FFR也和FFO协作来处理记账任务,如IP地址分配。
FFO为集群内所有容器进行控制平面的决定,基于用户定义的配置和集群的实时监控。它还维护了一个集中式的内存map,在4.3节会有进一步进行讨论。
3.4 挑战
设计FreeFlow时,我们需要解决两个关键的挑战。首先,FreeFlow需要提供一个RDMA接口来透明地支持现有所有类型的RDMA操作。这些各种各样的RDMA操作包括单向和双向的数据传输,轮询和基于事件的任务完成通知,以及用于连接建立的TCP/IP和RDMA-CM。由于RDMA操作的复杂性,我们观察到现在并没有直接透明地支持它们。其次,FreeFlow需要提供接近裸金属RDMA的,并且最小化CPU和内存开销。因为FFR通过FFL拦截了来自应用的Verbs调用,我们需要小心地设计FFR和FFL之间的通信通道。
我们会分别在第4和第5章介绍我们解决这两个挑战的方法。
4 透明地支持RDMA操作
Verbs支持多种操作和机制。在单向的操作如读或写中,writer可以写数据到远端的一个特定内存地址中,后者并不知道这个操作。在双向操作如发送和接收中,接收者必须在发送者发送数据前准备好接收数据。并且,应用可以使用轮询或事件机制来获取任务完成的通知。不同的应用按需使用不同的类型,我们可以看到在流行的应用中所有这些都会被用到。
FreeFlow完全和透明地支持了这些不同类型的RDMA操作。最主要的挑战是支持单向操作和基于事件的完成通知,在这些操作中,RDMA NIC可以静静地修改FFR中的内存和文件描述符。除非它频繁地轮询内存或文件描述符的状态,否则FFR不会立刻知道这些修改,这样很难尽可能快地将物理NIC的操作转换成容器上的虚拟NIC操作。我们利用了容器本质上是一个进程这个事实解决了这个挑战,因此FFL和FFR可以共享内存和文件描述符,因此物理NIC的修改可以自动地被传递到容器中。在FFL和FFR之间共享内存并不是直接对应用透明透明的,因为容器内的应用并没有分配内存在IPC共享内存空间上,我们需要转换这些内存为共享内存。
4.1 连接建立
两个RDMA endpoint需要先建立连接。它们在每个NIC上创建一个QP,注册一块内存给QP以及将本地QP和远端QP配对起来。连接建立以后,应用会让NIC把已注册的内存上的内容发送给远端或把接收的数据放到本地内存缓冲上。
图5的步骤1-7显示了使用Verbs时典型的连接建立过程。左边的一列显示了应用发出的Verbs调用的顺序。阴影的两列显示了FreeFlow如何拦截来自应用的Verbs调用,以及建立发送端的FFR和接收端的FFR的连接。
- 步骤1:应用查询驱动支持Verbs的NIC列表。FFL拦截这个调用,返回容器的虚拟NIC
- 步骤2:应用创建一个QP和CQ在其虚拟NIC上,同时FFR在物理NIC上创建相应的QP’和CQ’。在FFR创建完这些队列后,队列的QP-ID和其他元数据信息将会通过FFL被转发给应用。
- 步骤3:应用注册一块内存给QP。FFR分配一块相应的同样大小的内存块(s-mem)在其共享内存上,注册s-mem给QP’。FFR返回一个用于创建s-mem的ID(IPC内存中唯一的名字)。通过这个ID,FFL可以映射s-mem到其自己的虚拟内存空间。
- 步骤4:应用查询本地QP的地址(在RDMA中也叫GID)。这个地址信息会共享出来,用于配对本地QP和远端QP。这个步骤的最后,FFR返回QP’的实际GID。
- 步骤5:应用和远端交换GID和QP-ID。应用可以通过任何通道如TCP/IP或RDMA-CM来交换这些信息。
- 步骤6:应用使用接收方的GID来让本地QP和远端容器的QP配对。FFL将这个GID转发给FFR,FFR使用这个GID来配对QP’
- 步骤7:应用修改本地QP的状态为Ready状态,相应地,FFR修改QP’的状态。
在步骤7之后,从应用的角度看,已经可以开始发送或接收数据了——已经创建了一个QP和CQ,给QP注册了内存,和远端QP配对并和远端QP建立连接。从FreeFlow的角度看,已经创建了QP’和CQ’,注册了s-mem作为mem的影子内存,QP’和远端的FFR配对,它也准备好转发来自应用的Verbs调用。
FreeFlow可能会由于FFR和FFL之间额外的交互增加连接建立的延迟。然而,它并不会对FreeFlow总体的延迟有太大的影响,因为它是一次性的开销,许多RDMA应用会复用前面建立的连接进行通信。
4.2 双向操作
每个发送端和接收端都需要经历两个步骤来执行数据传输。第一步是使用QP来开始发送或接收数据,第二步是使用CQ来得到完成的通知。图5的步骤8-9显示了这个过程。
- 步骤8: 应用发起SEND调用,并且提供指向mem的指针。FFL首先从mem拷贝数据到s-mem,然后FFR调用它自己的SEND来把s-mem发送给远端FFR。我们通过0拷贝机制(详见4.3)来避免从mem到s-mem的内存拷贝。需要注意的是,此时远端的FFR需要执行相应的RECV调用。
- 步骤9: 应用对CQ进行轮询或等待发送完成的通知。FFR也轮询/等待 CQ’,并会将其转发给FFL
对于随后在通过QP的SEND操作,应用只需要重复地执行步骤8和9。RECV操作的工作流的类似的。
对于应用来说,FFL和FFR的存在是完全透明的。对于应用来说,它好像在它的vNIC执行普通的verb操作。图5的步骤是writing verbs程序的标准方式。这里阐述的FreeFlow行为对于完全支持SEND和RECV操作来说至关重要。
4.3 单向操作
在单向操作中,客户端不仅需要服务端的GID,还需要远程内存缓冲区的地址,以及访问该内存的安全密钥。这些信息在图5的步骤5上呗交换,在步骤8时可以被访问(此时WRITE和READ可以被调用)。
相比起双向操作,要透明地支持单向操作更具有挑战性。在FreeFlow支持单向操作时有两个挑战性。
首先,目标内存地址mem是在远程容器的虚拟内存中,而本地的FFR不知道其相应的s-mem,例如,在图6(a)中,当发送端试图将mem-1的数据写到远端内存mem-2上时,失败了,因为mem-2对于发送端来说是不可访问的。
为了解决这个问题,FreeFlow在FFO中构建了一个中央key-value存储,为所有FFR提供了应用的虚拟内存空间中的mem指针和相应的FFR虚拟内存空间中s-mem指针的映射。更新这个表增加了图5中步骤3的延迟。但是,数据平面的性能不会受到影响,因为FFR可以将这个映射缓存到本地。
其次,即使我们知道远端的内存映射,WRITE和READ可以远程修改或拷贝数据而无需通知对端的CPU,所以FFR不知道什么时候将数据拷贝到应用或从应用中拷贝出来。例如,图6(b),发送端发现s-mem-2正确的地址并且发送数据给它。然而,当数据已经在s-mem-2时,没有通知发到接收端的FFR,来告诉它将s-mem-2的数据拷贝到mem-2。一种解决办法是不断地同步s-mem-2和mem-2,这会消耗大量的CPU和内存总线带宽。
为了解决这个问题,基于0拷贝的机制来高效地支持单向操作。一个高端的主意是mem和s-mem使用同一块物理内存,这样FFR就无需做任何拷贝,数据会自然呈现给应用。图6© 展示了这个设计。通过避免内存拷贝,我们也提高了FreeFlow的性能。
这里的关键是让应用直接分配和使用共享内存,为了达到这个目的,FreeFlow提供了两个选择:
- 选择1——使用新的API分配共享内存: 我们创造了两个新的Verbs函数,ibv_malloc和ibv_free,来让应用将内存的创建和释放委托给FreeFlow。这允许FFL直接在共享内存区(和FFR共享)分配这些内存缓冲,并且避免拷贝。缺点是需要修改应用的代码,虽然只需要修改数据缓冲区创建部分的几行代码。
- 选择2——重新映射应用的虚拟内存地址到共享内存: 当一个应用注册了一个虚拟内存地址为va的私有内存作为数据缓冲区(如图5的步骤3),FFL会释放va对应的物理内存,并且从FFR中分配一块共享物理内存给va。在Linux中,当va是一个内存页的起始地址时,这个操作是合法的。为了强制应用在分配内存时总是在内存页的起始位置,FFL拦截了像C语言中的malloc的调用,让它总是返回整齐的内存页地址。虽然这个选择可以达到0拷贝并且不修改应用代码, 但它强制应用的所有内存分配操作都是使用对齐页的,这会导致宿主机上内存效率下降。
实践中,我们建议第一个选择,因为它更简洁和高效。然而,因为许多RDMA应用已经让它们的数据缓冲区的内存页对齐来达到更高的性能(如RDMA-Spark),我们可以直接使用选择2,无需拦击malloc,所有副作用是有限的。需要注意的是,如果一个开发者选择使用选择1修改应用代码或应用已经支持内存页对齐,FreeFlow都不会在内存使用上导致额外的开销。
4.4 基于事件的操作
有两个选择来从CQ中获取通知。第一个选择是让应用定期地轮询CQ来检查是否有任务已完成的操作。第二个选择是基于事件的,意味着应用创建了一个事件通道,并将CQ加入到该通道中。这个通道包含了一个文件描述符,它可以在操作完成时触发事件。
在FreeFlow中,因为文件描述符是从物理NIC创建的,FFR需要将文件描述符传给FFL,否则后续不能检测到和该文件描述符相关的事件。我们利用FFL和FFR实际上是共享同个OS内核的两个进程,使用相同的方法论在进程间传递文件描述符,从而将事件通道从FFR传到FFL。
5 FFL和FFR之间的通信通道
因为FreeFlow通过FFL拦截了每个Verbs调用,把它们通过FFR翻译和转发给物理NIC,所以FFL和FFR之间要有一个高效通道就显得至关重要,它在提供高RDMA性能的同时减少系统资源的消耗。在本章中,我们展示了两种这样的通道的设计,它允许牺牲RDMA的性能来降低系统资源消耗或者反过来,取决于应用的需求。
5.1 通过文件描述符进行Verbs转发
直接在FFL和FFR之间传递Verbs调用的一种方法是使用RPC:FFL传递API名字和参数给FFR,FFR适当地修改参数,执行这个API并返回结果给FFL。但是,这种简单的RPC方法在FreeFlow中并不是很好用,因为Verbs调用的输入数据结构很复杂。如图7(a)所示,一个典型的Verbs调用函数:ibv_post_send,有输入(qp,wr)和输出bad_wr,它们都是指向复杂数据结构的指针。因为FFL和FFR是两个不同的进程,FFL中的指针在FFR中是无效的。
有人可能会建议“深拷贝”,这样就可以将复杂的数据结构传过去。但是,这种方法有两个严重的缺点,首先,Verbs中的数据结构可能很深(多层指针和递归),这样的深拷贝可能会引起性能损失。其次,有一些用户自定义数据结构FreeFlow的深拷贝方法无法提前预知。
为了解决这个问题,我们利用了现有Verbs库的结构。如图7(b)所示,Verbs库有三层。顶层是最复杂的一层,并且如上面描述的难以处理。但是,当我们来到中间层,它和NIC文件描述符通信,Verbs库可以准备一个足够简单的数据结构(没有指针),这样的数据结构NIC硬件能够接受。
因此,我们没有转发初始的Verbs调用函数,而是转发对NIC文件描述符的请求。我们在容器中使用Unix Socket文件描述符来替代NIC文件描述符,Socket文件描述符的另一端就是FFR,如图7©所示。FFR可以得知应用发送的命令和参数。FFR会将对容器中虚拟队列的操作映射到对物理NIC上实际队列的相同操作。然后通过Socket文件描述符将物理NIC的回应转化成虚拟NIC的回应。FFL中的NIC驱动通信层会正常地处理这个回应,而不需要知道socket文件描述背后进行的操作。
虽然这个基于文件描述符的方法消耗很少的CPU,但它会引起额外的延迟,这是通过socket进行通信固有的延迟。我们测试显示在一个商业服务器上,通过Unix Socket来回一趟的时间会轻易超过5us。基于此,图7©所示的Socket通信通道对于那些对延迟敏感的应用来说可能成为性能的瓶颈。
对于那些需要低延迟通信的应用,我们有另外一个设计:Fastpath,它会优化通信的延迟,但会增进CPU资源的使用。
FFL和FFR之间的Fastpath
为了加速FFL和FFR之间的通信,我们设计了一个Fastpath,和基于socket的通道并存。如图8所示,FFL和FFR共同拥有一块专属的共享内存,在Fastpath下,FFR在一个CPU核上不断执行,并检查是否有新的从FFL发过来的请求被写到共享内存中。一旦检测到一个请求,FFR会立刻执行它,同时FFL在一个CPU核上不断执行,检查响应是否已经就绪,在读到响应后,FFL会停止在CPU上的轮询。
在8.1.2中我们可以看到,Fastpath可以大大地降低延迟,代价是花费在不断读请求和响应的CPU循环。为了限制Fastpath带来的CPU开销,我们做了两个设计决定:
- 1 在一台宿主机上,FFR只在一个CPU核上进行轮询
- 2 Fastpath只用在那些使用数据通道和非阻塞的函数,这样FFL等待响应花费的时间会很少(几微秒)。总之,Fastpath平均每台宿主机仅消耗一个CPU核来大大降低消息传递的延迟。另外,如果FFO应用是延迟不敏感的(根据运行的容器镜像),可以禁用Fastpath
6 实现
我们通过修改libiverbs、 libmlx4和librdmacm来实现FFL。我们增加了大约4000行代码来实现FreeFlow的逻辑。我们从零开始用2000行左右的C++代码实现了FFR。对于FFO,我们使用Zookeepler来存储用户自定义的信息,如IP分配、访问控制、资源共享策略和内存映射信息。由于空间限制,接下来我们只展示3个有代表性的实现细节。
控制和数据平面策略
因为FreeFlow可以控制容器请求的数据和控制平面操作,它可以支持通用的数据平面和控制平面策略,包括带宽限制、优先级和资源使用限制。
作为控制平面策略的一个例子,在我们的原型中,FreeFlow为每个容易能够创建的QP数量强制使用了一个配额,因为大量的QP是RDMA性能下降的一个主要原因。这个控制平面策略防止一个容器创建太多的QP,以免影响同个宿主机上的其他容器。
作为数据平面策略的一个例子,FreeFlow支持pre-flow速率限制,并且开销很少。我们在FreeFlow中实现了一个简单的token-bucket数据结构。当一个应用创建一个新的QP,我们会检查存储在FFO中的策略,将该QP预先设置的限速关联到token-buckeet上。在应用每次发送请求时,FFR检查QP是否有足够的token来发送请求的数据量,有的话请求会马上被转发到真实的NIC,否则FFR会通知FFL并延迟它的发送知道有足够的token。需要注意的是这只是实现QoS策略的一个例子。FreeFlow提供灵活的API以便在FFR中实现成熟的QoS算法,由于空间限制我们省略了这部分细节。
Fastpath中的内存管理
在Fastpath的实现中,我们使用汇编代码来明确地强制FFR和FFL写入的请求和响应的cache会被立刻刷新到主存中。这样做是必要的否则CPU会把新写的行放到cache中一段时间等待足够多写入的行,降低了Fastpath信息交换的速度。
支持并行
因为应用可以创建多个QP和使用多个线程并行地传输数据,每个FFL和FFR之间的Unix域socket需要一个锁,为了提高性能,我们在FFR和FFL之间创建了多个socket。我们通过分配更多的socket给数据平面操作和事件通知来避免“队头阻塞”,并且只有少量的socket创建、设置和删除操作。在FFR,我们为每个Socket连接分配一个专用的线程。我们还为每个容器创建了一个专有的数据结构,以及为每个注册的内存缓冲区创建了专有的共享内存区域,来保持数据路径的锁畅通。
7 讨论
在本章中,我们讨论当前FreeFlow的一些主要的关注点和潜在的扩展。
- CPU开销: 和基于软件的TCP/IP虚拟网络方案类似,FreeFlow导致了额外的CPU开销。尤其是当FreeFlow使用一个CPU核用于轮询FFL和FFR之间的控制消息来支持低延迟的IPC通道时。我们承认这是在当前商业硬件之上实现网络虚拟化的一个代价。解决这个问题的一种可能方法是利用硬件来支持这些CPU任务,如FPGA,ARM处理器或者RDMA NIC。我们把消除Fastpath的CPU开销作为未来的一个工作。
- 安全: 一个担忧是FFR和容器共享内存,是否一个容器可以通过扫描IPC空间读取同个宿主机上其他容器的通信呢?这在FreeFlow中不是一个问题,因为FreeFlow为每个QP创建一个专有的共享内存缓冲区。只有属于一个容器的共享内存缓冲区会被映射到容器的虚拟内存空间。另一个担忧是内存key的安全。如果key被窃听,接下来的通信都可能被泄露。这个问题是原生RDMA的单向操作本身的问题,不是FreeFlow带来的问题。
- 和外部普通对端配合工作: FreeFlow中的容器可以自然地和外部的RDMA对端通信,因为每个FFR都是独立工作的,FFR并不区分远端是另一个FFR还是普通的RDMA对端。
- 容器迁移: FreeFlow天生支持离线迁移。如果一个容器关闭,移动,并在另外一台宿主机上重启,它的IP地址不会改变,它的对端会重新和它建立RDMA连接,就像它只是重启一样。当前,离线迁移在容器集群中被普遍用于资源打包和失败恢复。FreeFlow不支持在线迁移,因为现在RDMA的移动性很差。
- VM宿主机: 我们的原型(以及测评)是基于运行在裸金属宿主机的容器,但FreeFlow可以直接用于部署在VM中的容器,只要VM使用SR-IOV来访问物理NIC。
- 拥塞控制: RDMA已经有拥塞控制机制,FreeFlow依赖这些机制
8 测评
我们测评了FreeFlow的性能和开销。我们从微基准测试(microbenchmarks)开始,然后测试真实世界应用在FreeFlow上的性能。
微基准测试
设置: 我们在两个实验台上运行微基准测试。一个运行InfiniBand,它是一个传统的RDMA专线。服务器配置是2个CPU(Intel Xeon E5-2620 2.10GHz 8-core),64G内存,56Gbps Mellanox FDR CX3 NIC。OS是Ubuntu 14.04,内核是 3.13.0-129-generic。
另外一个运行了RoCE (RDMA over Converged Ethernet)。顾名思义,RoCE只需要传统的以太网交换机(我们这里使用Arista 7050QX 作为ToR switch)。这个集群的服务器配置是Intel Xeon E5-2609 2.40GHz 4-core CPU,64G内存, 40Gbps Mellanox CX3 NIC and Ubuntu 14.04 with the kernel version 4.4.0-31-generic。
我们使用Docker(v1.13.0)运行容器,并使用Weave(v1.8.0)设置了一个基础的TCP/IP虚拟网络,开启OVS内核模块支持。没有特别指出的话,我们运行Fastpath模式。
我们主要比较了FreeFlow和裸金属,因为裸金属是最佳性能的代表。我们将展示FreeFlow以最小的性能损失为容器提供了虚拟RDMA网络。在8.1.4,我们会阐述将TCP socket调用转化为FreeFlow之上的RDMA的性能,以让传统的TCP应用可以从FreeFlow中受益。我们还比较了FreeFlow和裸金属TCP和Weave。
8.1.1 吞吐量和延迟
我们关注两个基本的性能指标,吞吐量和延迟。我们使用基准测试工具测试了ib_send_lat和ib_send_bw来测量双向操作(SEND)的延迟和吞吐量,测试了ib_write_lat和ib_write_bw来测试单向操作(WRITE)的延迟和吞吐量。如4.3所述,这些工具可以在FreeFlow上运行无需任何修改。总之,FreeFlow不区分跨主机设置(sender和receiver运行在不同主机)和主机内设置。这里我们只展示跨主机的性能。
吞吐量
我们在两个实验台上测量了单线程RDMA SEND/WRITE的吞吐量,SEND的结构如图9所示。每次运行传输1GB的数据,消息大小从2KB到1MB不等。FreeFlow RDMA WRITE的结果实际上比SEND要好一点点,这里为了简要省略了。我们可以看到当消息大小大于或等于8KB时,FreeFlow获得了和RDMA一样的吞吐量。另外,当我们并发容器对的数量到512时,总的吞吐量仍然接近最优(如图11所示)。我们也证实了带宽被平均分配到每个容器对的数据流中。
总的来说,带宽敏感型应用更倾向于使用大的消息大小。例如,在我们内部使用RDMA的存储集群上,典型的消息大小是1MB或更大,在这种场景中FreeFlow没什么吞吐量损失。
即便消息大小很小,如2KB,FreeFlow也取得了超过一半的吞吐量。我们证明了,在这种场景下,吞吐量受限于单个FFR Fastpath线程。这个瓶颈可以通过给FFR多分配一个或多个CPU核和平衡两个核之间的RDMA请求负载来克服。我们保留了这个选项,因为开发者通常不希望牺牲带宽来使用小消息。使用小消息时,开发者通常关注的是延迟。
延迟
我们分别测量了发送64B,258B,1KB和4KB的延迟。如吞吐量基准测试中一样,2个容器运行在两台宿主机上,两个宿主机使用同一个Tor交换机连接起来。对于每种消息大小,我们测量1000次延迟。
图10显示了延迟测试结果。我们可以看到单向的WRITE操作比双向的SEND操作延迟更低,并且FreeFlow和裸金属RDMA的差距更小。但是,即使是双向的操作,FreeFlow引起的额外延迟小于1.5us,这个额外的延迟主要是因为FFL和FFR之间的IPC通信。单向操作只会触发一次IPC,而双向操作会触发两次IPC和一次内存拷贝。这解释了双向操作的差距更大的原因。
把这些延迟考虑在内,网络上的一跳(如一个硬件交换机)有0.55us的延迟。因此,FreeFlow延迟的开销大约相当于在网络中增进一个交换机。相比之下,主机TCP栈的延迟至少10us,而TCP/IP虚拟网络的延迟更大(在我们的测试中超过40us),这意味着FreeFlow保留了RDMA低延迟的好处同时为容器提供了虚拟网络。
8.1.2 CPU开销和权衡
FreeFlow在低CPU开销的前提下取得了很好的性能。FreeFlow有两个模式:Fastpath和no-Fastpath(也叫LowCPU模式)。默认情况下是开启Fastpath模式并在延迟方面提供最佳性能。这个模式下,FFR会在一个CPU核上不断进行轮询以让Verbs请求尽快被处理。一个CPU核就足以支持一台宿主机上的所有容器,归功于FFR只处理消息级别的事件,而不是像OVS那样处理包级别的。在一台有多个CPU核的商业服务器上,这是可以接受的。
另外,用户可以选择LowCPU模式,这会使用Unix socket作为信号机制而非通过CPU核来轮询。这会增加延迟(增加2.4us到17us),如表3所示。图12我们记录了测试主机间吞吐量时的CPU使用。三种场景下的吞吐量是一样的。这表明了LowCPU模式的CPU性能优势,特别是在FFR中。在LowCPU模式,FFR CPU开销和实际负载相关。
我们建议根据任务的需求来选择合适的模式。延迟敏感型或非CPU密集型(如GPU密集型)应用可以选择Fastpath模式,否则可以选LowCPU模式。但是,即使在Fastpath模式下,FFR最多消耗一个CPU核,FFL引起的开销则小于30%个核。
8.1.3 速率限制和性能隔离
我们证明了第6章提到的速率限制的表现。在图13中,我们在不同宿主机的两个容器之间发起了一个单独的数据流,在 Infiniband实验台上。我们限制了数据流的速率,并设置了不同的带宽,从1Gbps到40Gbps。我们可以看到被控制的带宽(y轴)接近于我们设置的带宽(x轴)。FreeFlow完成这个功能仅花费了6%的CPU开销。
FreeFlow可以针对不同的容器的单个性能指标(如吞吐量)使用不同是限速值。为了证明这一点,我们运行了10个并发是数据流,并应用不同的限速值给每个流,从1Gbps到10Gbps,我们验证了每个流的实际吞吐量和预期精确匹配。
8.1.4 TCP Socket over RDMA
启用虚拟RDMA可以给基于socket的应用带来好处。下文我显示了,FreeFlow在rsocket(一种现有的socket-to-verbs翻译层)的帮助下,提供了比传统TCP/IP虚拟网络更好的性能。
我们在 InfiniBand和RoCE集群上都进行了实验。通过在运行时动态地使用rsocket进行连接,应用是socket调用被透明地转化成verbs调用。我们运行iperf来测量TCP吞吐量,运行NPtcp来测量TCP延迟,对这些工具没有做任何修改。我们比较了同种工具运行在虚拟网络和主机网络下的情况。
如图14所示,FreeFlow总是比Weave性能更好。尤其是小消息的延迟,FreeFlow甚至比host TCP/IP的延迟还低,提升了98%。对于吞吐量,FreeFlow有时候会比host TCP差,并且不能像原生RDMA那种取得满格的吞吐量,因为socket-to-verbs翻译的开销。但它在大消息场景中仍然比Weave要大6.8到13.4倍(???)。
FreeFlow的高性能有两个原因。首先,RDMA栈和FreeFlow架构只在用户态工作,避免了内核TCP栈中的上下文切换。这个好处不是独特的,自定义的用户态网络栈也能达到这个效果。第二个原因,FreeFlow比Weave性能好是基础的。现有的TCP/IP虚拟网络方案执行从主机网络到虚拟网络一个一个网络包的地址转换。而FreeFlow从虚拟连接到物理连接执行基于消息的转换,因此,FreeFlow总是表现比Weave,尽管rsocket引入了一些socket-to-verbs的翻译开销。
8.2 真实世界的应用
在本章中,我们展示了运行在容器中的TensorFlow和Spark的性能。我们比较了FreeFlow,Host-RDMA,Host-TCP和Weave的应用性能。
因为TensorFlow要求使用的GPU我们的RoCE集群没有,我们所有试验都在 InfiniBand集群上进行。
8.2.1 TensorFlow
我们运行启用了RDMA的Tensorflow(v1.3.0)在InfiniBand集群的三个服务器上。我们修改了Tensorflow的一行源代码来替换原有的内存分配函数为我们的自定义内存分配函数(4.3)。每台服务器有8个GPU(NVIDIA GTX 1080 Ti)。其中一台服务器是master节点及parameter服务器,其他两个服务器是worker。我们运行两种主要类型的深度学习训练任务:基于CNN的图像识别和基于RNN的语音识别。
对于图像识别,我们运行了3个指定的模型:ResNet-50, Inception-v3和AlexNet。我们使用合成的图像集数据作为训练数据。图15(a)显示了每秒的中值训练速度。对于三种模型的结果,我们得出结论,首先,网络性能在分布式训练中确实是一个瓶颈。对比Host-RDMA和Host-TCP,Host-RDMA的训练速度是Host-TCP的1.8到3倍。FreeFlow和Weave的差距更大。例如FreeFlow在AlexNet中运行的速度要快14.6倍。其次,FreeFlow表现十分接近Host-RDMA,差距小于4.9%,FreeFlow有时候还更快,但我们推测这是测量误差。
对于语音识别,我们运行了一个私有的RNN模型,包括了一个双向的编码器和全连接的解码层。数据集是包括了1860万个样本的4G数据。在每个训练步骤,GPU从一小片数据中学习并且通过通信来彼此同步。图15(b)显示了每个训练步骤的CDF的时间花费,包括GPU时间和网络时间。同样,FreeFlow非常接近于Host-RDMA,训练时间的中值比Weave要快8.7倍。
Spark
我们在两台服务器上运行Spark(v2.1.0)。其中一台服务器运行了一个master容器,用于调度slave容器上的任务。所有的服务器都运行了slave容器。Spark的RDMA扩展的实现是闭源的。我们下载从他们的官网上下载了二进制文件,并且没有做任何修改。
我们展示了Spark发行版自带的基准测试工具——GroupBy和SortBy。每个基准工具运行在262144个键值对上,大小为2KB。我们设置了Spark的mapper和reducer数量为8,每个都为单线程。图16展示了结果。网络性能对首尾相连的应用性能影响确实很大。当运行FreeFlow时,性能非常接近于运行在Host-RDMA,比Host-TCP也要好,是Weave是1.8倍。
9 相关工作
容器的RDMA虚拟化
有一个正在进行的工作, Mellanox扩展Linux内核的网络命名空间和cgroup以便在网络隔离上适应RDMA。它使用了MACVLAN来把一个物理接口分割成多个虚拟接口,插入一个或多个接口到容器上,依赖VLAN路由来传送流量到正确的接口上。显然,这种云环境中会有可移植性问题,因为移动一个IP意味着要更新硬件上的VLAN路由信息。而且,它没有提供灵活的控制性,因为它允许容器直接访问物理NIC。
另一个方法是使用可编程硬件的方式来为容器提供RDMA虚拟化,如smart NIC或FPGA。FreeFlow和这些基于硬件的解决方案相比的优点是,通过使用商业硬件的成本更低,并且可以更灵活地定制网络特性。
VM的RDMA虚拟化
HyV是最解决FreeFlow的解决方案。它也拦截了应用和NIC驱动之间的通信,并且提供了地址转换,QP/CQ映射和内存映射。关键的不同点在于HyV没有控制数据路径来在私有集群中提供裸金属性能,而FreeFlow做到了这一点,从而适应了云环境。这也给FreeFlow带来了更多挑战,比如在数据路径上对应用透明的同时,保持接近裸金属的性能。VMM-bypass I/O有类似的设计。VMWare正在虚拟化RDMA设备上投入工作,称为vRDMA,vRDMA是为VMWare的hypervisor和虚拟机设计的,所有它不能在容器上运作。
10 结论
本文中,我们展示了FreeFlow,一个虚拟RDMA网络方案,它提供了容器云所需的隔离性,可移植性和可控制性。FreeFlow对应用透明并且在可接受的开销下取得了解决裸金属RDMA的性能。在真实应用和微基准测试工具的测评中显示了FreeFlow可以支持相当于裸金属RDMA的性能,并且比现有的TCP/IP性能要好得多。