文章目录
摘要介绍System OverviewFrontend Serving ArchitectureMaglev Configuration Forwarder Design and ImplementationOverall StructureFast Packet ProcessingBackend SelectionConsistent Hashing Operational ExperienceEvolution of MaglevFailoverPacket Processing VIP MatchingFragment Handling EvaluationLoad BalancingSingle Machine ThroughputKernel BypassTraffic TypeNIC Speed Consistent Hashing Related WorkConclusion参考摘要
Maglev 是谷歌的网络负载均衡器。这是一个已商用的大型分布式软件系统Linux服务器。与传统的硬件网络负载不同,Maglev 不需要专门的设备部署,其容量可以很容易通过添加或删除服务器变化。网络路由器通过等价路径路由 (ECMP)分发数据包到不同 Maglev;然后,每台 Maglev 将数据包发送到其相应的服务且将它们均匀地分布到服务端点。以适应高速和不断增长的流量,Maglev 针对数据包处理性能进行了优化。单个 Maglev 可以将 10Gbps 链路跑满(小包);Maglev 还配备了哈希和连接跟踪功能,最大限度地减少意外故障和面向连接的协议故障。它还提供谷歌云平台的网络负载平衡。
介绍
LB 通常由逻辑上位于路由器和服务端点(通常为TCP或UDP服务器之间的多个设备组成,如图,LB 负责将每个数据包与其对应的服务进行匹配,以及将其转发到该服务的一个端点。
传统上,网络负载平衡器被实现为专用硬件设备,这种方法有几个局限性。首先,他们的可扩展性通常受到单个单元的最大容量的限制;第二,高可用性的要求。虽然通常成对部署以避免单点故障,它们只提供1+1冗余。第三,他们缺乏快速迭代所需的灵活性和可编程性。第四,升级成本高昂。增强硬件负载平衡器的容量通常还需要购买新的硬件。
所有服务都托管在集群服务器,我们可以构建网络负载平衡器作为在这些服务器上运行的分布式软件系统。软件负载平衡系统比硬件负载平衡系统有许多优点。我们可以通过采用横向扩展模型来解决可伸缩性问题,通过增加系统中的机器数量:通过ECMP转发,流量可以均匀分布在所有机器。可用性和可靠性得到了增强,系统提供N+1冗余。通我们可以自己快速添加、测试整个系统,并部署新功能。同时,部署负载平衡器本身被大大简化:系统只使用集群内现有的服务器。我们还可以在多个负载碎片之间划分服务在同一集群中使用平衡器,以实现性能隔离。
尽管有这些好处,设计和实现软件网络负载平衡器的而且很有挑战性。首先,系统必须提供高吞吐量。设N为系统中的机器数量,T为最大值单机吞吐量。系统的最大容量以N×T为界。如果T够高,系统为所有服务提供足够的容量是不经济的。系统组件整体还必须提供连接持久性:属于同一连接的数据包应始终指向同一服务端点。这确保了服务质量,因为集群是非常动态的,并且出现故障是很常见的。
本文介绍了一种快速可靠的软件网络负载平衡系统Maglev。通过一致性通过哈希和连接跟踪,Maglev可 以在频繁更改和意外故障的情况下提供可靠的数据包传递。
System Overview
每个服务都有一个或多个虚拟IP地址(VIP)。VIP 与中的物理 IP 不同,它没有分配给特定的网络接口,而是由 Maglev 背后的多个服务端点提供服务。将每个VIP与一套服务端点联系起来并通过 BGP 向路由器宣布;这个路由器依次向谷歌主干网宣布 VIP。VIP 网络聚合将向互联网使其全球可访问。Maglev 处理 IPv4 和 IPv6 流量。
Frontend Serving Architecture
当用户访问谷歌服务时,浏览器首先发出 DNS 查询,DNS 权威服务器根据用户的地理位置和每个位置的当前负载,将用户分配到指定位置,并返回所选位置的 VIP 作为回复。然后浏览器与 VIP 进行连接;当路由器收到访问 VIP 数据包时,它会转发数据包通过 ECMP 发送到集群中的一台 Maglev 机器,因为所有 Maglev 机器都会宣布同样费用的VIP。当 Maglev 机器接收数据包,它从集合中选择一个端点,并使用通用路由封装来封装数据包(GRE)将外部 IP 标头发送到端点。
当数据包到达所选服务端点时,它将被解封装并使用。准备就绪后,响应被放入IP数据包中,源地址为VIP,目标地址为用户的IP。我们使用直接服务器返回(DSR)将响应直接发送到路由器,以便 Maglev 不需要处理返回的数据包,这些数据包通常是尺寸更大。
Maglev Configuration
Maglev 负责向路由器宣布 VIP,并将 VIP 流量转发给服务端点。因此,如图所示,每个 Maglev 都包含一个控制器和一个转发器。控制器和转发器都从配置对象学习要服务的 VIP,这些配置对象要么从文件读取,要么通过RPC 从外部系统接收。
在每台 Maglev 上,控制器定期检查转发器的健康状态。根据结果,管制员决定是否通过 BGP 宣布或撤回所有 VIP。这确保路由器只将数据包转发给健康的Maglev 机器。
Maglev 接收到的所有 VIP 数据包均由转发器处理。在转发器中,每个 VIP 都配置有一个或多个后端池。除非另有规定,否则 Maglev 的后端是服务端点。后端池可以包含服务端点的物理 IP 地址;它还可以递归地包含其他后端池,因此不需要重复指定经常使用的后端集。每个后端池根据其特定需求与一个或多个健康检查方法相关联,所有后端都使用这些方法进行验证;数据包将只转发到正常的后端。由于同一台服务器可能包含在多个后端池中,因此通过 IP 地址消除运行状况检查的重复,以避免额外的开销。
转发器的配置管理器负责在更改转发行为之前解析和验证配置对象。所有配置更新都以原子方式提交。由于配置推送或运行状况检查的延迟,同一集群内的 Maglev 配置可能会暂时不同步。然而,一致的散列将使具有类似后端池的 MagLev 之间的连接切换大多成功,即使在这些非常短的窗口中也是如此。
可以在同一集群中部署多个 Maglev 碎片。不同的Maglev 碎片配置不同,为不同的 VIP 提供服务。切分对于提供性能隔离和确保服务质量非常有用。它还可以在不干扰常规通信的情况下测试新功能。
Forwarder Design and Implementation
转发器是 Maglev 的关键组件,因为它需要快速可靠地处理大量数据包。本节解释了 Maglev 转发器关键模块的设计和实施细节,以及设计背后的基本原理。
Overall Structure
如图 显示了 Maglev 转发器的总体结构。转发器从 3个 NIC(网络接口卡)接收数据包,用适当的 GRE/IP 标头重写数据包,然后将其发送回 NIC。Linux内核不参与此过程。
NIC 接收的数据包首先由转发器的引导模块处理,该模块计算数据包的 5元组哈希1,并根据哈希值将其分配给不同的接收队列。每个接收队列都连接到一个数据包重写器线程。数据包线程首先尝试将每个数据包与配置的 VIP 相匹配。此步骤过滤掉不以任何 VIP 为目标的不需要的数据包。然后,它重新计算数据包的5元组哈希,并在连接跟踪表中查找哈希值(见第3.3节)。我们不再次使用来自指导模块的哈希值,以避免跨线程同步。
连接表存储最近连接的后端选择结果。如果找到匹配项并且所选后端仍然正常,则只需重复使用结果。否则,线程将参考一致哈希模块(见第3.4节),并为数据包选择一个新的后端;它还向连接表中添加了一个条目,用于将来具有相同5元组的数据包。如果没有可用的后端,则丢弃数据包。转发器为每个数据包线程维护一个连接表,以避免访问争用。选择后端后,数据包线程用适当的 GRE/IP 头封装数据包,并将其发送到附加的传输队列。然后,muxing模块轮询所有传输队列,并将数据包传递给NIC。
由于两个原因,转向模块执行5元组哈希而不是循环调度。首先,它有助于降低由于不同数据包线程的处理速度不同而导致的连接内数据包重新排序的可能性。其次,对于连接跟踪,转发器只需要为每个连接执行一次后端选择,从而节省了时钟周期,消除了后端运行状况更新的竞争条件导致后端选择结果不同的可能性。在极少数情况下,如果给定的接收队列已满,指导模块会返回到循环调度,并将数据包传播到其他可用队列。这种回退机制在处理具有相同5元组的大量数据包时尤其有效。
Fast Packet Processing
Maglev 需要尽可能快地处理数据包,以便经济高效地扩展服务能力,以满足谷歌流量的需求。我们将其设计为以线路速率转发数据包,目前在谷歌集群中通常为10Gbps。对于1500 字节的 IP 数据包,这转换为813 Kpps(每秒数据包数)。
然而,我们的要求要严格得多:我们必须有效地处理非常小的数据包,因为传入的请求通常很小。假设 IP数据包的平均大小为 100 字节,转发器必须能够以9.06 mps的速度处理数据包。本小节描述了我们用来达到和超过此数据包处理速度的关键技术。
Maglev 是一个在商用 Linux 服务器上运行的用户空间应用程序。由于 Linux 内核网络堆栈在计算上相当昂贵,而且 Maglev 不需要 Linux 堆栈的任何功能,因此最好让 Maglev 完全绕过内核进行数据包处理。在NIC 硬件的适当支持下,我们开发了一种在转发器和 NIC 之间移动数据包的机制,而无需任何内核参与,如图所示。当 Maglev 启动时,它会预先分配一个在NIC 和转发器之间共享的数据包池。转发和 muxing 模块都维护指向数据包池中数据包的指针的环形队列。
转发和 muxing 模块都有三个指向环的指针。在接收端,NIC 将新接收到的数据包放在接收到的指针处,并将其前进。转发模块将接收到的数据包分发给数据包线程,并前进处理后的指针。它还保留来自数据包池的已使用数据包,将它们放入环中,并前进保留指针。如箭头所示,三个指针相互追逐。类似地,在发送端,NIC 发送由发送指针指向的数据包,并将其前进。muxing 模块将由数据包线程重写的数据包放入环中,并前进就绪指针。它还将 NIC 已经发送的数据包返回到数据包池,并前进回收的指针。请注意,转发器不会将数据包复制到任何地方。
为了减少昂贵的越界操作的数量,我们尽可能分批处理数据包。此外,数据包线程之间不共享任何数据,从而防止了它们之间的争用。我们将每个数据包线程固定到专用的 CPU 核心,以确保最佳性能。通过所有这些优化,Maglev 能够使用小数据包实现线路速率,如第5.2节所示。
此外,Maglev 为每个数据包所采用的路径增加的延迟很小。通常,在我们的标准服务器上处理每个数据包需要大约 350ns 的数据包线程。有两种特殊情况下,数据包处理可能需要更长的时间。由于转发器分批处理数据包,所以当每一批数据足够大或周期计时器过期时,就会对其进行处理。实际上,我们将计时器设置为 50µs。因此,如果 Maglev 严重欠载,在最坏的情况下,每个数据包将增加 50µs 延迟。对于这种情况,一种可能的优化方法是动态调整批量大小[32]。Maglev 可能增加额外处理延迟的另一种情况是Maglev 超载。Maglev 可以缓冲的最大数据包数是数据包池的大小;除此之外,数据包将被 NIC 丢弃。假设数据包池大小为3000,转发器可以处理10pps,则处理所有缓冲数据包大约需要 300µs。因此,如果Maglev 严重超载,则每个数据包最多可增加300µs的延迟。幸运的是,通过适当的容量规划和根据需要添加 Maglev 设备,可以避免这种情况。
Backend Selection
一旦数据包与 VIP 匹配,我们需要从 VIP 的后端池中为数据包选择后端。对于面向连接的协议(如TCP),将连接的所有数据包发送到同一后端至关重要。我们通过两部分策略来实现这一点。首先,我们使用一种新形式的一致性哈希选择后端,这种哈希可以非常均匀地分布流量。然后,我们将选择记录在本地连接跟踪表中。
Maglev 的连接跟踪表使用固定大小的哈希表将数据包的5元组哈希值映射到后端。如果表中不存在数据包的哈希值,Maglev 将为数据包分配后端并将分配存储在表中。否则,Maglev 将简单地重用以前分配的后端。这保证了属于同一连接的数据包总是发送到同一后端,只要后端仍然能够为它们提供服务。连接跟踪在后端集更改时非常方便:例如,当后端上下移动、添加或删除时,或者当后端权重更改时。
然而,在我们的分布式环境中,单凭 Maglev 连接跟踪是不够的。首先,它假设具有相同5元组的所有数据包始终发送到同一台 Maglev 机器。由于 Maglev 前方的路由器通常不提供连接亲和力,因此当 Maglev 发生变化时,这种假设不成立。不幸的是,这种变化是不可避免的,可能由于各种原因而发生。例如,当在集群中升级 Maglev 时,我们对机器进行滚动重启,提前几分钟排出每个机器的流量,并在 Maglev 再次开始服务时恢复流量。这一过程可能持续一个多小时,在此期间,Maglev 不断变化。我们有时也会添加、删除或更换 Maglev 机器。所有这些操作都使标准ECMP实现大规模地洗牌流量,导致连接在中游切换到不同的 Maglev。新的 Maglev 将没有正确的连接表条目,因此如果同时发生后端更改,连接将中断。
第二个理论限制是连接跟踪表的空间有限。该表可能在重载或 SYN 洪水攻击下填满。由于 Maglev 只在连接表中的条目过期时才将其逐出,一旦该表变满,我们将需要为每个不适合该表的数据包选择一个后端。虽然在实践中,现代机器上有大量内存,但在我们在Maglev 和其他服务之间共享机器的部署中,我们可能需要大幅限制连接表的大小。
如果出现上述任何情况,我们就不能再依靠连接跟踪来处理后端更改。因此,Maglev 还提供一致的哈希,以确保在这种情况下可靠的数据包传递。
Consistent Hashing
解决连接跟踪限制的一种可能的方法是在所有 Maglev 机器之间共享连接状态,例如在分布式哈希表中。然而,这会对转发性能产生负面影响–回想一下,为了避免争用,连接状态甚至不会在同一台 Maglev 机器上的数据包线程之间共享。
性能更好的解决方案是使用本地一致性哈希。一致散列(consistent hashing)或集合散列(rendezvous hashing)的概念于20世纪90年代首次引入。其思想是生成一个大的查找表,每个后端在表中包含许多条目。这些方法提供了 Maglev 在弹性后端选择方面也需要的两个理想特性:
负载平衡:每个后端将接收几乎相同数量的连接。最小中断:当后端集更改时,可能会将连接发送到与以前相同的后端。
都将最小中断置于负载平衡之上,因为它们旨在优化少量服务器上的 web 缓存。然而,Maglev 采用相反的方法有两个原因。首先,Maglev 必须在后端之间尽可能均匀地平衡负载。否则,必须大力过度配置后端,以适应高峰流量。对于某些 VIP,Maglev 可能有数百个后端,我们的实验表明,[28]和[38]都需要为每个 VIP 提供一个非常大的查找表,以提供 Maglev 所需的负载平衡水平。其次,虽然最小化查找表中断很重要,但 Maglev 可以容忍少量中断。稳定状态下,对查找表的更改不会导致连接重置,因为连接与Maglev 机器的关联不会同时更改。当连接与 MagLev 的关联发生变化时,重置与查找表中断的数量成正比。
考虑到这些因素,我们开发了一种新的一致性哈希算法,我们称之为 MagLev 哈希。Maglev 哈希的基本思想是为每个后端分配所有查找表位置的首选项列表。然后,所有后端轮流填充它们最喜欢的仍然为空的表位置,直到查找表完全填充完毕。因此,MagLev 哈希为每个后端提供了几乎相等的查找表份额。通过改变后端转弯的相对频率,可以实现异构后端权重;
Operational Experience
Evolution of Maglev
今天的 Maglev 在许多细节上与原来的系统不同。由于采用了可扩展的软件体系结构,大多数更改(例如添加了IPv6支持)都顺利进行。本小节讨论了自Maglev 技术诞生以来对其实施和部署的两大变化。
Failover
Maglev 最初是以主动-被动对部署的,以提供故障恢复能力,它们所更换的硬件负载平衡器也是如此。在正常情况下,只有活动机器服务于流量。当主动机器变得不健康时,被动机器将接管并开始服务。由于Maglev 哈希,连接在这个过程中通常是不间断的,但这种设置有一些缺点。它使用资源效率低下,因为有一半的机器一直处于闲置状态。这也阻止了我们将VIP 规模扩大到超过一台 Maglev 机器的容量。最后,主动和被动机器之间的协调是复杂的。在这种设置中,机器的广播将监控彼此的健康状况和服务优先级,如果彼此看不见,则会通过各种中断机制提升各自的 BGP 优先级。
通过转向 ECMP 模型,我们获得了大量的容量、效率和操作简单性。虽然 Maglev 哈希继续保护我们免受偶尔的ECMP攻击,但我们可以将 VIP 的容量乘以路由器的最大 ECMP 集大小,所有机器都可以得到充分利用。
Packet Processing
Maglev 最初使用 Linux 内核网络堆栈进行数据包处理。它必须使用内核套接字与 NIC 交互,这给数据包处理带来了巨大的开销,包括硬件和软件中断、上下文切换和系统调用[26]。每个数据包还必须从内核复制到用户空间,然后再复制回来,这会产生额外的开销。Maglev 不需要 TCP/IP 堆栈,只需要为每个数据包找到一个合适的后端,并使用 GRE 对其进行封装。因此,当我们引入内核旁路机制时,我们没有失去任何功能,并大大提高了性能——每台 Maglev 机器的吞吐量提高了五倍多。
VIP Matching
在谷歌的生产网络中,每个集群都分配了一个可全局路由的外部IP前缀。例如,图中的集群 C1 的前缀为74.125.137.0/24。同一服务在不同集群中配置为不同的 VIP,用户通过 DNS 定向到其中一个 VIP。例如,服务1在C1中配置为74.125.137.1,在C2中配置为173.194.71.1。
谷歌有几个不同类别的集群,为不同的 VIP 提供服务。对于同一类的集群,外部前缀长度相同,但对于不同的集群类,外部前缀长度可能不同。有时,在紧急情况下,我们需要通过Maglev 封装将流量重定向到不同的集群。因此,我们需要目标 Maglev 能够正确分类任意其他集群的流量。一种可能的解决方案是在所有集群中定义可能接收重定向流量的所有VIP,但这会导致同步和可伸缩性问题。
相反,我们实现了一种特殊的编号规则和一种新的 VIP 匹配机制来解决这个问题。对于每个集群类,我们在该类的所有集群中为每个 VIP 分配相同的后缀。然后使用前缀/后缀匹配机制进行 VIP 匹配。首先,传入的数据包经过最长前缀匹配,以确定其目的地是哪个集群类。然后,它进行特定于该集群类的最长后缀匹配,以确定应该将其发送到哪个后端池。为了减少在短时间内保持配置全局同步的需要,我们为每个集群类预先配置了一个大前缀组,从中为同一类的新集群分配前缀。这样一来,Maglev 就可以正确地服务于原本为其从未听说过的集群提供服务的交通。
因此,每个VIP都配置为<前缀组、IP后缀、端口、协议>元组。以图6为例。假设C2和C3属于同一类,如果C2中接收到指向173.194.71.1的数据包,但 Maglev 确定C2中的任何端点都不能为该数据包提供服务,则它将封装该数据包并通过隧道将其传输到C3中相同服务的VIP地址(173.194.72.1)。然后,C3中的 Maglev 将解密数据包,并使用前缀/后缀匹配将内部数据包与服务1匹配,而数据包将由C3中的端点提供服务。
这种 VIP 匹配机制特定于 Google 的生产设置,但它提供了一个很好的示例,说明了基于软件的负载平衡器可以提供快速原型和迭代的价值。
Fragment Handling
到目前为止,所描述的系统还没有涵盖的一种特殊情况是IP碎片。片段需要特殊处理,因为Maglev对大多数 VIP 执行5元组哈希,但片段并不都包含完整的5元组。例如,如果将一个大数据报拆分为两个片段,则第一个片段将同时包含 L3 和 L4 标头,而第二个片段将仅包含 L3 标头。因此,当 Maglev 接收到非第一个片段时,它无法仅基于该数据包的报头做出正确的转发决策。
为了正确处理碎片,Maglev 必须满足两个要求。首先,同一 Maglev 必须接收同一数据报的所有片段。其次,Maglev 必须为未分段的数据包、第一个片段和非第一个片段做出一致的后端选择决策。
一般来说,我们不能依靠 Maglev 前面的硬件来满足其自身的第一个要求。例如,一些路由器对第一个片段使用5元组哈希,对非第一个片段使用3元组哈希。因此,我们在 Maglev 中实现了一个通用解决方案,以处理任何片段哈希行为。每个 Maglev 都配置了一个特殊的后端池,由集群内的所有 Maglev 组成。收到片段后,Maglev 使用L3头计算其3元组哈希,并根据哈希值将其从池转发给 Maglev。由于属于同一数据报的所有片段都包含相同的3元组,因此保证将它们重定向到相同的 Maglev 。我们使用GRE递归控制字段来确保片段只重定向一次。
为了满足第二个要求,Maglev 使用相同的后端选择算法为未分段的数据包和第二跳第一段选择后端(通常在不同的Maglev实例上)它维护一个固定大小的片段表,该表记录第一个片段的转发决策。当同一台机器接收到第二跳非第一跳片段时,Maglev 在片段表中查找它,如果找到匹配项,则立即转发它;否则,它将缓存在片段表中,直到收到第一个片段或条目过期。
这种方法有两个局限性:它会给零碎的数据包引入额外的跳数,这可能会导致数据包重新排序。它还需要额外的内存来缓冲非第一个片段。由于数据包重排序可能发生在网络中的任何位置,因此我们依赖端点来处理无序数据包。实际上,只允许少数VIP接收片段,我们可以很容易地提供足够大的片段表来处理它们。
Evaluation
在本节中,我们将评估 Maglev 的效率和性能。我们展示了谷歌的一个生产集群以及一些微基准的结果。
Load Balancing
作为一个网络负载均衡器,Maglev 的主要职责是在多个服务端点之间均匀分布流量。为了说明 Maglev 的负载平衡性能,我们从位于欧洲的集群中的458个端点收集了每秒连接数(cps)数据。数据从多个HTTP服务(包括Web搜索)聚合而来。数据收集的粒度为5分钟,负载通过全天的平均cps标准化。图显示了一天中所有端点的负荷平均值和标准偏差。交通负荷呈现明显的日变化规律。与平均负荷相比,标准偏差总是很小;变异系数大多在6%-7%之间
图还显示了计算为每个时间点平均负载上的最大负载的超配置系数。这是一个重要的指标,因为我们必须确保即使是最繁忙的端点也始终有足够的容量来服务所有流量。在60%的时间内,超额供应系数小于1.2。非高峰时段的负荷明显较高,这是预期的行为,因为当流量较少时,很难平衡负荷。此外,在非高峰时段,更高的超提系数不需要添加 Maglev 。这为在该特定位置过度提供多少提供了指导。
Single Machine Throughput
由于每台 Maglev 机器通过 ECMP 接收的流量大致相等,因此可以通过 Maglev 机器数量乘以每台机器的吞吐量来估计磁悬浮的总吞吐量。每台机器能够处理的流量越多,提供相同前端容量所需的机器就越少。因此,单机吞吐量对系统的效率至关重要。
Maglev 机器的吞吐量受许多因素影响,包括数据包线程数、NIC速度和流量类型。在本小节中,我们报告了一个小型试验台的结果,以评估 Maglev 在各种条件下的数据包处理能力。除非另有规定,所有实验均在配备两个8核最新服务器级CPU、一个10Gbps NIC和128GB内存的服务器上进行。我们只为 Maglev 使用一个CPU。其他一切,包括操作系统,都在另一个CPU上运行。该试验台由位于同一以太网域中的两个发送器、两个接收器和一台 Maglev 机器组成。发送方缓慢增加其发送速率,Maglev 的吞吐量记录为开始丢弃数据包之前 Maglev 能够处理的每秒最大数据包数(pps)2。我们使用两个发送器来确保 Maglev 最终超载。
Kernel Bypass
在本实验中,我们在普通 Linux 网络堆栈模式和内核旁路模式下运行 Maglev,以评估内核旁路对 Maglev吞吐量的影响。发送方被配置为从不同的源端口发送最小大小的 UDP 数据包,以便引导模块不会将它们分配给相同的数据包线程。由于测试环境的限制,发送方可以发送的 UDP 数据包的最小大小为 52 字节,略大于以太网的理论最小值。我们在每次运行实验时改变数据包线程的数量。每个数据包线程都固定在一个专用的CPU核上(就像我们在生产中所做的那样),以确保最佳性能。我们使用一个内核进行引导和muxing,因此最多可以有7个包线程。我们测量了有无内核旁路的 Maglev 吞吐量,结果如图所示。
该图显示了在内核旁路模式下运行 Maglev 的明显优势。在那里,当包线程不超过4个时,Maglev 是瓶颈;它的吞吐量随着数据包线程数的增加而增加。但是,当有5个或更多数据包线程时,NIC将成为瓶颈。另一方面,Maglev 始终是使用 vanilla Linux网络堆栈时的瓶颈,达到的最大吞吐量不到内核旁路的30%。
Traffic Type
根据包线程中的代码执行路径,Maglev 以不同的速度处理不同类型的流量。例如,数据包线程需要为 TCP SYN 数据包选择后端,并将其记录在连接跟踪表中;它只需要在连接跟踪表中查找非 SYN 数据包。在这个实验中,我们测量了 Maglev 处理不同类型TCP数据包的速度。
考虑了三种流量类型:SYN、non-SYN 和 constant-5-tuple。对于SYN和非SYN实验,分别只发送SYN和非SYN TCP数据包。SYN实验显示了 Maglev 在 SYN洪水攻击期间的行为,而非SYN实验显示了 Maglev 如何与常规TCP流量一起工作,只执行一次后端选择,然后使用连接跟踪。对于常量5元组实验,所有数据包都包含相同的L3和L4头。这是一种特殊情况,因为引导模块通常尝试将具有相同5元组的数据包发送到相同的数据包线程,并且仅在所选的一个线程已满时将其传播到其他线程。发送方改变SYN和非SYN实验的源端口以生成不同的5元组,但始终使用相同的源端口进行常量5元组实验。它们总是发送最小大小的TCP数据包,在我们的测试环境中是64字节。
与之前的实验一样,Maglev 在非SYN和常量5元组实验中使用5个数据包线程达到NIC的容量。然而,对于SYN数据包,我们发现Maglev需要6个数据包线程来饱和NIC。这是因为Maglev需要为每个SYN数据包执行后端选择。Maglev在恒定的5元组流量下表现最好,表明转向模块可以有效地控制低分布的数据包模式。由于所有数据包都具有相同的5元组,因此它们的连接跟踪信息始终保留在CPU缓存中,以确保最高的吞吐量。对于非SYN数据包,存在用于连接跟踪查找的零星缓存未命中,因此当数据包线程少于5个时,吞吐量略低于恒定5元组流量的吞吐量。
NIC Speed
在之前的实验中,NIC是瓶颈,因为它被5个数据包线程饱和。为了了解Maglev 的全部性能,本实验使用更快的NIC评估其吞吐量。我们在Maglev 机器上安装40Gbps NIC,而不是10Gbps NIC,并使用与第5.2.1节中相同的设置。结果如图所示。当数据包线程不超过5个时,40Gbps NIC提供的吞吐量略高,因为其芯片速度比10Gbps快。然而,在使用7个数据包线程之前,40Gbps NIC的吞吐量增长不会减缓。
由于NIC不再是瓶颈,此图显示了当前硬件的Maglev 吞吐量上限,略高于15pps。事实上,这里的瓶颈是Maglev 转发模块,当我们将来切换到40Gbps NIC时,这将是我们优化的重点。
Consistent Hashing
在本实验中,我们评估了Maglev 哈希,并将其与Karger[28]和Rendezvous[38]哈希进行了比较。我们对两个指标感兴趣:负载平衡效率和后端更改的弹性
为了评估这些方法的负载平衡效率,我们使用每个方法填充一个查找表,并计算分配给每个后端的表条目数。我们将后端总数设置为1000,查找表大小设置为65537和655373 立方。对于Karger,我们将视图数设置为1000。图显示了每个方法和表大小的每个后端条目的最大和最小百分比。
正如所料,无论表大小如何,Maglev 哈希都能提供近乎完美的负载平衡。当表大小为65537时,Karger和Rendezvous要求后端分别超额提供29.7%和49.5%,以适应不平衡的流量。随着表格大小增加到655373,数字分别下降到10.3%和12.3%。由于每个VIP有一个查找表,因此必须限制表的大小,以扩展VIP的数量。因此,Karger和Rendezvous不适合Maglev 的负载平衡需求。
一致性哈希的另一个重要指标是对后端更改的弹性。Karger和Rendezvous都保证,当某些后端出现故障时,其余后端的条目不会受到影响。
因此,我们仅对 Maglev 的这一指标进行评估。图显示了作为并发后端故障百分比函数的更改表项百分比。我们将后端数设置为1000。对于每个故障数k,我们从池中随机删除k个后端,重新生成查找表并计算更改条目的百分比。我们对每个k值重复实验200次,并报告平均结果。
显示了更改条目的比率随并发故障数的增加而增加。当表大小较大时,Maglev哈希对后端更改更具弹性。在实践中,我们使用65537作为默认表大小,因为我们预计并发后端故障很少,而且我们仍然将连接跟踪作为主要的保护手段。此外,微基准测试表明,随着表大小从65537增加到655373,查找表生成时间从1.8毫秒增加到22.9毫秒,这阻止了我们无限期地增加表大小。
Related Work
与传统的硬件负载平衡器不同,Maglev是一个分布式软件系统,运行在商用服务器上。硬件负载平衡器通常部署为主动-被动对。通过在活动模式下运行所有服务器,Maglev提供了更好的效率和弹性。此外,升级硬件负载平衡器容量需要购买新硬件并进行物理部署,这使得按需容量调整变得困难。另一方面,Maglev 的容量可以轻松地上下调整,而不会造成任何服务中断。一些硬件供应商还提供在虚拟化环境中运行的负载平衡软件。Maglev 提供了比这些虚拟负载平衡器高得多的吞吐量。
还有许多通用的负载平衡软件包,其中最流行的是NGINX【14】、HAProxy【7】和Linux虚拟服务器【11】。它们通常在单个服务器上运行,但也可以在路由器后面的ECMP组中部署多个服务器,以实现横向扩展模型。它们都提供了一致的哈希机制。与Maglev 相比,它们大多将最小中断优先于均匀负载平衡,正如[28]和[38]所做的那样。因为它们是为可移植性而设计的,所以没有针对性能进行积极的优化。
一致性哈希[28]和集合哈希[38]最初是为了分布式缓存协调而引入的。这两种方法都提供了有保证的弹性,以便在删除一些后端时,只更新指向这些后端的表条目。然而,它们不能在后端之间提供良好的负载平衡,这是负载平衡器的基本要求。相反,Maglev的一致性哈希方法在后端实现了完美的平衡,但弹性略有降低,与连接跟踪相结合,在实践中效果良好。实现一致性哈希的另一个选项是分布式哈希表,如Chord[37],但这会给系统增加额外的延迟和复杂性。
最近开发了几种内核旁路技术,包括DPDK【8】、OpenOnload【15】、netmap【35】和PF-RING【17】等。在【10】中对流行的内核旁路技术进行了很好的总结。这些技术可以有效地加快数据包处理速度,但它们都有一定的局限性。例如,DKPK和OpenOnload与特定的NIC供应商绑定,而netmap和PF-RING都需要经过修改的Linux内核。在Maglev中,我们实现了一个灵活的I/O层,它不需要修改内核,并允许我们方便地在不同的NIC之间切换。与其他技术一样,一旦启动,Maglev 将接管NIC。它使用TAP接口将内核数据包注入回内核。
GPU最近开始流行于高速数据包处理[24,39]。然而,Kalia等人[27]最近表明,如果正确实施,基于CPU的解决方案能够以更高效的资源利用率实现类似的性能。
Conclusion
本文介绍了一种快速、可靠、可扩展、灵活的软件网络负载均衡器Maglev。我们建造了Maglev,以通过ECMP扩展,并在每台机器上以10Gbps的线路速率可靠地提供服务,从而在服务需求快速增长的情况下实现经济高效的性能。我们通过连接跟踪和Maglev哈希的组合将连接一致地映射到相同的后端。多年来,大规模运行该软件系统使我们能够有效地运营我们的网站,快速响应不断增长的需求和新功能需求。
参考
Maglev: A Fast and Reliable Software Network Load Balancer