译者 | 陈豪
策划 | 云昭
Extended Berkeley Packet Filter ( eBPF ) 是 Linux 内核的一个相对较新的特性,它让许多 DevOps 专业人士、SRE 和工程师兴奋不已。eBPF 甚至可以与标准的Linux iptables 相提并论。但是,它真能成为满足所有 Linux 内核需求的“一站式商店”呢?
eBPF正当时
过去,对内核进行更改是很困难的。开发者虽然可以调用 API 来获取数据,但却无法影响内核内部的执行代码。相反,你必须向 Linux 社区提交补丁并等待其获得批准。使用 eBPF,可以将程序加载到内核中,并在例如看到某个数据包或发生其他事件时指示内核执行程序。
eBPF 是Linux内核中提供的一项功能,它允许你在内核中运行虚拟机。该虚拟机允许你安全地将程序加载到内核中以自定义其操作。为什么这很重要?
使用 eBPF,内核及其行为变得高度可定制,而不是固定的。在适当的情况下使用时,这可能是非常有益的。
典型用例
eBPF 有几个典型用例,包括流量控制、创建网络策略、连接时负载平衡和可观测性。
流量控制
如果没有 eBPF,数据包在到达最终目的地的途中会使用标准的 Linux 网络路径。如果一个数据包出现在 A 点,并且你知道该数据包需要到达 B 点,你可以通过将其直接发送到 B 点来优化 Linux 内核中的网络路径。使用 eBPF,你可以利用额外的上下文来使内核中的这些更改使数据包绕过复杂的路由并简单地到达其最终目的地。
这在拥有大量网络的 Kubernetes 容器环境中尤其重要。(除了主机网络堆栈,每个容器都有自己的迷你网络堆栈。)当流量进入时,它通常被路由到容器堆栈,并且必须经过复杂的路径,因为它从主机堆栈到达那里。可以使用 eBPF 绕过此路由。
创建网络策略
创建网络策略时,有两种情况可以使用 eBPF:
1. eXpress 数据路径 (XDP) –当原始数据包缓冲区进入系统时,eBPF 为你提供了一种有效的方法来检查该缓冲区并快速决定如何处理它。
2. 网络策略——eBPF 允许你有效地检查数据包并为 pod 和主机应用网络策略。
连接时负载平衡
在 Kubernetes 中对服务连接进行负载平衡时,端口需要与服务通信,因此必须进行网络地址转换 (NAT)。一个数据包被发送到一个虚拟 IP,该虚拟 IP 将其转换为支持该服务的 pod 的目标 IP;pod 然后响应虚拟 IP,返回的数据包被翻译回源。
使用 eBPF,你可以通过使用已加载到内核中的 eBPF 程序并在连接源进行负载平衡来避免这种数据包转换。由于目标网络地址转换 (DNAT) 不需要在数据包处理路径上进行,因此消除了服务连接的所有 NAT 开销。
可观测性
收集统计信息和深入调试内核是 eBPF 可用于可观测性的两种有用方式。eBPF 程序可以附加到内核中的许多不同函数,提供对函数正在处理的数据的访问,同时还允许修改该数据。例如,使用 eBPF,如果建立了网络连接,你可以在创建套接字时收到调用。将套接字调用作为事件接收非常有用,通过 eBPF 在打开套接字的程序的上下文中提供这些调用,因此你可以获得有关哪个进程在打开它以及套接字发生了什么的信息。
性能的代价
那么eBPF比标准的Linux iptables更高效吗?视情况而定。
如果你要在应用具有大量 IP 地址(即 ipsets)的网络策略时对 iptables 的工作方式进行微基准测试,那么在许多情况下 iptables 比 eBPF 更好。
但是如果你想在 Linux 内核中做一些需要改变内核中的数据包流的事情,eBPF 将是更好的选择。
标准 Linux iptables 是一个复杂的系统,当然也有其局限性,但同时它提供了操纵流量的选项;如果你不清楚如何编写 iptables 规则,进展就会寸步难行。eBPF 则允许将自己的程序加载到内核中以影响按需定制的行为,因而它比 iptables 更灵活,因为它不限于一组规则。
还有一点需要考虑的是,虽然 eBPF 允许你运行程序、添加逻辑、重定向流和绕过处理——这绝对是一个胜利——但它是一个虚拟机,因此必须转换为字节码。相比之下,Linux 内核的 iptables 已经编译为代码。
如你所见,将eBPF与iptables进行简单的比较意义不大。我们需要评估的是性能,这里要看的两个关键因素是延迟(速度)和开销。如果 eBPF 非常快但占用了你 80% 的资源,那么它就像一辆兰博基尼——一辆昂贵的快车。如果这对你有用,那就太好了(也许你真的很喜欢昂贵、快速的汽车)。
注意,更多的 CPU 占用,意味着更多的钱花在你的云提供商上。因此,虽然兰博基尼可能比许多其他汽车更快,但如果你需要遵守日常通勤的速度限制,它可能不是最好的花钱方式。
何时使用 eBPF(何时不使用)
想要获得高性能,自然也需要付出一定的代价。使用eBPFizer之前需要通过计算其性能价格,来确定是否可以接受,从而在两者之间找到平衡点。
下面是是一些使用 eBPF 的特定情况,包括适用和不适用的场景。
何时不使用eBPF
实施应用层政策——由于价格与性能的权衡,使用 eBPF 执行深度协议检查和实施应用层策略并不是很有效。
你可以利用 Linux 内核的连接跟踪器来实施策略,对每个流应用一次策略(无论该流有 5 个数据包还是 5,000 个数据包),然后在 Linux conntrack 表中将其标记为允许或拒绝。你无需继续检查流中的每个数据包。如果你要使用 eBPF 实施策略,它允许你在单个 TCP 连接上拥有多个 HTTP 事务,你将需要检查每个数据包以检测这些事务,然后实施第 7 层控制。为此,你需要执行 CPU 周期,这将变得昂贵。
一个更有效的方法是获得像 Envoy 这样的代理,并使用 eBPF 优化 Envoy 的流量,同时让 Envoy 为你翻译应用程序协议。具有像 Envoy 这样的代理的 iptables 是一个更好的设计,在这种情况下将是一个更好的选择。
构建服务网格控制平面——类似地,服务网格依赖于像 Envoy 这样的代理。多年来,人们在设计这个过程中进行了很多思考。这样做的主要原因是,在许多情况下,以集群内部的高速对 HTTP 等应用程序协议进行内联处理是不可行的。因此,你应该考虑使用 eBPF 以一种有效的方式将流量路由到像 Envoy 这样的代理,而不是使用它来替换代理本身。
逐包处理——使用 eBPF 执行 CPU 密集型或逐包处理,例如加密流的解密和重新加密,效率不高,因为你需要构建结构并查找每个数据包,这是昂贵的。
何时使用
XDP—— eBPF 提供了一种在原始数据包缓冲区进入系统时检查它们的有效方法,使您可以快速决定如何处理它们。
连接时负载平衡——使用eBPF,您可以使用加载到内核中的程序在源头进行负载平衡,而不是使用虚拟 IP。由于 DNAT 不需要在数据包处理路径上进行,因此消除了服务连接的所有 NAT 开销。
可观测性——eBPF 程序是在 Linux 内核中添加探测器作为传感器以获取上下文丰富的数据的绝佳方式。这是一个巨大的好处,因为无需更改内核即可启用跟踪和分析。您可以在打开套接字的程序的上下文中轻松接收套接字调用,或者添加程序以跟踪内核中的系统调用。在我们看来,可观测性是 eBPF 最有益的用例。
写在最后
eBPF 是 iptables 的替代品吗?不完全是。很难想象使用eBPF和使用 iptables 一样高效。目前,两者共存,用户可以根据自己的具体需求权衡性价比并决定何时使用哪个功能。
我们相信正确的解决方案是利用 eBPF 以及 Linux 内核中的现有机制来实现你想要的结果。这就是一些开源方案支持标准Linux、Windows HNS、Linux eBPF等多个数据平面的原因。
既然我们已经确定 eBPF 和 iptables 都是有用的,那么唯一合乎逻辑的事情就是同时拥抱这两者。
译者介绍
陈豪,51CTO社区编辑,具有6年工作经验的高级系统工程师。擅长技能有Linux内嵌汇编语言,Python,C,C++,Java,Linux内核分析,智能机器人软件设计等。
原文标题:When (And When Not) to Use eBPF,作者:Manish Sampat