在云中使用 eBPF 进行网络可观测性

eBPF 扩展了 Linux 内核,以帮助您监控云。
尚无读者喜欢这篇文章。
Parts, modules, containers for software

Opensource.com

可观测性是指了解和解释当前部署状态的能力,以及了解何时出现异常的方法。随着应用程序作为微服务在 Kubernetes 和 OpenShift 上云部署的增长,可观测性受到了广泛关注。许多应用程序都有严格的保证,例如针对停机时间、延迟和吞吐量的服务级别协议 (SLA),因此网络级可观测性是一项非常重要的功能。网络级可观测性由多种编排器提供,可以通过原生方式或使用插件和操作符来实现。

最近,eBPF(扩展的 Berkeley 数据包过滤器)作为在终端主机内核中实现可观测性的流行选项而兴起,这归功于其性能和灵活性。此方法允许将自定义程序挂钩到网络数据路径上的特定点(例如,套接字、TC 和 XDP)。已经发布了多个基于 eBPF 的开源插件和操作符,每个插件和操作符都可以插入到终端主机节点中,以通过您的云编排器提供网络可观测性。

现有的可观测性工具

可观测性模块的核心组件是如何以非侵入方式收集必要的数据。为此,我们使用检测代码和测量,研究了 eBPF 数据路径的设计如何影响可观测性模块的性能以及它所监控的工作负载。我们的测量工件是开源的,并且可以在我们的研究 Git 仓库中找到。我们还能够提供一些有用的见解,您可以在设计可扩展且高性能的 eBPF 监控数据路径时使用这些见解。

以下是可用于在网络和主机上下文中实现可观测性的现有开源工具

Skydive

Skydive 是一个网络拓扑和流分析器。它将探针附加到节点以收集流级别的信息。探针使用 PCAP、AF_PacketOpen vSwitch 等等进行附加。Skydive 没有捕获整个数据包,而是使用 eBPF 来捕获流指标。附加到套接字挂钩点的 eBPF 实现使用哈希映射来存储流标头和指标(数据包、字节和方向)。

libebpfflow

Libebpfflow 是一个使用 eBPF 提供网络可见性的网络可见性库。它挂钩到主机堆栈中的各个点,如内核探针 (inet_csk_accept, tcp_retransmit_skb) 和跟踪点 (net:netif_receive_skb, net:net_dev_queue),以分析 TCP/UDP 流量状态、RTT 等。此外,它还提供进程以及它分析的流量的容器映射。它的 eBPF 实现使用 perf 事件缓冲区向用户空间通知 TCP 状态更改事件。对于 UDP,它附加到网络设备队列的跟踪点,并使用 LRU 哈希映射和 perf 事件缓冲区的组合来存储 UDP 流指标。

eBPF Exporter

Cloudflare 的 eBPF Exporter 提供了用于插入自定义 eBPF 代码以记录感兴趣的自定义指标的 API。它需要将整个 eBPF C 代码(以及挂钩点)附加到 YAML 文件以进行部署。

Pixie

Pixie 使用 bpftrace 跟踪系统调用。它使用 TCP/UDP 状态消息来收集必要的信息,然后将其发送到 Pixie Edge Module (PEM)。在 PEM 中,数据根据检测到的协议进行解析并存储以供查询。

Inspektor

Inspektor 是用于 Kubernetes 集群调试的工具集合。它有助于将低级内核原语与 Kubernetes 资源进行映射。它作为守护程序集添加到集群的每个节点上,以使用 eBPF 收集系统调用等事件的跟踪。这些事件被写入 perf 环形缓冲区。最后,当发生故障时(例如,在 pod 崩溃时),追溯地使用环形缓冲区。

L3AF

L3AF 提供了一组 eBPF 包,这些包可以使用尾调用打包和链接在一起。它提供了一个网络可观测性工具,该工具根据流 ID 将流量镜像到用户空间代理。此外,它还通过将流记录存储在 eBPF 数据路径中的哈希映射上来提供 IPFIX 流导出器。

Host-INT

Host-INT 扩展了带内网络遥测支持,以支持主机网络堆栈的遥测。从根本上讲,INT 将每个数据包产生的交换延迟嵌入到数据包中的 INT 标头中。Host-INT 对两个主机之间主机网络堆栈执行相同的操作。Host-INT 有两个数据路径组件:基于 eBPF 的源和接收器。源在发送主机接口的 TC 挂钩上运行,接收器在接收主机接口的 XDP 挂钩上运行。在源处,它使用哈希映射来存储流统计信息。此外,它还在 INT 标头中添加入口/出口端口、时间戳等等。在接收器处,它使用 perf 数组在每个数据包到达时将统计信息发送到接收器用户空间程序,并将数据包发送到内核。

Falco

