0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

在WebAssembly中使用Rust编写eBPF程序并发布OCI镜像

jf_wN0SrCdH 来源:Rust语言中文社区 2023-02-14 18:10 次阅读

eBPF(extended Berkeley Packet Filter)是一种高性能的内核虚拟机,可以运行在内核空间中,以收集系统和网络信息。随着计算机技术的不断发展,eBPF 的功能日益强大,并且已经成为各种效率高效的在线诊断和跟踪系统,以及构建安全的网络、服务网格的重要组成部分。

WebAssembly(Wasm)最初是以浏览器安全沙盒为目的开发的,发展到目前为止,WebAssembly 已经成为一个用于云原生软件组件的高性能、跨平台和多语言软件沙箱环境,Wasm 轻量级容器也非常适合作为下一代无服务器平台运行时,或在边缘计算等资源受限的场景高效执行。

现在,借助 Wasm-bpf 编译工具链和运行时,我们可以使用 Wasm 将 eBPF 程序编写为跨平台的模块,使用 C/C++ 和 Rust 编写程序。通过在 WebAssembly 中使用 eBPF 程序,我们不仅让 Wasm 应用获得 eBPF 的高性能、对系统接口的访问能力,还可以让 eBPF 程序享受到 Wasm 的沙箱、灵活性、跨平台性、和动态加载的能力,并且使用 Wasm 的 OCI 镜像来方便、快捷地分发和管理 eBPF 程序。例如,可以类似 docker 一样,从云端一行命令获取 Wasm 轻量级容器镜像,并运行任意 eBPF 程序。通过结合这两种技术,我们将会给 eBPF 和 Wasm 生态来一个全新的开发体验!

使用 Wasm-bpf 工具链在 Wasm 中编写、动态加载、分发运行 eBPF 程序

在前两篇短文中,我们已经介绍了 Wasm-bpf 的设计思路,以及如何使用 C/C++ 在 Wasm 中编写 eBPF 程序:

  • Wasm-bpf: 架起 Webassembly 和 eBPF 内核可编程的桥梁
  • 在 WebAssembly 中使用 C/C++ 和 libbpf 编写 eBPF 程序

基于 Wasm,我们可以使用多种语言构建 eBPF 应用,并以统一、轻量级的方式管理和发布。以我们构建的示例应用 bootstrap.wasm 为例,使用 C/C++ 构建的镜像大小最小仅为 ~90K,很容易通过网络分发,并可以在不到 100ms 的时间内在另一台机器上动态部署、加载和运行,并且保留轻量级容器的隔离特性。运行时不需要内核特定版本头文件、LLVM、clang 等依赖,也不需要做任何消耗资源的重量级的编译工作。对于 Rust 而言,编译产物会稍大一点,大约在 2M 左右。

本文将以 Rust 语言为例,讨论:

  • 使用 Rust 编写 eBPF 程序并编译为 Wasm 模块
  • 使用 OCI 镜像发布、部署、管理 eBPF 程序,获得类似 Docker 的体验

我们在仓库中提供了几个示例程序,分别对应于可观测、网络、安全等多种场景。

编写 eBPF 程序并编译为 Wasm 的大致流程

一般说来,在非 Wasm 沙箱的用户态空间,使用 libbpf-bootstrap 脚手架,可以快速、轻松地使用 C/C++构建 BPF 应用程序。编译、构建和运行 eBPF 程序(无论是采用什么语言),通常包含以下几个步骤:

  • 编写内核态 eBPF 程序的代码,一般使用 C/C++ 或 Rust 语言
  • 使用 clang 编译器或者相关工具链编译 eBPF 程序(要实现跨内核版本移植的话,需要包含 BTF 信息)。
  • 在用户态的开发程序中,编写对应的加载、控制、挂载、数据处理逻辑;
  • 在实际运行的阶段,从用户态将 eBPF 程序加载进入内核,并实际执行。

使用 Rust 编写 eBPF 程序并编译为 Wasm

Rust 可能是 WebAssembly 生态系统中支持最好的语言。Rust 不仅支持几个 WebAssembly 编译目标,而且 wasmtime、Spin、Wagi 和其他许多 WebAssembly 工具都是用 Rust 编写的。因此,我们也提供了 Rust 的开发示例:

  • Wasm 和 WASI 的 Rust 生态系统非常棒
  • 许多 Wasm 工具都是用 Rust 编写的,这意味着有大量的代码可以复用。
  • Spin 通常在对其他语言的支持之前就有Rust的功能支持
  • Wasmtime 是用 Rust编写的,通常在其他运行时之前就有最先进的功能。
  • 可以在 WebAssembly 中使用许多现成的 Rust 库。
  • 由于 Cargo 的灵活构建系统,一些 Crates 甚至有特殊的功能标志来启用Wasm的功能(例如Chrono)。
  • 由于 Rust 的内存管理技术,与同类语言相比,Rust 的二进制大小很小。

