速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

Wasm-bpf: 为云原生 Webassembly 提供通用的 eBPF 内核可编程能力

  • 2023-02-25
    北京
  • 本文字数:5433 字

    阅读完需:约 18 分钟

Wasm-bpf: 为云原生 Webassembly 提供通用的 eBPF 内核可编程能力

Wasm-bpf 由 eunomia-bpf 社区发起,该社区是阿里龙蜥社区 eBPF 技术探索 SIG 中孵化的一个探索性的社区开源项目,在 2022 年九月份发起,主要关注于简化 eBPF 程序的编写、分发,以及探索 eBPF 和 Wasm 相结合的工具链、运行时等技术。


目前 eunomia-bpf 开源社区主要由西安邮电大学陈莉君教授,来自阿里云、腾讯等的国内外 eBPF 领域知名专家担任 Maintainer,团队也包含来自 deepflow 等多个企业的工程师,和多位内核贡献者。社区目前也由中科院软件所程序语言与编译技术实验室(PLCT)赞助和持续维护,其中包含的一些工具链项目也在阿里云的 eBPF 开发框架 Coolbpf、三度观测云等企业项目中落地和使用。


Wasm 最初是以浏览器安全沙盒为目的开发的,发展到目前为止,WebAssembly 已经成为一个用于云原生软件组件的高性能、跨平台和多语言软件沙箱环境,Wasm 轻量级容器也非常适合作为下一代无服务器平台运行时。另一个令人兴奋的趋势是 eBPF 的兴起,它使云原生开发人员能够构建安全的网络、服务网格和多种可观测性组件,并且它也在逐步渗透和深入到内核的各个组件,提供更强大的内核态可编程交互能力。


Wasm-bpf 是一个全新的开源项目[1],它定义了一套 eBPF 相关系统接口的抽象,并提供了一套对应的开发工具链、库以及通用的 Wasm + eBPF 运行时平台实例,让任意 Wasm 虚拟机或者 Wasm 轻量级容器中的应用,有能力将使用场景下沉和拓展到内核态,获取内核态和用户态的几乎所有数据,在网络、安全等多个方面实现对整个操作系统层面的可编程控制,从而极大的拓展 WebAssembly 生态在非浏览器端的应用场景。

基于 eBPF 的系统接口,为 Wasm 带来更多可能


也许你也已经看过 Solomon Hykes (Docker 的创始人之一)这句话:


如果在 2008 年已经有了 WASM + WASI,我们根本不需要创建 Docker。 Wasm 就有这么重要。 服务端的 WebAssembly 是计算的未来。


因为无法依赖浏览器中现有可用的 JavaScript 引擎接口,所以目前大多数在浏览器外运行的 Wasm 轻量级容器需要使用 WASI(WebAssembly 系统接口)。这些运行时允许 Wasm 应用程序以与 POSIX 类似(但不完全相同)的方式与其 host 操作系统交互。


但是,相对于传统的容器中可以使用几乎所有的系统调用,目前 WASI 所能提供的系统资源非常有限,目前仅仅在文件系统、socket 网络连接等方面提供了一些基本的支持,对于操作系统底层资源的访问、控制和管理能力仍然存在大量空白,例如对 Wasm 模块或者外部其他进程的执行资源限制与行为观测,对网络包的快速转发和处理,甚至和 wasm 沙箱外的其他进程进行通信,访问外设等,都没有一个比较成熟的解决方案。这也使得大多数的 Wasm 轻量级容器在实际应用中还是主要集中于纯粹的计算密集型应用,而在网络、安全等方面,还是需要依赖于传统的容器技术。


这也是我们希望建立 Wasm-bpf 项目的初衷:借助当前内核态 eBPF 提供的系统接口以及和用户态交互的能力,拓展整个 WASI 的生态蓝图,为 Wasm 应用带来更多可能的使用场景,同时也能在用户态增强 eBPF 程序的能力。


或者换句话说,类似于浏览器中运行的 Wasm 程序,通过 JavaScript 引擎接口访问浏览器提供的各种系统资源,Wasm-bpf 的方案就是借助 eBPF 虚拟机访问操作系统的各类资源;得益于 eBPF 目前在 Linux 内核甚至 Windows 等其他操作系统中的广泛支持,以及不同内核版本和架构之间的可移植性,和内核 BPF 验证引擎的可靠性,我们仍然可以在一定程度上保证应用的可移植性和安全边界。