Falco 是一个云原生运行时安全项目。它使用 eBPF 探针监控系统调用,并在运行时解析它们。Falco 具有配置针对活动发出警报的功能,例如使用特权容器的特权访问、读取和写入内核文件夹、用户添加、密码更改等。Falco 包括一个用户空间程序作为 CLI 工具来指定警报并获取解析的系统调用输出,以及一个基于 libscap 和 libsinsp 库构建的 falco 驱动程序。对于系统调用探针,falco 使用 eBPF 环形缓冲区。

Cilium

Cilium 中的可观测性通过 eBPF 启用。Hubble 是一个平台,其 eBPF 挂钩在集群的每个节点上运行。它有助于深入了解彼此通信的服务,以构建服务依赖关系图。它还有助于第 7 层监控,以分析例如 HTTP 调用以及 Kafka 主题,第 4 层监控,包括 TCP 重传率等等。

Tetragon

Tetragon 是 Cilium 中用于安全和可观测性的可扩展框架。tetragon 的底层支持者是 eBPF,数据使用环形缓冲区存储,但除了监控之外,eBPF 还用于强制执行跨越各种内核组件(例如虚拟文件系统 (VFS)、命名空间、系统调用)的策略。

Aquasecurity Tracee

Tracee 是一种事件跟踪工具,用于调试基于 eBPF 构建的行为模式。Tracee 在 tc、kprobes 等处有多个挂钩点,用于监控和跟踪网络流量。在 tc 挂钩处,它使用环形缓冲区 (perf) 将数据包级事件提交到用户空间。

重新审视流指标代理的设计

虽然不同工具的动机和实现方式有所不同,但所有可观测性工具通用的核心组件是用于收集可观测性指标的数据结构。虽然不同的工具采用不同的数据结构来收集指标,但尚没有现有的性能测量来查看用于收集和存储可观测性指标的数据结构的影响。为了弥合这一差距,我们使用不同的数据结构实现了模板 eBPF 程序,以从主机流量中收集相同的流指标。我们使用 eBPF 中可用的以下数据结构(称为映射)来收集和存储指标

  1. 环形缓冲区
  2. 哈希
  3. 每 CPU 哈希
  4. 数组
  5. 每 CPU 数组

环形缓冲区

环形缓冲区

是 eBPF 数据路径和用户空间之间的共享队列,其中 eBPF 数据路径是生产者,而用户空间程序是消费者。它可用于将每个数据包的“明信片”发送到用户空间,以聚合流指标。虽然这种方法可能很简单并且可以提供准确的结果,但它无法扩展,因为它会为每个数据包发送明信片,这会使用户空间程序处于繁忙循环中。

哈希和每 CPU 哈希映射

(每 CPU)哈希映射可用于 eBPF 数据路径中,通过对流 ID(例如,5 元组 IP、端口、协议)进行哈希处理来聚合每个流的指标,并在流完成/不活动时将聚合信息驱逐到用户空间。虽然这种方法通过仅为每个流而不是每个数据包发送一次明信片来克服了环形缓冲区的缺点,但它也有一些缺点。

首先,多个流可能被哈希到同一个条目中,从而导致流指标的聚合不准确。其次,哈希映射必然对内核 eBPF 数据路径的内存有限制,因此可能会耗尽。因此,用户空间程序必须实现驱逐逻辑,以在超时时不断驱逐流。

基于数组的映射

(每 CPU)基于数组的映射也可用于临时存储每个数据包的明信片,然后再驱逐到用户空间,尽管这不是一个明显的选择。使用数组的优点在于,它将每个数据包的信息存储在数组中,直到数组已满,然后在数组已满时才刷新到用户空间。这样,与每个数据包使用环形缓冲区相比,它可以改善用户空间的繁忙循环。此外,它没有哈希映射的哈希冲突问题。但是,它实现起来很复杂,因为它需要多个冗余数组来存储每个数据包的明信片,当主数组将其内容刷新到用户空间时。

测量

到目前为止,我们已经研究了可用于使用多种数据结构实现流指标收集的选项。现在是时候研究使用上述每种数据结构的流指标明信片的参考实现所实现的性能了。为此,我们实现了代表性的 eBPF 程序,这些程序收集流指标。我们使用的代码可在我们的 Git 仓库中找到。此外,我们通过使用基于 PcapPlusPlus 构建的自定义 UDP 数据包生成器发送流量来进行测量。

此图表描述了实验设置

eBPF test environment

(Kannan/Naik/Lev-Ran,CC BY-SA 4.0)

观察代理是执行流指标收集的 eBPF 数据路径,挂钩在发送者的 tc 挂钩点。我们使用通过 40G 链路连接的两台裸机服务器。数据包生成是使用 40 个单独的内核完成的。为了使这些测量具有透视性,可以使用基于 libpcap 的 Tcpdump 来收集类似的流信息。

单流

