由应用定义的网络 | HotNets 2023

随着微服务的兴起,许多云应用的执行环境变成了一组虚拟机或容器,并通过灵活且功能丰富的虚拟网络连接起来。本文认为,这种虚拟网络的实现应该完全特定于应用程序,而不是建立在互联网时代的通用网络之上,因为通用网络的分层架构往往会使应用程序的延迟和 CPU 负载增加一倍以上。为此本文提出了应用定义的网络(Application Defined Networks),其中开发者使用高级语言指定网络功能,控制器生成跨可用硬件和软件资源运行的自定义分布式实现。实验初步表明,与最先进的技术相比,ADN 将延迟降低了 20 倍,吞吐量提高了 6 倍。

来源:HotNets ’23
题目:Application Defined Networks
作者:Xiangfeng Zhu, Weixin Deng, Banruo Liu, Jingrong Chen, Yongji Wu, Thomas Anderson, Arvind Krishnamurthy, Ratul Mahajan, Danyang Zhuo
原文链接:https://conferences.sigcomm.org/hotnets/2023/papers/hotnets23_zhu.pdf
内容整理:李雨航

引言

自互联网诞生以来,数据网络的设计和实现就重视通用性——即支持尽可能多的应用的能力——并利用模块化组织实现这一目标。Internet 体系结构被组织为一个分层的协议栈。每个协议都提供特定的功能,构建在一个或多个低层协议之上。

然而,通用性和模块化会带来带宽、计算和延迟开销。应用程序消息可能被先包装在 HTTP 中,然后包装在 TCP 中,然后包装在 IP 中,并由发送方和接收方的多个协议按顺序处理。即便如此,通用网络通常也无法支持给定应用程序的所有要求。结果是,它对某些应用程序来说做的太多(成本很高),而对其他应用程序来说又做的太少。例如,许多分布式应用程序需要跨副本进行负载平衡,而 Internet 无法提供,迫使应用程序通过中间盒设计自己的解决方案。

对于一般网络来说,高开销和不完善的应用程序支持可能无法避免,但今天的许多网络都是为了支持单个应用程序而构建的。这种应用网络的关键驱动因素是微服务,其中应用逻辑被拆分为许多(有时甚至是数千种)服务。微处理器之间的通信具有丰富的需求,如负载均衡、限速、认证、访问控制和遥测等。工程师使用 Istio 和 Linkerd 等服务网格来构建满足这些要求的网络。这些网络工程实际上是隔离的,并具有特定的入口和出口来与外部通信。

但是问题在于,即使应用程序网络服务于单个应用程序,它们也是使用为通用通信而设计的相同抽象来构建的。而这种服务网格架构有明显的缺点,根据配置的不同,它会使消息处理延迟提高 2.7-7.1 倍,将 CPU 负载提高 1.6-7 倍。分层还会隐藏或遮蔽信息,这使得难以实现特定于应用程序的网络策略(例如根据应用程序RPC中的信息选择副本)。而且一般来说,服务网格的实现过于庞大且复杂,因此几乎不可能通过可编程内核、NIC 和交换机来加速它们。

我们主张应用程序网络应完全针对应用程序及其部署环境进行定制。实现这一愿景的关键挑战是:如何实现自定义应用网络,而不会使每个应用程序的开发者为了实现他们自己的网络功能承受过多的负担。我们建议通过 应用定义网络(Application Defined Networks,ADN) 来应对这一挑战。ADN 在具有基本第 2 层连接的网络上运行,类似于云虚拟网络提供的连接。应用程序需要的任何其他内容都以高级、特定于域的语言表示。我们将规范语言定义为处理应用程序发出的 RPC 消息,因为该处理最相关。编译器采用此规范,并在可用的硬件和软件资源中生成高效的分布式实现,运行时间控制器根据负载和故障动态重新配置网络。

通用性的困境

由应用定义的网络 | HotNets 2023
图 1 在服务网格中的包处理

我们通过一个例子强调了使用通用抽象构建应用程序网络的陷阱。我们将应用程序视为 RPC 消息的源和接收器,将“网络”视为 RPC 在应用程序、发送和接收之间发生的一切。假设应用程序具有两个微服务 A 和 B。服务 B 是共享的,其两个位置 B.1 和 B.2 包含对象标识符空间的子集。应用程序开发者希望网络:

  • 1) 根据请求中的对象标识符对从 A 到 B.1 或 B.2 的 RPC 请求进行负载均衡
  • 2) 压缩和解压缩 RPC 有效负载
  • 3) 根据 RPC 请求中的用户和对象标识符执行访问控制。