Wasm-bpf 项目已经实现了内核态 eBPF 虚拟机和用户态之间系统接口完整的抽象机制,并提供了对应的工具链以将 eBPF 应用编译为 Wasm 模块,帮助进行内核态 eBPF 和用户态 Wasm 之间无序列化,共享内存的高效双向通信,并通过代码生成技术,提供和其他用户态 eBPF 开发框架几乎一致的、简单便捷的开发体验。借助 Wasm 组件模型不断完善的生态支持,我们也可以为 eBPF 社区带来更多用户态开发语言,不同语言实现的可观测性、网络等 eBPF 应用和数据处理插件也可以被轻松集成、复用、统一管理。


在几乎已经成为 eBPF 用户态事实上的 API 标准的 libbpf 库,和 WAMR(wasm-micro-runtime) 之上,只需要 300+ 行代码即可构建完整的通用 Wasm-eBPF 运行组件,并支持大多数的 eBPF 使用场景 -- 任何人用任何主流 Wasm 运行时,或者任何 eBPF 用户态库,以及任何编程语言,都可以轻松添加对应的虚拟机支持,并复用我们的工具链轻松实现 Wasm-eBPF 程序的编写和开发。


之前在 eunomia-bpf 项目中,已经有一些将 eBPF 和 Wasm 结合的探索,但它并不是为了 Wasm 原生应用的场景设计的,不符合 Wasm-eBPF 的通用编程模型,性能也较为低下,因此我们创建了一个新的开源仓库,让 Wasm-bpf 项目专注于利用 eBPF 增强和扩展 WebAssembly 使用场景,并进一步完善对应的工具链和开发库支持:https://github.com/eunomia-bpf/wasm-bpf

eBPF 能给 Wasm 带来什么?

eBPF 是一项革命性的技术,起源于 Linux 内核,可以在操作系统的内核中运行沙盒程序。它被用来安全和有效地扩展内核的功能,而不需要改变内核的源代码或加载内核模块。


从历史上看,由于内核具有监督和控制整个系统的特权能力,所以操作系统一直是实现可观察性、安全性和网络功能等多种能力的理想场所。同时,由于操作系统内核对稳定性和安全性的高要求,内核的新功能迭代通常非常谨慎,也很难接受自定义的、较少通用性的功能改进。因此,与用户态的更多功能相比,内核态操作系统层面的创新率历来都比较低[2]。



eBPF 从根本上改变了这个公式。通过允许在操作系统内运行沙盒程序,应用程序开发人员可以在运行时,可编程地向操作系统动态添加额外的功能。然后,操作系统保证安全和执行效率,就像在即时编译(JIT)编译器和验证引擎的帮助下进行本地编译一样。eBPF 程序在内核版本之间是可移植的,并且可以自动更新,从而避免了工作负载中断和节点重启。


今天,eBPF 被广泛用于各类场景:在现代数据中心和云原生环境中,可以提供高性能的网络包处理和负载均衡;以非常低的资源开销,做到对多种细粒度指标的可观测性,帮助应用程序开发人员跟踪应用程序,为性能故障排除提供洞察力;保障应用程序和容器运行时的安全执行,等等。可能性是无穷的,而 eBPF 在操作系统内核中所释放的创新才刚刚开始[3]。

eBPF 的未来:内核的 JavaScript 可编程接口

对于浏览器而言,JavaScript 的引入带来的可编程性开启了一场巨大的革命,使浏览器发展成为几乎独立的操作系统。现在让我们回到 eBPF:为了理解 eBPF 对 Linux 内核的可编程性影响,对 Linux 内核的结构以及它如何与应用程序和硬件进行交互有一个高层次的理解是有帮助的。



Linux 内核的主要目的是抽象出硬件或虚拟硬件,并提供一个一致的 API(系统调用),允许应用程序运行和共享资源。为了实现这个目的,我们维护了一系列子系统和层,以分配这些责任。每个子系统通常允许某种程度的配置,以考虑到用户的不同需求。如果不能配置所需的行为,就需要改变内核,从历史上看,改变内核的行为,或者让用户编写的程序能够在内核中运行,就有两种选择:


本地支持内核模块写一个内核模块
改变内核源代码,并说服Linux内核社区相信这种改变是必要的。等待几年,让新的内核版本成为一种商品。定期修复它,因为每个内核版本都可能破坏它。由于缺乏安全边界,冒着破坏你的Linux内核的风险


实际上,两种方案都不常用,前者成本太高,后者则几乎没有可移植性。


有了 eBPF,就有了一个新的选择,可以重新编程 Linux 内核的行为,而不需要改变内核的源代码或加载内核模块,同时保证在不同内核版本之间一定程度上的行为一致性和兼容性、以及安全性。为了实现这个目的,eBPF 程序也需要有一套对应的 API,允许用户定义的应用程序运行和共享资源 --- 换句话说,某种意义上讲 eBPF 虚拟机也提供了一套类似于系统调用的机制,借助 eBPF 和用户态通信的机制,Wasm 虚拟机和用户态应用也可以获得这套“系统调用”的完整使用权,一方面能可编程地扩展传统的系统调用的能力,另一方面实现更高效的可编程 IO 处理。


