eBPF 是一种事件驱动的编程技术,使开发人员能够编写高效、安全且非侵入性的程序,这些程序直接运行在 Linux 操作系统 (OS) 内核空间,从而有效地“扩展”操作系统内核。
操作系统的内核是一个极其稳定且被刻意保持稳定的实体。它支撑着整个操作系统,因此——出于设计考量——对其进行修正或修改可能复杂且劳动密集。eBPF 通过使开发人员能够在特权上下文(例如操作系统内核)中运行沙盒化程序,来解决这一可扩展性挑战。
操作系统栈可分为三个逻辑层:硬件层、内核层和用户层。内核层是操作系统的核心。它位于物理层(包含操作系统的所有物理硬件、内存和存储组件)与用户层(包含操作系统上的网络浏览器和应用程序)之间。
用户空间中的应用程序和浏览器必须与物理层的组件通信以完成各自的任务,但物理层的每个组件都有特定的通信协议和兼容性要求。这正是内核层(或内核空间)发挥作用之处。它解释系统调用,并使应用程序能够有效地与物理网络组件通信。
eBPF 工具帮助开发人员更轻松地在运行时扩展现有软件的功能,而无需修改内核源代码、加载内核模块(可扩展内核功能的可加载代码段)或以其他方式扰乱内核空间。
eBPF 技术代表了原始伯克利数据包过滤器 (BPF) 的演进,该过滤器提供了一种在用户空间程序中选择和分析网络数据包的简单方法。但除了数据包过滤之外,BPF 程序缺乏在内核中处理更复杂任务的灵活性。
为响应对更通用技术的需求,Linux 社区基于 BPF 的后端功能构建了 eBPF,但扩展了 BPF 内核内可编程性。eBPF 程序的高级特性——及其沙盒方法——使开发人员能够实施增强的数据包过滤流程、改进内核空间可观测性与监控能力、执行高端性能分析,并在本地数据中心和云原生环境中实施内核级安全策略。
eBPF 程序的主要组件:
eBPF 程序最初使用受限制的 C 子集编写,然后通过 LLVM 等工具编译为 eBPF 字节码;LLVM 在此充当了支持前端编程语言(如 Clang)的后端架构 字节码本质上是一组受限制的指令,遵循 eBPF 指令集架构并能防止运行时错误。
Linux 内核技术可将 eBPF 字节码转换为可执行操作,但即时 (JIT) 编译器能提供更卓越的性能。JIT 编译器可根据需要将字节码转换为特定硬件平台的原生机器码。
用户空间加载器是用户空间中的程序,负责将 eBPF 字节码加载到内核、将其附加到适当的钩子并管理任何关联的 eBPF 映射。用户空间加载器的示例包括 BPF Compiler Collection (BCC) 和 bpftrace 等工具。
eBPF 映射是具有键值对和读写访问权限的数据结构,提供共享存储空间并促进 eBPF 内核程序和用户空间应用程序之间的交互。eBPF 映射使用系统调用创建和管理,还可维护 eBPF 程序的不同迭代之间的状态。
验证器(eBPF 系统的关键组件)在字节码加载到内核之前检查字节码,确保程序不包含任何有害操作,例如无限循环、非法指令或越界内存访问。它还有助于确保程序的所有数据路径成功终止。
钩子是内核代码中可以连接 eBPF 程序的点。当内核到达钩子时,它会运行附加的 eBPF 程序。
不同类型的钩子(如跟踪点、kprobes、uprobes 和网络数据包接收队列)为 eBPF 程序提供广泛的数据访问权限,并使它们能够完成各种操作。例如,跟踪点支持程序检查和收集有关内核或其他进程的数据,而流量控制钩子可用于检查和修改网络数据包。kprobes 和 uprobes 则分别在内核层级和用户层级实现动态追踪。
XDP 是一种高性能数据路径,可在驱动程序层级加速数据包处理,并促进跨通信层的传输。它们使 eBPF 系统甚至能在数据包到达内核之前就做出路由决策。
XDP 与 Linux 内核的集成(在 2010 年代中期)最终使开发人员能够部署基于 eBPF 的 负载均衡 功能,即使是最繁忙的数据中心也能管理数据流量。
因为 eBPF 无法生成任意函数且必须保持与每个可能内核版本的兼容性,因此有时基本的 eBPF 指令集不够细致,无法执行高级操作。辅助函数弥补了这一差距。
辅助函数(eBPF 可以从系统内部调用的一组预定义的、基于 API 的内核函数)让 eBPF 程序可以完成不受指令集直接支持的复杂操作(例如获取当前时间和日期或生成随机数)。
一般来说,eBPF 在 Linux 内核中作为虚拟机 (VM) 运行,在低级指令集架构上工作并运行 eBPF 字节码。但是,运行 eBPF 程序的复杂过程往往遵循某些主要步骤。
开发人员首先编写 eBPF 程序并编译字节码。程序的目的将决定对应代码类型。例如,如果一个团队想要监控 CPU 的使用情况,它将编写包含捕获使用指标功能的代码。
在 eBPF 编译器将高级 C 代码转换为低级字节码之后,用户空间加载器将生成 BPF 系统调用,从而将程序加载到内核中。加载器还负责解决错误并设置程序所需的任何 eBPF 映射。
程序字节码和映射就位后,eBPF 执行验证过程,确保程序在内核中安全执行。如果程序被认为不安全,则加载程序的系统调用将失败,并且加载程序将收到错误消息。如果程序通过验证,则允许运行。
无论是通过解释器还是 JIT 编译器,eBPF 都会将字节码转换为可执行的机器码。然而,eBPF 是一种事件驱动型技术,因此它仅在响应内核中的特定钩子点或事件(例如系统调用、网络事件、进程启动、CPU 空闲)时运行。当事件发生时,eBPF 会运行相应的字节码程序,使开发人员能够检查与操控系统的各个组件。
当 eBPF 程序运行时,开发人员就可以使用 eBPF 映射从用户空间与程序交互。例如,应用程序可能会定期检查映射以从 eBPF 程序收集数据,或者更新映射以更改程序的行为。
卸载程序是大多数 eBPF 执行过程的最后一步。当 eBPF 完成工作后,加载器可以再次使用 BPF 系统调用将其从内核中卸载,此时 eBPF 停止运行并释放相关资源。卸载过程还可能包括迭代团队不再需要的 eBPF 映射,以释放有用的单个元素,然后删除映射本身(使用“删除”系统调用)。
伯克利数据包过滤器 (BPF) 最初是作为 Unix 系统中数据包过滤机制开发的,它允许用户级代码定义过滤器,在内核中高效捕获和处理网络数据包。因此,这种方法最大限度地减少了将不必要数据传输到用户空间所需的处理能力,并能简化和优化 计算机网络。
BPF 使用内核代理在网络栈入口点处理数据包。BPF 程序开发完成后,由 BPF 内核代理加载到内核空间,该代理在将其附加到相应套接字前会验证其准确性。因此,在用户空间中,只有匹配 BPF 程序过滤器的数据包才能从给定套接字接收数据。此安全防护特性限制了程序对允许内存区域的访问,并防止潜在的内核崩溃。
eBPF 最初出现于 2014 年,当时它代表了原始 BPF 概念的重大演进。除了最初的网络用例外,eBPF 应用范围扩展到系统调用和其他功能,这正是开发人员常将其称为“完全扩展的伯克利数据包过滤器”的原因。
eBPF 表现突出的关键领域之一是网络性能监控。它使 IT 团队能够通过提供网络行为、性能指标和瓶颈的细粒度洞察,进行实时分析和故障排除。eBPF 在网络安全中发挥着关键作用,监控和过滤系统调用与网络活动,执行网络安全策略并检测系统异常。
eBPF 还为开发人员提供了宝贵的工具,用于跟踪和分析内核及用户空间应用程序,并在数据穿越内核时运行自定义操作与数据转换,进一步增强了其多功能性和实用性。由于这些远超数据包过滤范围的扩展能力,eBPF 现已被视为一个独立术语,而非“扩展型伯克利数据包过滤器”的缩写。
eBPF 技术的进步迫使软件开发人员将应用程序扩展到所有操作系统,以便非基于 Linux 的平台可以利用 eBPF 复杂的跟踪、网络和监控功能。1
事实上,eBPF 基金会(Linux 基金会的延伸,成员包括 Google、Meta、Netflix、Microsoft、Intel 和 Isovalent 等)已大力投资扩展 eBPF 程序的操作系统兼容性,希望最终扩大 eBPF 编程的应用范围。2
尽管 BPF 为高效数据包过滤奠定了基础,但 eBPF 无疑扩展了其范畴。现代 eBPF 为 Linux 系统提供了优化可观测性、性能与安全性的综合工具。eBPF 能够在内核中运行动态、用户定义程序,为系统监控与管理创造了新的可能性,使 eBPF 成为软件开发人员和计算机程序员不可或缺的工具。
eBPF 技术已经成为现代 Linux 系统的基石,实现对 Linux 内核的细粒度控制,并使企业能够在 Linux 生态系统中构建更创新的项目。
eBPF 促进了以下方面的进步:
eBPF 使开发人员能够安装更快速、更定制化的数据包处理功能、负载均衡过程、应用性能分析脚本及网络监测实践。诸如Cilium等开源平台通过部署 eBPF,为 Kubernetes 集群与工作负载以及其他容器化 微服务提供安全、可扩展、可观测的网络连接。
eBPF 还能帮助IT团队在事件路径的早期阶段实施简单和复杂的规则,以实现更有效的流量路由、内容过滤和丢包预防。利用内核级数据包转发逻辑,eBPF 可以最小化延迟、简化路由过程,并实现更快的整体网络响应。
随着应用程序被分解为微服务,对用户空间的可观测性可能变得困难。eBPF 为监测工具提供内核空间视角,从而保持端到端的完整可观测性。
eBPF 允许开发人员对内核和用户空间应用程序进行插桩,以收集详细的性能数据和指标,而不会显著影响系统性能。这些功能帮助组织保持领先,实现对每个网络组件(及其依赖项)的实时监控和 可观测性。
eBPF 可以在内核和套接字层级监控系统调用、网络流量和系统行为,以实时检测并响应潜在的安全威胁。例如,Falco(云原生运行时安全工具)使用 eBPF 实现运行时安全审计和事件响应,从而增强系统的整体安全性。
许多 eBPF 工具可以跟踪系统调用、监控 CPU 使用情况并追踪资源使用情况(例如磁盘 I/O)。这些功能可帮助开发人员更轻松地探测系统性能瓶颈、实施调试协议并识别优化机会。
eBPF 可以安装并实施内核级安全策略(例如网络流量过滤器、防火墙和行为限制)及安全检查,以防止恶意行为者和未经授权的用户访问网络。
在微服务架构中,了解容器内生产工作负载至关重要。然而,传统的可观测性工具可能难以跟上容器化微服务的步伐。
容器的设计初衷就是短暂的:它们在需要时被创建,一旦完成使命就立即被销毁。每个容器都像一个独立的主机,在生产环境中,它们产生的海量指标很容易压垮标准的应用程序、网络和基础设施监测工具。虚拟机可能表现类似,但容器的快速循环特性会使遥测数据采集复杂化。
此外,容器通常以大规模方式部署在云环境中,这使得对其了解更具挑战性。
eBPF 运行在主机或容器的内核层级,使开发人员能够从短暂的数据实体收集遥测数据。它有助于将网络、应用程序和基础设施的可见性整合到基于 eBPF 的统一服务中。借助 eBPF,开发人员可以在容器层级捕获进程、内存使用、网络活动和文件访问的数据,即使容器没有部署在云中。
同样,在基于 Kubernetes 的容器化环境中,eBPF 使用单一接口和工具集从不同集群收集数据,使 IT 团队无需部署单独的用户空间代理即可完成整个网络的数据采集任务。eBPF 工具可以运行在控制平面节点(例如用于 API 服务器监控)并监控工作节点以生成洞察,同时关联来自两种节点类型的数据点和洞察,从而实现精细化的集群可观测性。
Red Hat OpenShift on IBM Cloud 是一个完全托管的 OpenShift 容器平台 (OCP)。
借助 IBM Spectrum Virtualize,为 VMware 环境提供安全、可靠和高效的存储虚拟化技术。
查找适合企业的业务需求的云基础设施解决方案,并按需扩展资源。
1 基金会提议在多种操作系统中推进 eBPF 技术普及, DevOps.com,2021 年 8 月 21 日。
2 最新 eBPF 进展预示 IT 领域重大变革, DevOps.com,2023 年 9 月 13日。