我们可以将这些网络策略与应用程序代码本身一起实现,但这是不切实际的。网络策略通常独立于应用程序逻辑而发展,每次更改时修改应用程序源并重新部署这显然不可能。此外,对于信任问题,某些网络策略(即访问控制)必须在应用程序外部强制执行。因此,开发者需要在应用程序之外实现网络,即使它只为这个应用程序服务。

为实现通用性,现在的应用程序开发者不使用自定义请求处理器来检查和操作消息以实现所需的策略,而是依赖于标准化协议,例如HTTP。HTTP协议允许在其标头中嵌入任意信息,并修改应用程序以添加对象和用户标识符的标头。由于它们选择了 HTTP,因此 TCP 和 IP 也被选为附加层,将应用程序信息包装到其中。否则,我们的应用程序便不会关心这些层。

然后,开发者选择一个可以强制执行其策略的模块;此功能在 L7 代理中很常见。最后,他们需要一种机制,使应用程序的流量在发送到 B 时到达此模块。这可以通过拦截和重写应用程序生成的 IP 数据包(例如,使用 iptables)或使用 DNS 将 B.1 和 B.2 解析为模块的地址来实现。路由模块收到数据包后,会对其进行解析以提取 HTTP 标头,并将其发送到正确的 B 版本。

图 1 显示了生成的数据包路径和处理。应用程序 RPC 库序列化请求消息,内核网络堆栈(由 iptable 规则配置)将消息转发到代理,代理通常需要解析消息标头并反序列化有效负载以强制执行所需的策略。然后,代理对标头进行重新编码,并重新序列化消息以进行传输。今天的服务网格遵循这种架构范式。代理称为 sidecar,它们作为单独的用户空间进程(或容器)运行,拦截和操作所有传入和传出的数据包。这种方法的主要优点是它可以支持一系列应用,但它也有很大的缺点:

高开销: 数据包在堆栈中上下传输,并多次编码和解码。这在应用程序延迟和服务器 CPU 方面具有很高的开销。一些研究表明,服务网格可以降低吞吐量,增加延迟,并增加 CPU 负载 3-7 倍(在已经很高的 baseline 之上)。服务网格开销的一个主要组成部分是解析所有协议标头以恢复包装信息 。它们有时还实现了与较低层重叠的功能(例如,重试、速率限制),因为应用程序需要不同的语义。

不可移植性: 借助服务网格,开发者可以通过选择和链接特定的软件插件(如负载均衡器和记录器)来实现所需的网络行为。此类网络功能只能在 sidecar 的上下文中运行,并使用 vanilla IP 进行传输。这种限制与操作系统内核(通过 eBPF)的可编程性越来越强以及可编程网络硬件(NIC 和交换机)的可用性相抵触。许多标准化协议的解析和处理几乎不可能卸载到内核或硬件。由于硬件限制,使用可编程网络硬件通常需要定制的接头设计。基于 P4 的可编程交换机的交流信号约为每个网络数据包的前 200 字节。为了卸载负载均衡,我们必须将负载均衡器需要的字段放入数据包的前 200 个字节中,这在多层标头包装中可能不会发生。

扩展性差: 如果当前应用程序网络架构网格具有高度可扩展性,那么它们的高开销和不可移植性可能更容易容忍,但事实并非如此。难以使用标准协议表达的网络策略很难构建和部署。考虑一个请求路由策略,该策略将 T2 类型的 RPC 请求发送到特定服务实例,但仅当它遵循 T1 类型的 RPC 时。对于此类自定义功能,服务网格提供了一个插件框架。但是,用于此类插件的低级抽象(IP 或 HTTP 数据包,而不是 RPC)使它们很难开发,并且安全运行这些插件(例如 Web Assembly)的隔离机制进一步增加了开销。

方法

应用定义网络

由应用定义的网络 | HotNets 2023
图 2 RPC 处理链的可能实现

鉴于使用通用抽象和实现构建应用程序网络的陷阱,我们主张以完全针对应用程序及其部署环境定制的方式构建它们。默认情况下,应用程序下的网络和软件堆栈不应提供任何协议或抽象,但(虚拟)链路层除外,该链路层可以基于平面标识符(如 MAC 地址)将数据包传送到端点。云虚拟网络工作(例如,AWS VPC)提供了这种抽象,而像 VXLAN 这样的技术可以在任何地方实现它。

网络执行的所有其他操作都由应用程序开发者以特定于域的语言 (DSL) 指定。我们建议将此规范构建为一个元素链,每个元素都是对两个服务之间的 RPC 消息的操作。控制器决定如何在应用程序的部署环境中实现规范。根据可用资源,RPC 处理可能发生在 RPC 库(例如 gRPC)、内核内(例如,使用 eBPF)、单独的进程(目前的做法)、可编程硬件设备上或混合位置。控制器还可以选择并行运行多个元素或重新排序它们。