目前的 eBPF 仍然处于早期阶段,但是借助当前 eBPF 提供的内核接口和用户态交互的能力,经由 Wasm-bpf 的系统接口转换,Wasm 虚拟机中的应用已经有能力获取内核以及用户态任意一个函数调用的数据和返回值;以很低的代价收集和理解所有系统调用,并获取所有网络操作的数据包和套接字级别的数据;在网络包处理解决方案中添加额外的协议分析器,并轻松地编程任何转发逻辑,以满足不断变化的需求,而无需离开 Linux 内核的数据包处理环境。


所有的这些场景都不需要离开 Wasm 轻量级容器:不像传统的使用 Wasm 作为数据处理或者控制插件的应用中,这些步骤由 Wasm 虚拟机外的逻辑实现,现在可以在 Wasm 轻量级容器中实现对 eBPF 以及 eBPF 能访问的几乎所有系统资源,完整的控制和交互,甚至实时生成 eBPF 代码改变内核的行为逻辑,实现整个系统从用户态扩展到内核态的可编程性。

用户空间和 eBPF 程序的交互流程

eBPF 程序是以函数为单位的、事件驱动的,当内核或用户空间应用程序通过某个 hook 点时就会运行特定的 eBPF 程序。要使用一个 eBPF 程序,首先我们需要使用 clang/LLVM 工具链将对应的源代码编译为 bpf 字节码,其中包含对应的数据结构定义、maps 和 progs 定义,progs 即程序段,maps 可以用来存储数据或者和用户空间实现双向通信。之后,我们可以借助用户态的开发框架和加载框架,实现完整的 eBPF 应用。

通常的用户态 eBPF 开发框架

对于一个完整的 eBPF 应用,通常需要包含用户态和内核态两部分:


  • 用户态程序需要通过一系列系统调用跟内核进行交互(主要是 bpf 系统调用),创建对应的 map 以在内核态保存数据或和用户态通信,根据配置动态选择加载不同的程序段,动态修改字节码或配置 eBPF 程序的参数,将对应的字节码信息加载进内核,通过验证器确保安全性,并通过 maps 和内核之间实现双向通信,通过 ring buffer / perf buffer 之类的机制从内核态向用户态传递数据(或者反之)。

  • 内核态主要负责具体的计算逻辑与数据收集。



在用户态 Wasm-eBPF 系统接口之上定义的全新 eBPF 开发框架

这个项目本质上可以说是希望把 Wasm 沙箱当做在操作系统之上建立的另一个用户态运行空间,让 Wasm 应用在沙箱中实现和通常用户态中运行的 eBPF 应用一样的编程模型和执行逻辑。Wasm-bpf 会需要一个在 host(沙箱外部)构建的运行时模块,以及一些在沙箱内部被编译为 Wasm 字节码的运行时库来提供完整的支持。



要实现完备的开发模型,我们需要:


  • 一个 Wasm 模块可以对应多个 eBPF 程序;

  • 一个 eBPF 程序实例也可以被多个 Wasm 模块所共用;

  • 可以将 eBPF 程序从 Wasm 沙箱中动态加载进内核、选择所需的挂载点挂载、卸载,控制多个 eBPF 字节码对象的完整生命周期,并支持大多数的 eBPF 程序类型;

  • 可以通过多种类型的 Maps 和内核双向通信,支持大多数的 Maps 类型;

  • 通过 ring buffer 和 perf event polling 从内核态向用户态高效发送信息(对于 ring buffer 来说,也可以反之);

  • 几乎可以适配于所有的使用 eBPF 程序的应用场景,并可以随着内核功能的添加不断演化和扩展,同时不需要变动 Wasm 虚拟机的系统接口。


这就是目前 Wasm-bpf 项目所做的工作。我们也提出了一个新的 WASI 的 Proposal: WASI-eBPF[7].


在 Wasm-bpf 项目中,所有 Wasm 和 eBPF 虚拟机之间的通信都无需经过序列化、反序列化机制,通过工具链中代码生成技术和 BTF(BPF 类型格式[7])信息的支持,我们可以实现在 eBPF 和 Wasm 之间可能不同的结构体内存布局、不同的大小端机制、不同的指针宽度之间的正确通信,在运行时几乎不会引入任何额外的开销;通过 eBPF Maps 通信的时候数据可以直接由内核态复制到 Wasm 虚拟机的内存中,避免多次拷贝带来的额外损耗。同时,通过自动生成 skeleton (bpf 代码框架)和类型定义的方式,用户态程序的 eBPF-Wasm 开发体验也得到了非常大的改善。


