
译者 | 陈豪
策划 | 云昭
Extended Berkeley Packet Filter ( eBPF ) 是 Linux 内核的一个相对较新的特性,它让许多 DevOps 专业人士、SRE 和工程师兴奋不已。eBPF 甚至可以与标准的Linux iptables 相提并论。但是,它真能成为满足所有 Linux 内核需求的“一站式商店”呢?
过去,对内核进行更改是很困难的。开发者虽然可以调用 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 占用,意味着更多的钱花在你的云提供商上。因此,虽然兰博基尼可能比许多其他汽车更快,但如果你需要遵守日常通勤的速度限制,它可能不是最好的花钱方式。
想要获得高性能,自然也需要付出一定的代价。使用eBPFizer之前需要通过计算其性能价格,来确定是否可以接受,从而在两者之间找到平衡点。
下面是是一些使用 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
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返
它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput
我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or