图 2 显示了控制器如何在不同的部署环境中实现所需的 RPC 处理。配置 1 显示了将负载均衡器和压缩部署为 RPC 库的一部分的情况(类似于 gRPC proxyless)。配置 2 将这些功能移动到发送方的操作系统内核和接收方的 SmartNIC。配置 3 将负载平衡和访问控制移动到可编程交换机,并在自动确定重新排序保留语义后对处理进行重新排序。在此示例中,不压缩以下负载均衡器使用的 RPC 字段足以保留语义。配置 4 复制 RPC 处理以提高吞吐量。

关键研究问题

实现 ADN 概念需要回答几个关键的研究问题。

问题1: 我们的 DSL 应该提供哪些抽象来指定 RPC 处理? 抽象应该是高级的,独立于底层平台,同时适合于有效的实现。它们还应

  • 1) 允许一系列自动优化,例如重新排序、卸载和生成最少的标题;
  • 2)能够对元素的内部状态进行推理,因为这是无缝迁移和扩展的关键。

我们还希望使开发者能够重用其他人开发的电子代码,而不必每次都实现自己的代码。元素重用需要仔细考虑,因为没有标准标头(如 HTTP),并且操作一个应用程序的 RPC 字段的元素不一定在另一个应用程序中起作用。最后,我们应该让开发者指定消息排序和可靠性约束以及任何元素位置约束(例如,加密元素必须与发送者位于同一位置)。

问题2: 如何将高级规范转化为跨一系列硬件和软件平台的高效分布式实现? 这包括用于跨设备通信的低级代码(例如 eBPF、P4)和数据包标头设计。当多个元素在同一设备上运行时,我们应该能够进行跨元素优化。最后,我们需要确定满足网络要求所需的最小标头集。

问题3: 如何在不中断应用程序的情况下确定网络处理在可用资源中发生的位置,并根据工作负载扩展/折叠过程? 部署新应用程序时,ADN 控制器需要根据规格和可用资源选择初始配置。应用程序运行后,可能需要重新配置(例如选择一个图 2 中的配置)根据当前工作负载进行配置。当工作负载增加时,我们可能需要将 RPC 处理链横向扩展到更多计算设备。此类重新配置不应中断应用程序。

一种可能的实现

由应用定义的网络 | HotNets 2023
图 3 ADN 架构

如图 3 所示,我们给出了一种实现 ADN 的潜在方法,它回答了上述问题。输入程序将网络功能描述为 DSL 中的 ADN 元素链。控制平面包括编译器和运行时间控制器,而数据平面由执行网络功能的基于硬件或软件的处理器组成。

编程抽象

作为主要的编程抽象,我们从流处理系统(如 Dataflow SQL)中汲取灵感,并将每个 RPC 视为具有一个或多个字段的元组。元素处理传入的元组流,其处理逻辑在类似 SQL 的 DSL 中指定,然后编译为本机设备代码。每个元素 都可以读写建模为表的内部状态。处理逻辑输出零个或多个元组。修改 RPC 时,输出字段与输入字段不同。管道中的下游元素可以读取和进一步编辑这些字段。

由应用定义的网络 | HotNets 2023
图 4 实现访问控制的元素

图 4 显示了一个实现访问控制的元素。该元素将其状态保存在 ac_tab 表中,而 ac_tab 表保存了用户名到许可的映射。它使用这个状态来根据输入表生成输出表,输入表仅包含传入 RPC 的单行。该元素阻止没有写入权限的用户,并在每次 RPC 到达时执行,向下游发送新输出。

控制平面

ADN 控制器是一个逻辑集中的组件,它拥有网络拓扑、服务定位和可用 ADN 处理器的全局知识(通过 Kubernetes 等集群管理器获得)。它在可用处理器上设置网络处理。

为了响应工作负载变化和故障,它还对 ADN 元素进行迁移和缩放。代码和状态的解耦,以及状态的表格性质,使我们能够在不中断应用程序的情况下重新配置网络。要迁移或横向扩展负载均衡器,控制器可以复制其状态并开始运行新实例;在减少负载均衡器实例数量的同时,它可以合并其状态并终止某些实例。一些重新配置可能需要我们将网络置于中间状态,以防止传输中断。状态解耦还使我们能够热更新元素处理逻辑。

ADN 编译器采用 ADN 元素,并为目标平台生成高效的实现。在内部,编译器首先将程序转换为中间表示 (IR)。然后,它会对 IR 应用一组优化。例如,如果两个元素不对相同的 RPC 字段进行操作,则可以并行执行它们。最后,编译器将优化的 IR 转换为平台原生代码。

数据平面