得益于 libbpf 提供的 CO-RE(Compile-Once, Run Everywhere)技术,在不同内核版本之间移植 eBPF 字节码对象,也不需要引入额外的重新编译流程,运行时也没有任何的 LLVM/Clang 依赖[8]。


通常一个编译好的 eBPF-Wasm 模块只有大约 90Kb,在不到 100ms 内即可以完成动态加载进内核并执行的过程。我们也在仓库中提供了几个例子,分别对应于可观测、网络、安全等多种场景。


感谢华南理工大学赖晓铮副教授、西安邮电大学陈莉君教授团队和达坦科技对 Wasm 和 eBPF 相结合的指导与帮助,在接下来的工作中,我们会一同针对一些 Wasm-bpf 具体的应用场景,进行更深入的研究与探讨,并在下一篇 blog 中给出更详细的原理解析与性能分析,以及对应的一些示例代码。


Wasm-bpf 编译工具链与运行时模块等目前由 eunomia-bpf 开源社区开发与维护,感谢中科院软件所 PLCT 实验室对社区的大力支持和资助,感谢社区同伴们的贡献。接下来,也会在对应的 eBPF 和 Wasm 相关的工具链和运行时方面,进行更多的完善和探索,并积极向上游社区反馈和贡献。

参考资料

2023-02-25 11:2211393

评论

发布
暂无评论
发现更多内容

消息队列表设计

Rabbit

npm必知必会点

废材壶

大前端 npm Node

Ubuntu系统下《汇编语言》环境配置

codists

汇编语言

怎么清空.NET数据库连接池

喵叔

11月日更

赞!一篇博客讲解清楚 Python queue模块,作为Python爬虫预备知识,用它解决采集队列问题

梦想橡皮擦

11月日更

一文告诉你 K8s PR (Pull Request) 怎样才能被 merge?

腾源会

k8s

腾讯云原生开源生态专场召开,洞察开源云原生技术发展趋势和商业化路径

腾源会

腾讯云 开源 云原生

flutter小部件知多少?

坚果

flutter 11月日更

模块八作业:设计消息队列存储消息数据的 MySQL 表格

apple

Android C++系列:JNI操作Bitmap

轻口味

c++ android jni 11月日更

架构训练营 模块三 作业

dog_brother

「架构实战营」

腾讯开源全景图再刷新:社区贡献领跑国内企业,获超过38万开发者关注

腾源会

开源 腾讯

【高并发】通过源码深度解析ThreadPoolExecutor类是如何保证线程池正确运行的

冰河

Java 并发编程 多线程 高并发 异步编程

【LeetCode】反转链表Java题解

Albert

算法 LeetCode 11月日更

多模态内容理解算法框架项目 Lichee 正式开源,为微服务开源社区贡献力量

腾源会

开源

Prometeus 2.31.0 新特性

耳东@Erdong

release Prometheus 11月日更

面试官:讲讲雪花算法,越详细越好

秦怀杂货店

分布式 雪花算法

Golang Gin 框架入门介绍(二)

liuzhen007

11月日更

[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (2)

baiyutang

golang 微服务 11月日更

我在 IBM 从事开源工作的十一年

腾源会

开源

Serverless 架构模式及演进

阿里巴巴云原生

阿里云 Serverless 云原生 架构模式

进击的Java(七)

ES_her0

11月日更

数据库连接池Demo(1)单线程初步

Java 数据库 连接池

如何评价一个开源项目(一)--活跃度

腾源会

开源

SuperEdge 和 FabEdge 联合在边缘 K8s 集群支持原生 Service 云边互访和 PodIP 直通

腾源会

开源 边缘计算 superedge

腾讯发布 K8s 多集群管理开源项目 Clusternet

腾源会

开源 K8s 多集群管理 Clusternet

腾讯自研分布式远程Shuffle服务Firestorm正式开源

腾源会

大数据 开源 腾讯

干货分享:细说双 11 直播背后的压测保障技术

阿里巴巴云原生

阿里云 云原生 性能测试 PTS

在华为云专属月,找到开启互联网第二增长曲线的一把钥匙

脑极体

CNCF 沙箱再添“新将”!云原生边缘容器开源项目 SuperEdge 正式入选

腾源会

开源 容器 云原生 cncf

Github webhooks 自动部署博客文章,使用总结【含视频】

小傅哥

GitHub 小傅哥 WEBHOOKS 自动部署 通知回调

Wasm-bpf: 为云原生 Webassembly 提供通用的 eBPF 内核可编程能力_开源_郑昱笙_InfoQ精选文章