Cloudflare 平台重塑(五):生产规模化运行 eBPF 的实践
本文地址:https://www.ebpf.top/post/cloudflare-replatforming-5/
原文地址:Cloudflare’s eBPF Replatforming Part 5: Tips for Operating eBPF at Scale
原作者:Cloudflare Engineering
发布时间:2026 年 10 月 29 日
译者:狄卫华
许可:原文采用 CC BY 4.0 许可,本文为其中文翻译,并进行了必要的语言与格式适配。
本系列记录了 Cloudflare 使用 eBPF 的八年历程:它从一项专门用于抵御分布式拒绝服务攻击(DDoS)的工具,发展为其全球网络的可编程骨干。内容涵盖向 Linux 内核贡献代码、应对技术挑战、业务投资回报(ROI),以及为其他采用 eBPF 的公司提供的建议与运维策略。
在前几篇文章中,我们介绍了 Cloudflare 如何利用 eBPF,从绕过传统套接字(socket)API 的限制,到实现分布式 XDP 攻击缓解流水线,以及 BPF Linux 安全模块(LSM)安全策略。不过,在本地测试机上管理 BPF 对象,与在遍布全球、覆盖超过 330 个站点的服务器集群中将其投入运行,是截然不同的事情。
本文将重点转向在生产环境中安全运行 eBPF 所需的部署机制与运维保障。在这一全球服务器集群中安全部署 eBPF 程序,需要多个相互配合的层次:以内核 eBPF 验证器(verifier)作为第一道关卡,进行全面的生产前测试,历时多天、逐步覆盖更大流量范围的分阶段发布,用功能开关(feature flag)将功能启用与二进制部署解耦,通过 bpf_link 原子替换程序,利用平滑重载(graceful reload)机制在升级期间维持数据平面(data plane)运行,借助固定的 BPF map(pinned BPF maps)在守护进程崩溃后保留状态,以及通过配置管理处理站点特有的逻辑。
1. BPF 验证器:第一道安全关卡
每个加载的 eBPF 程序都必须先通过内核的 BPF 验证器,才能执行。验证器提供了传统部署流程无法提供的硬性保证:
- 禁止非法内存访问:验证器对每条代码路径进行静态分析,拒绝可能解引用空指针(NULL)或越界访问内存的程序。
- 禁止无限循环:验证器会检测并拒绝包含无法终止的循环的程序。
- 强制限制栈大小:
BPF程序的栈大小上限为 512 字节;使用BPF_MAP_TYPE_PROG_ARRAY进行尾调用(tail call)的程序,上限还会更低。超过上限的程序会在加载时被拒绝。
【译者注:这里保留原文对 512 字节栈限制的概括。具体上限取决于内核版本、架构及调用方式等条件;当前内核设计问答已说明,支持较大栈的部分 JIT 架构可使用至多 2 KiB,其他情形以及为解释器验证的程序仍有 512 字节限制,不宜把原文数字理解为所有实现的统一上限。】(Linux 内核 BPF Design Q&A)
这意味着,那些会让传统内核模块崩溃的一类缺陷——空指针解引用、缓冲区溢出和无限循环——从结构上就不可能出现在已加载的 eBPF 程序中。验证器会在测试期间,以及向每台服务器部署时,拒绝不符合要求的程序。
【译者注:上文是作者对验证器设计保证的概括。验证器检查的是规定范围内的程序安全性,不等于证明业务逻辑正确,也不意味着验证器或内核实现本身绝无缺陷。】(Linux 内核 eBPF verifier 文档)
2. 生产前测试
任何 eBPF 变更接触生产流量之前,都要经过全面测试,包括:
- 虚拟机负载测试:对于竞态条件(race condition)等复杂问题,Cloudflare 会创建虚拟化测试环境,让虚拟机持续接受负载测试,以捕获或复现罕见的竞态条件。
- VET(Verification and Exhaustive Testing,验证与穷尽测试):变更在接触任何真实流量之前,都要先经过 Cloudflare 专门的生产前测试环境。
3. 通过 Release Manager 分阶段发布
Cloudflare 的 Release Manager 系统是部署用户空间 eBPF 守护进程的主要工具。更新首先部署到金丝雀(canary)数据中心:这些数据中心承载真实客户流量,但不属于主服务器集群。其主要特点包括:
- 发布持续多天,各发布阶段之间必须等待规定的时间。
- 每一阶段继续推进之前,都需要获得多重审批。
- 金丝雀优先:只有在内部环境成功完成测试后,更新才会进入金丝雀站点。这使工程师能够在问题影响全球网络之前发现它们。
- “持续运行并观察"模式(Soak and Observe):在每个阶段,工程师都会监控对应服务的仪表盘、指标和日志,包括丢包、CPU 使用率、规则编译错误和程序加载错误。每一阶段都有强制规定的最短持续观察时间(soak time),达到后才能继续推进;高风险变更的观察时间更长。
4. 功能门控:将功能启用与二进制部署解耦
对于重大的 eBPF 功能变更,Cloudflare 会使用功能门控(feature gate)。其本质是由发布系统管理的功能开关,用来控制某台服务器是否启用某项功能,而不取决于已部署的二进制版本。这样就可以先在功能关闭的状态下,将新的二进制程序部署到整个服务器集群,再逐步启用功能。
5. 通过 bpf_link 实现原子升级
对于 Tubular 这样的关键组件,部署流程按无人值守、故障安全(fail-safe)的要求设计。发布过程一旦失败,必须让先前的版本继续运行,而不能破坏网络连通性。
Cloudflare 使用 bpf_link 将程序挂载到钩子(hook)上。升级程序时,先将新程序加载到内核,再更新现有的 link,使其指向新程序。这一操作是原子的,确保流量要么由旧程序处理,要么由新程序处理,不会出现丢包的空档。部署工具还会校验正在运行的程序的 tag(指令的哈希值),以确保它替换的确实是预期版本。如果 tag 不匹配,工具就会中止更新,防止出现不一致。
6. 平滑重载:二进制程序的零停机升级
用户空间守护进程采用基于 tableflip 的平滑重载机制,由子进程接替父进程,而不中断数据平面。这保证了升级用户空间管理守护进程时,不会影响正在运行的 eBPF 程序,也不会造成丢包。
7. 固定 BPF map:抵御崩溃
用户空间服务通过 bpffs 文件系统(/sys/fs/bpf/、/run/xdpd/bpffs/)中固定的 BPF map,来应对守护进程崩溃和控制平面(control plane)中断。活动套接字绑定、转发表等状态存储在固定到文件系统的 eBPF map 中,使数据的生命周期与用户空间管理进程解耦。
如果用户空间部署工具崩溃或重启,内核中的 eBPF 程序仍会使用保留下来的状态继续运行,不受干扰。如果没有固定的 map,在控制平面中断期间一旦用户空间服务崩溃,物理服务器虽然仍在接收流量,却不再有 XDP/BPF 处理流水线;数据包要么被丢弃,要么出现更糟的情况——全部递交到本机。
8. 配置管理
向数百个站点部署时,需要处理各站点特有的规则,例如不同地区采用不同的 DDoS 规则。Cloudflare 并不为每个站点单独编译二进制程序,而是编译一个代码路径可配置的统一 eBPF 主程序。在某个站点将程序加载到内核之前,先应用配置,启用或禁用特定的逻辑路径。一个二进制程序就能服务整个服务器集群,同时按站点定制。
9. 总结
在大规模、全球分布式环境中运行 eBPF,需要像对待传统内核升级那样严谨地对待 eBPF 程序和 map,同时又要以用户空间部署的节奏快速实施。本文介绍的运维模式,例如依靠验证器的静态分析、通过 bpf_link 执行原子切换,以及利用 BPF 文件系统保留状态,从根本上将数据平面的执行与用户空间守护进程的生命周期解耦。
确保用户空间进程崩溃或版本发布回滚不会中断活动套接字绑定或 XDP 数据包处理后,管理员就能持续、安全地部署内核级逻辑,而不必冒网络分区的风险。构建这套部署机制,是一项历时多年、不断迭代的工程工作。在第六篇文章中,我们将考察这些技术投入的实际回报,用具体的业务、运维和规模扩展指标,量化将网络边缘处理移入内核所带来的收益。
10. 回顾本系列已发布的文章
- 在第一篇中,我们介绍了 Cloudflare 为什么选择将
eBPF作为一个平台,而不是一组单点解决方案。 - 在第二篇中,我们探讨了标准 Linux 网络 API 无法满足 Cloudflare 需求的地方,以及团队为填补这些缺口构建了什么。
- 在第三篇中,我们介绍了构建 Cloudflare 基于
eBPF的网络数据路径所需的架构调整,以及向上游贡献代码的工作。 - 在第四篇中,我们介绍了 Cloudflare 如何利用
eBPF改善运维能力,包括从头重建可观测性(observability)。
延伸阅读
以下为发布附加的往期推荐,不属于原文系列导航:
- 原文作者:DavidDi
- 原文链接:https://www.ebpf.top/post/cloudflare-replatforming-5/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。
- 最后更新时间:2026-11-08 10:49:09.119147972 +0000 UTC