ADN 数据平面由 ADN 处理器组成,这些处理器执行 ADN 元素的低级别执行。每个处理器从控制平面获取 RPC 处理逻辑的编译版本,并定期将日志记录、跟踪和运行时统计信息的报告发送回控制器。ADN 处理器可以在软件(以 RPC 库、用户空间代理或 eBPF 的形式)或硬件(如 SmartNIC 或可编程交换机)中实现。ADN 处理器可能只管理处理图的一部分,如果是这种情况,RPC 标头可能会传达用于下游处理器的其他信息。

实验

为了帮助评估上述方案的可行性和性能,我们实现了一个原型。我们的原型与 Kubernetes 集成。我们创建了一个名为 ADNConfig 的 Kubernetes 自定义资源,开发者使用它来提供 ADN 程序。ADN 控制器监视对此资源或部署(例如,新的服务副本)的更改。当其中任何一个发生更改时,它会更新数据平面处理器。我们的原型仅支持 mRPC,一种托管的 RPC 系统服务,作为处理器。我们使用 TCP/IP 作为 mRPC 的传输。ADN 编译器将基于 SQL 的高级 DSL 转换为基于 Rust 的 mRPC 模块(即引擎的实现)。支持其他平台的工作正在进行中。

在我们的 DSL 中实现了多个元素,其中在评估中使用的几个元素是:

  • 1)日志记录,它记录了对文件的请求和响应
  • 2)访问控制列表(ACL),它检查RPC参数并根据一组规则删除RPC
  • 3)故障注入,它根据配置的概率中止请求。有趣的是,对于这些元素来说,标准的 SQL 语法足够丰富。

实验设置

我们使用一个简单的客户端-服务器应用程序来评估我们的原型。客户端使用单个线程不断发送 128 个并发 RPC 请求。RPC 请求和响应都包含一个短字节字符串。ADNnetwork 规范链接了上述三个元素。也就是说,RPC 被记录、访问控制,其中一些被丢弃。

我们将原型的性能与将 Envoy 代理和 gRPC 一起使用的标准方法进行了比较。我们还与手写的 mRPC 模块进行了比较,以了解我们的 DSL 与 Rust(mRPC 的语言)相比,其开发难易性以及自动生成代码对性能的影响。mRPC 模块由 mRPC 开发者编写,以实现高性能。我们在两台机器上运行实验,配备两个 Intel 10 核 Xeon Gold 5215 CPU 和 256GB RAM,Ubuntu 20.04。我们使用 Envoy v1.20。

实验结果

由应用定义的网络 | HotNets 2023
图 5 ADN 与 Envoy + 手动编码 mRPC 模块的性能比较

结果如图 5 所示。与将 Envoy 用于相同的网络功能相比,ADN 的 RPC 速率提高了 5-6 倍,RPC 延迟降低了 17-20 倍。使用 Envoy 的性能开销来自当前的服务网格架构,该架构需要解析/序列化标准协议(gRPC、HTTP)标头,并具有额外的 RPC 有效负载的排序/解组。Envoy 的 RPC 进程也比我们的应用程序开销更大,因为用于日志记录、访问控制和故障注入的过滤器更通用。

与手工优化的 mRPC 模块相比,ADN 模块的性能降低了 3-12%。这种降级主要是由于 ADN 的编程抽象。在代码行数方面,为了便于开发,ADN 元素有几十行 SQL,而手写的 mRPC模块有几百行 Rust。

讨论

ADN 需要修改应用程序的源代码吗? 不。我们可以通过修改 gRPC 等 RPC 库来实现,而无需修改源代码的 ADN。应用程序通过此类库发送和接收 RPC 消息,我们的修改将处理消息并根据 ADN 控制器确定的实现将它们转发给其他进程。通过与修改后的库链接,ADN 可以直接替代现有的服务网格。

ADN应用程序如何与外部通信? ADN 聚焦于为应用程序量身定制一个网络,但此应用程序可能需要与其他应用程序和外部客户端进行通信。与服务网格一样,此类通信可以通过应用程序的指定入口和出口位置进行。入口位置将传入的 IP 数据包转换为 ADN 格式,而出口位置执行反向转换。当两个基于 ADN 的应用程序进行通信时,我们可以直接在两个 ADN 之间转换信息,而不是将发送方 ADN 的消息转换为 mat 的标准格式,然后将标准格式转换为接收方 ADN 的格式。这种“应用程序对等互连”不仅消除了一个转换步骤,而且消除了将应用程序消息“下移”到 IP 并返回的需要。

结论

借助 ADN,开发者可以用高级语言指定应用程序所需的网络功能。然后,将自动生成针对应用程序和部署环境自定义的分布式实现。ADN 不仅像手套一样适合应用程序,还可以利用异构硬件并随工作负载扩展。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论