我们最初使用单流 UDP 帧运行测试。单流测试可以向我们展示观察代理可以容忍的单流流量突发量。如下图所示,没有任何观察代理的本机性能约为 4.7 Mpps(每秒百万个数据包),而在运行 tcpdump 的情况下,吞吐量降至约 2 Mpps。使用 eBPF,我们观察到性能在 1.6 Mpps 到 4.7 Mpps 之间变化,具体取决于用于存储流指标的数据结构。使用共享数据结构(如 HashMap),我们观察到单流性能下降幅度最大,因为每个数据包都会写入地图中的同一条目,而与数据包的来源 CPU 无关。

对于单流突发,环形缓冲区的性能略优于单个 HashMap。使用每 CPU 哈希映射,我们观察到吞吐量性能显着提高,因为来自多个 CPU 的数据包不再争用同一个映射条目。但是,性能仍然只有没有任何观察代理的本机性能的一半。(请注意,此性能不处理哈希冲突和驱逐。)

使用(每 CPU)数组,我们看到单流吞吐量显着增加。我们可以将此归因于事实上数据包之间实际上没有争用,因为每个数据包都以递增方式占用数组中的不同条目。但是,我们实现中的主要缺点是我们不处理数组在满时刷新,而它以循环方式执行写入。因此,它存储在任何时间点观察到的最后几个数据包记录。尽管如此,它为我们提供了通过在 eBPF 数据路径中适当应用数据结构可以实现的性能增益范围。

eBPF data

(Kannan/Naik/Lev-Ran,CC BY-SA 4.0)

多流

我们现在使用多个流测试 eBPF 观察代理的性能。我们通过检测数据包生成器生成了 40 个不同的 UDP 流(每个内核 1 个流)。有趣的是,对于多个流,我们观察到每 CPU 哈希和哈希映射的性能与单流相比存在明显差异。这可以归因于单个哈希条目的争用减少。 但是,我们没有看到环形缓冲区有任何性能改进,因为无论流如何,争用通道(即环形缓冲区)都是固定的。数组在多个流下的性能略好。

经验教训

从我们的研究中,我们得出了以下结论

  1. 基于环形缓冲区的每个数据包明信片不可扩展,并且会影响性能。
  2. 哈希映射限制了流的“突发性”,即每秒处理的数据包数。每 CPU 哈希映射的性能略好。
  3. 为了处理流中的短数据包突发,使用数组映射来存储每个数据包的明信片将是一个不错的选择,因为数组可以存储几个数据包 10 秒或 100 秒的数据包记录。这将确保观察代理可以容忍短突发而不会降低性能。

在我们的研究中,我们分析了云中多个主机之间数据包级和流级信息的监控。我们从可观测性的核心特征是以非侵入方式收集数据这一前提开始。基于这种观点,我们调查了现有工具,并测试了以从 eBPF 数据路径中观察到的数据包中收集流指标的形式收集可观测性数据的不同方法。我们研究了流的性能如何受到用于收集流指标的数据结构的影响。

理想情况下,为了最大限度地减少由于可观测性代理的开销而导致的主机流量性能下降,我们的分析表明应混合使用每 CPU 数组和每 CPU 哈希数据结构。这两种数据结构可以一起使用来处理流中的短突发,使用数组和使用每 CPU 哈希映射进行聚合。我们目前正在设计可观测性代理 (https://github.com/netobserv/netobserv-ebpf-agent),并计划发布一篇未来的文章,其中包含与现有工具相比的设计细节和性能分析。

[ 下载电子书: 管理您的 Linux 环境以获得成功 ]

标签
praveingk
Pravein 是 IBM Research India 的 Staff Research Scientist。他的研究兴趣是围绕网络、数据中心网络和云的领域。
Etai Lev Ran
Etai 是 IBM Israel Research 实验室的高级技术人员。他的兴趣包括联网和分布式系统,主要是在云和 Kubernetes 的背景下。
priyanka_naik
Priyanka 是 IBM India Research 实验室的 Staff Research Scientist。她广泛从事网络系统工作。她的研究重点是电信和边缘网络。在加入 IBM 之前,她毕业于印度理工学院孟买分校,获得博士学位

5 条评论

很好的总结,以及对 eBPF 技术用于捕获流日志的有趣评估

很高兴看到此分析以及您在使用数据结构性能方面的发现,但是,我认为您忽略了最重要的一点:几乎每个人都做错了。现在你不应该进行按数据包跟踪。那是 90 年代的网络可观测性。我帮助 Netflix 构建了一个基于 TCP 会话更改的内核函数跟踪的流计费解决方案,其核心是我在 github 上开源的 tcplife bcc 工具。即使在非常繁忙的服务器上,这种方法的开销也只有 0.1%。

(似乎我之前的帖子在包含小于号时被截断了)... 小于 0.1%。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.