我们同样提供了一个 Rust 的 eBPF SDK,可以使用 Rust 编写 eBPF 的用户态程序并编译为 Wasm。借助 aya-rs 提供的相关工具链支持,内核态的 eBPF 程序也可以用 Rust 进行编写,不过在这里,我们还是复用之前使用 C 语言编写的内核态程序。

首先,我们需要使用 rust 提供的 wasi 工具链,创建一个新的项目:

rustuptargetaddwasm32-wasi
cargonewrust-helloworld

之后,可以使用 Makefile 运行 make 完成整个编译流程,并生成 bootstrap.bpf.o eBPF 字节码文件。

使用 wit-bindgen 生成类型信息,用于内核态和 Wasm 模块之间通信

wit-bindgen 项目是一套着眼于 WebAssembly,并使用组件模型的语言的绑定生成器。绑定是用 *.wit 文件描述的,文件中描述了 Wasm 模块导入、导出的函数和接口。我们可以 wit-bindgen 它来生成多种语言的类型定义,以便在内核态的 eBPF 和用户态的 Wasm 模块之间传递数据。

我们首先需要在 Cargo.toml 配置文件中加入 wasm-bpf-bindingwit-bindgen-guest-rust 依赖:

wasm-bpf-binding = { path = "wasm-bpf-binding" }

这个包提供了 wasm-bpf 由运行时提供给 Wasm 模块,用于加载和控制 eBPF 程序的函数的绑定。

  • wasm-bpf-binding 在 wasm-bpf 仓库中有提供。
[dependencies]
wit-bindgen-guest-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.3.0" }

[patch.crates-io]
wit-component = {git = "https://github.com/bytecodealliance/wasm-tools", version = "0.5.0", rev = "9640d187a73a516c42b532cf2a10ba5403df5946"}
wit-parser = {git = "https://github.com/bytecodealliance/wasm-tools", version = "0.5.0", rev = "9640d187a73a516c42b532cf2a10ba5403df5946"}

这个包支持用 wit 文件为 rust 客户程序生成绑定。使用这个包的情况下,我们不需要再手动运行 wit-bindgen。

接下来,我们使用 btf2wit 工具,从 BTF 信息生成 wit 文件。可以使用 cargo install btf2wit 安装我们提供的 btf2wit 工具,并编译生成 wit 信息:

cdbtf
clang-targetbpf-gevent-def.c-c-oevent.def.o
btf2witevent.def.o-oevent-def.wit
cp*.wit../wit/
  • 其中 event-def.c 是包含了我们需要的结构体信息的的 C 程序文件。只有在导出符号中用到的结构体才会被记录在 BTF 中。

对于 C 结构体生成的 wit 信息,大致如下:

default world host {
    record event {
         pid: s32,
        ppid: s32,
        exit-code: u32,
        --pad0: list,
        duration-ns: u64,
        comm: list,
        filename: list,
        exit-event: s8,
    }
}

wit-bindgen-guest-rust 会为 wit 文件夹中的所有类型信息,自动生成 rust 的类型,例如:

#[repr(C,packed)]
#[derive(Debug,Copy,Clone)]
structEvent{
pid:i32,
ppid:i32,
exit_code:u32,
__pad0:[u8;4],
duration_ns:u64,
comm:[u8;16],
filename:[u8;127],
exit_event:u8,
}

编写用户态加载和处理代码

为了在 WASI 上运行,需要为 main.rs 添加 #![no_main] 属性,并且 main 函数需要采用类似如下的形态:

#[export_name="__main_argc_argv"]
fnmain(_env_json:u32,_str_len:i32)->i32{

return0;
}

用户态加载和挂载代码,和 C/C++ 中类似:

letobj_ptr=
binding::wasm_load_bpf_object(bpf_object.as_ptr()asu32,bpf_object.len()asi32);
ifobj_ptr==0{
println!("Failedtoloadbpfobject");
return1;
}
letattach_result=binding::wasm_attach_bpf_program(
obj_ptr,
"handle_exec".as_ptr()asu32,
"".as_ptr()asu32,
);
...

polling ring buffer:

letmap_fd=binding::wasm_bpf_map_fd_by_name(obj_ptr,"rb".as_ptr()asu32);
ifmap_fd< 0{
println!("Failedtogetmapfd:{}",map_fd);
return1;
}
//binding::wasm
letbuffer=[0u8;256];
loop{
//pollingthebuffer
binding::wasm_bpf_buffer_poll(
obj_ptr,
map_fd,
handle_eventasi32,
0,
buffer.as_ptr()asu32,
buffer.len()asi32,
100,
);
}

使用 handler 接收返回值:

extern"C"fnhandle_event(_ctx:u32,data:u32,_data_sz:u32){
letevent_slice=unsafe{slice::from_raw_parts(dataas*constEvent,1)};
letevent=&event_slice[0];
letpid=event.pid;
letppid=event.ppid;
letexit_code=event.exit_code;
ifevent.exit_event==1{
print!(
"{:<8} {:<5} {:<16} {:<7} {:<7} [{}]",
"TIME",
"EXIT",
unsafe{CStr::from_ptr(event.comm.as_ptr()as*consti8)}
.to_str()
.unwrap(),
pid,
ppid,
exit_code
);
...
}

接下来即可使用 cargo 编译运行:

$cargobuild--targetwasi32-wasm
$sudowasm-bpf./target/wasm32-wasi/debug/rust-helloworld.wasm
TIMEEXECsh18024533666/bin/sh
TIMEEXECwhich180246180245/usr/bin/which
TIMEEXITwhich180246180245[0](1ms)
TIMEEXITsh18024533666[0](3ms)
TIMEEXECsh18024733666/bin/sh
TIMEEXECps180248180247/usr/bin/ps
TIMEEXITps180248180247[0](23ms)
TIMEEXITsh18024733666[0](25ms)
TIMEEXECsh18024933666/bin/sh
TIMEEXECcpuUsage.sh180250180249/root/.vscode-server-insiders/bin/a7d49b0f35f50e460835a55d20a00a735d1665a3/out/vs/base/node/cpuUsage.sh

使用 OCI 镜像发布和管理 eBPF 程序

开放容器协议 (OCI) 是一个轻量级,开放的治理结构,为容器技术定义了规范和标准。在 Linux 基金会的支持下成立,由各大软件企业构成,致力于围绕容器格式和运行时创建开放的行业标准。其中包括了使用 Container Registries 进行工作的 API,正式名称为 OCI 分发规范 (又名“distribution-spec”)。

Docker 也宣布推出与 WebAssembly 集成 (Docker+Wasm) 的首个技术预览版,并表示公司已加入字节码联盟 (Bytecode Alliance),成为投票成员。Docker+Wasm 让开发者能够更容易地快速构建面向 Wasm 运行时的应用程序。

借助于 Wasm 的相关生态,可以非常方便地发布、下载和管理 eBPF 程序,例如,使用 wasm-to-oci 工具,可以将 Wasm 程序打包为 OCI 镜像,获取类似 docker 的体验:

wasm-to-ocipushtestdata/hello.wasm.azurecr.io/wasm-to-oci:v1
wasm-to-ocipull.azurecr.io/wasm-to-oci:v1--outtest.wasm

我们也将其集成到了 eunomia-bpf 的 ecli 工具中,可以一行命令从云端的 Github Packages 中下载并运行 eBPF 程序,或通过 Github Packages 发布:

#pushtoGithubPackages
eclipushhttps://ghcr.io/eunomia-bpf/sigsnoop:latest
#pullfromGithubPackages
eclipullhttps://ghcr.io/eunomia-bpf/sigsnoop:latest
#runeBPFprogram
eclirunhttps://ghcr.io/eunomia-bpf/sigsnoop:latest

我们已经在 LMP 项目的 eBPF Hub 中,有一些创建符合 OCI 标准的 Wasm-eBPF 应用程序,并利用 ORAS 简化扩展 eBPF 应用开发,分发、加载、运行能力的尝试[11],以及基于 Wasm 同时使用多种不同语言开发 eBPF 的用户态数据处理插件的实践。基于最新的 Wasm-bpf 框架,有更多的探索性工作可以继续展开,我们希望尝试构建一个完整的针对 eBPF 和 Wasm 程序的包管理系统,以及更多的可以探索的应用场景。

总结

本文以 Rust 语言为例,讨论了使用 Rust 编写 eBPF 程序并编译为 Wasm 模块以及使用 OCI 镜像发布、部署、管理 eBPF 程序,获得类似 Docker 的体验。更完整的代码,请参考我们的 Github 仓库:https://github.com/eunomia-bpf/wasm-bpf.

接下来,我们会继续完善在 Wasm 中使用多种语言开发和运行 eBPF 程序的体验,提供更完善的示例和用户态开发库/工具链,以及更具体的应用场景。

审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 模块
    +关注

    关注

    7

    文章

    2670

    浏览量

    47333
  • C++
    C++
    +关注

    关注

    22

    文章

    2104

    浏览量

    73482
  • Rust
    +关注

    关注

    1

    文章

    228

    浏览量

    6568

原文标题:在 WebAssembly 中使用 Rust 编写 eBPF 程序并发布 OCI 镜像

文章出处:【微信号:Rust语言中文社区,微信公众号:Rust语言中文社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何在Rust中使用Memcached

    Memcached是一种高性能、分布式的内存对象缓存系统,可用于加速动态Web应用程序Rust是一种系统级编程语言,具有内存安全、高性能和并发性等特点。Rust语言的Memcache
    的头像 发表于 09-19 16:30 1191次阅读

    如何编写高性能的Rust代码

    为了最大限度地提高Rust应用程序的性能,你需要了解支持代码的底层硬件架构,如何优化算法和数据结构,以及如何对代码进行配置和基准测试。本文中,我们将简要介绍这些主题,希望能更好地理解如何编写
    的头像 发表于 11-03 14:28 798次阅读
    如何<b class='flag-5'>编写</b>高性能的<b class='flag-5'>Rust</b>代码

    RUST嵌入式开发中的应用是什么

    的文档、有用的错误消息、友好编译器、一流的工具,只是Rust的几个好处。它带有一个集成的包管理器和构建工具,支持自动完成和类型检查的智能多编辑器,一个自动格式化程序,等等。为什么嵌入式开发
    发表于 12-24 08:34

    openEuler 倡议建立 eBPF 软件发布标准

    与推广。如下图所示,总结目前 eBPF 的开发、发布方式基本可以划分成 2 种技术路线:开发态、运行态分离(典型代表 libbpf)优点:ELF 文件形式(或者链接进应用程序发布,运
    发表于 12-23 16:21

    Rust代码中加载静态库时,出现错误 ` rust-lld: error: undefined symbol: malloc `怎么解决?

    “ [i]malloc ”、“ [i]exit ”。我验证了使用 ` [i]nm ` 命令。 问题是我打算使用 ffi rust 中使用这个静态库。当我尝试我的
    发表于 06-09 08:44

    关于Mozilla让WebAssembly并行启动

    Mozilla通过Firefox浏览器中使用并行提高了WebAssembly字节码和asm.js的Java子集的性能。 Mozilla的工程师通过使用并行来减少浏览器中asm.js程序
    发表于 10-10 17:32 5次下载
    关于Mozilla让<b class='flag-5'>WebAssembly</b>并行启动

    .NET应用程序可以直接调用WebAssembly模块了

    WebAssembly Runtime现已添加.NET Core API,开发者可直接在.NET应用程序中调用WebAssembly模块。
    的头像 发表于 12-10 11:35 2406次阅读

    WebAssembly中的BL602/BL604模拟器使用

    让我们使用WebAssembly Web 浏览器中模拟 BL602 / BL604 Rust 固件
    发表于 03-18 10:11 2次下载

    Chromium正式开始支持Rust

      Chromium 正式开始支持 Rust 目前的支持只是第一阶段,C++代码中使Rust写的第三方库(编译成.so)。估计明年Chromium的二进制发行文件中会包含
    的头像 发表于 01-14 10:04 939次阅读

    Rust中使用内联汇编

    与 GCC 内联汇编语法一样,Rust 希望即使需要手写汇编,程序员也能将一部分工作交给编译器来高效完成,这部分工作就是寄存器分配,毕竟只有编译器了解内联汇编前后的上下文,知道该怎么分配寄存器最合适。
    的头像 发表于 05-04 09:54 798次阅读

    Rust的内部工作原理

    : google发布的 libtracecmd Rust wrapper 这个库是libtracecmd的Rust wrapper,它允许编写程序来分析由trace-cmd 生成的Li
    的头像 发表于 06-14 10:34 764次阅读
    <b class='flag-5'>Rust</b>的内部工作原理

    使用C++编写通用库并在 Rust 中使用它 (WASI)

    使用 C++ 编写通用库并在 Rust 中使用它 (WASI) WebAssembly 简介 WebAssembly 是一种二进制指令格式,
    的头像 发表于 06-16 10:03 1028次阅读
    使用C++<b class='flag-5'>编写</b>通用库并在 <b class='flag-5'>Rust</b> <b class='flag-5'>中使</b>用它 (WASI)

    Rust开源社区推出龙架构原生适配版本

    应用程序时具有优良的并发性能,其高性能特性使Rust适用于编写高效的系统软件,如操作系统内核、嵌入式设备驱动程序和网络服务器。
    的头像 发表于 07-17 16:54 468次阅读
    <b class='flag-5'>Rust</b>开源社区推出龙架构原生适配版本

    如何在Rust项目中使用InfluxDB 2.x

    了更好的性能和更好的用户体验。Rust语言提供了InfluxDB 2.x的官方客户端库,可以方便地Rust项目中使用InfluxDB 2.x。 本教程将介绍如何在
    的头像 发表于 09-19 16:33 635次阅读

    FastTime-纯Rust编写的高并发快速时间库

    FastTime, 纯Rust编写的快速时间库, 并发: 2800万+/秒. 一、组件
    的头像 发表于 11-06 09:23 597次阅读