eBPF 為 Linux 核心提供了可擴充性,使開發人員能夠對 Linux 核心進行程式設計,以便根據他們的業務需求快速建構智能的或豐富的功能。
我們的 LMP(Linux Microscope) 項目^[1]^ 是為了充分挖掘 ebpf 的可能性而建立的,項目以建構 eBPF 學習社群、成為 eBPF 工具集散地、孵化 eBPF 想法和項目為目标,正在大力建設中。之前我們在 LMP 其中的 eBPF 超市 中包含了大量由個人開發者編寫的 eBPF 工具,覆寫了網絡、性能分析、安全等多種功能,我們正在嘗試把其中的一些程式遷移到 eBPF Hub,一些規範化的 eBPF 程式庫,可以随時下載下傳運作,或嵌入大型應用程式中作為插件使用。
我們嘗試在 eBPF Hub 中,基于 eunomia-bpf 開發架構建立符合 OCI 标準的 WASM 和 eBPF 程式,并利用 ORAS 簡化擴充 LMP 的 eBPF 分發、加載、運作能力。
快速使用
如果您想快速開始 eBPF,可以使用我們開發的輕量級架構之上的指令行程式 lmp-cli。當使用腳本安裝好我們的架構之後,您隻需要一條指令,無需任何編譯,即可體會到 eBPF 的強大之處:
$ lmp run sigsnoop
download with curl: https://linuxkerneltravel.github.io/lmp/sigsnoop/package.json
running and waiting for the eBPF events from perf event...
time pid tpid sig ret comm
00:21:41 109955 112863 28 0 gnome-terminal-
00:21:41 109955 112862 28 0 gnome-terminal-
...
如果您使用過 bcc 等 eBPF 開發工具,您一定會驚喜于 LMP 的便捷性。LMP 中包含了各種各樣的 eBPF 程式,這種便捷的運作,離不開我們基于的底層架構 eunomia-bpf,它完全實作了“一次編譯,處處運作”的 eBPF 跨平台目标。在 eunomia-bpf 架構下,LMP 開發的 eBPF 應用不僅可以适配任意架構和不同核心版本,而且還具有輕量級、良好的隔離性等優點,可以作為插件到嵌入大型應用之中。
eunomia-bpf:結合 eBPF 和 WASM 的輕量級開發架構
作為一個 eBPF 程式的輕量級開發加載架構,eunomia-bpf 基于 WASM 運作時和 BTF 技術,包含了一個使用者态動态加載架構/運作時庫,以及一個簡單的編譯 WASM 和 eBPF 位元組碼的工具鍊容器。
Wasm 是為了一個可移植的目标而設計的,可作為 C/C+/RUST 等進階語言的編譯目标,使用戶端和伺服器應用程式能夠在 Web 上部署。目前已經發展成為一個輕量級、高性能、跨平台和多語種的軟體沙盒環境,被運用于雲原生軟體元件。eunomia-bpf 将 eBPF 使用者态的所有控制和資料處理邏輯全部移到 WASM 虛拟機中,通過 WASM module 打包和分發 eBPF 位元組碼,同時在 WASM 虛拟機内部控制整個 eBPF 程式的加載和執行,将二者的優勢結合了起來。
在 WASM 子產品中編寫 eBPF 代碼和通常熟悉的使用 libbpf 架構或 Coolbpf 開發 eBPF 程式的方式是基本一樣的,WASM 的複雜性會被隐藏在 eunomia-bpf 的編譯工具鍊和運作時庫中,開發者可以專注于 eBPF 程式的開發和調試,不需要了解 WASM 的背景知識,也不需要擔心 WASM 的編譯環境配置。
大緻來說,eunomia-bpf 在 WASM 運作時和使用者态的 libbpf 中間多加了一層抽象層,使得一次編譯、到處運作的 eBPF 代碼可以從 JSON 對象中加載動态。JSON 對象會在編譯時被包含在 WASM 子產品中,是以在運作時,我們可以通過解析 JSON 對象來擷取 eBPF 程式的資訊,然後動态加載 eBPF 程式。通過 WASM module 打包和分發 eBPF 位元組碼,同時在 WASM 虛拟機内部控制整個 eBPF 程式的加載和執行,eunomia-bpf 就可以将二者的優勢結合起來,讓任意 eBPF 程式能有如下特性:
- 可移植:讓 eBPF 工具和應用不需要進行重新編譯即可以跨平台分發,省去了複雜的交叉編譯流程;
- 隔離性:讓 eBPF 程式的加載和執行、以及使用者态的資料處理流程更加安全可靠。
- 包管理:完成 eBPF 程式或工具的分發、管理、加載等工作。
- 靈活性:使每個人都可以使用官方和未經修改的應用程式來加載自定義擴充,任何 eBPF 程式的錯誤修複和/或更新都可以在運作時推送和/或測試,而不需要更新和/或重新部署一個新的二進制。
- 輕量級:與 Linux 容器應用相比,WASM 微服務冷啟動的時間是 1%,可以實作 eBPF as a service,讓 eBPF 程式的加載和執行變得更加輕量級、快速、簡便易行。
我們已經測試了在 x86、ARM 等不同架構不同核心版本的 Linux 系統上,eunomia-bpf 架構都可以使用同一個預編譯 eBPF 程式二進制,從雲端一行指令擷取到本地之後運作。之後 eunomia-bpf 還會添加 RISC-V 等更多架構的支援。
更多linux核心視訊教程文檔資料免費領取背景私信【核心】自行擷取.
使用 lmp-cli 建構一個 eBPF 項目
如果您是一個 eBPF 工具的使用者,您可以無需任何編譯流程,也不需要了解任何 eBPF 和 WASM 的相關知識,使用 就可以直接運作 LMP 倉庫的小程式,其中會調用指令從雲端從庫中下載下傳對應的小程式。lmp run <name>``lmp pull <name>
如果您是一個 eBPF 程式的開發者,讓我們開始建立、編譯并運作一個簡單的程式。在這裡,我們使用基簡單指令行工具 lmp-cli,概述如何從四個步驟開始建構。
1. 準備你的環境
eBPF 本身是一種 Linux 核心技術,是以任何實際的 BPF 程式都必須在 Linux 核心中運作。我建議您從核心 5.4 或更新的版本開始。從 SSH 終端,檢查核心版本,并确認您已經啟用了 CONFIG_DEBUG_INFO_BTF:
uname -r
cat /boot/config-$(uname -r) | grep CONFIG_DEBUG_INFO_BTF
你會看到類似這樣的輸出:
$ uname -r
5.15.0-48-generic
$ cat /boot/config-$(uname -r) | grep CONFIG_DEBUG_INFO_BTF
CONFIG_DEBUG_INFO_BTF=y
CONFIG_DEBUG_INFO_BTF_MODULES=y
安裝指令行工具 lmp-cli:
curl https://github.com/GorilaMond/lmp_cli/releases/download/lmp/install.sh | sh
2. 建立項目的核心部分
使用建立一個項目模闆,來初始化你的核心程式,快速地投入到代碼的編寫中:lmp init
lmp init hello
成功建立項目後,您将看到如下類似的輸出:
$ lmp init hello
Cloning into 'ebpm-template'...
它實際上建立了一個項目名對應的檔案夾,裡面有這些檔案:
$ cd hello/
$ ll
...
-rw-rw-r-- 1 a a 2910 10月 17 23:18 bootstrap.bpf.c
-rw-rw-r-- 1 a a 392 10月 17 23:18 bootstrap.bpf.h
-rw-rw-r-- 1 a a 221 10月 17 23:18 config.json
drwxrwxr-x 8 a a 4096 10月 17 23:18 .git/
drwxrwxr-x 3 a a 4096 10月 17 23:18 .github/
-rw-rw-r-- 1 a a 21 10月 17 23:18 .gitignore
-rw-rw-r-- 1 a a 2400 10月 17 23:18 README.md
核心程式模闆 bootstrap.bpf.c 中預設的跟蹤點為 和,用來跟蹤新程式的執行和退出,這裡不做修改。tp/sched/sched_process_exec``tp/sched/sched_process_exit
建構核心項目,如下所示。儲存您的更改,使用 建構核心程式,這會建立一個名為 package.json 的對象檔案。sudo lmp build
$ sudo lmp build
make
...
BINARY client
DUMP_LLVM_MEMORY_LAYOUT
DUMP_EBPF_PROGRAM
FIX_TYPE_INFO_IN_EBPF
GENERATE_PACKAGE_JSON
3. 運作核心程式
可以使用運作核心程式,沒有使用者端程式對資料的處理的情況下,該架構下核心程式将會輸出所有被 output 的資料:lmp run package.json
$ sudo lmp run ./package.json
running and waiting for the ebpf events from ring buffer...
time pid ppid exit_code duration_ns comm filename exit_event
一開始您不會看到任何資料,隻有當核心的跟蹤點被觸發時,這裡是新的程序被建立或退出時,才會輸出資料。這裡建立了一個虛拟終端,輸出了如下資料:
23:31:31 111788 109955 0 0 bash /bin/bash 0
23:31:31 111790 111788 0 0 lesspipe /usr/bin/lesspipe 0
...
4. 添加使用者态程式
我們提供的是 demo 是 C 語言版本的 WASM 開發架構,在建構好的核心項目檔案夾内,使用 生成一個 WASM 使用者态項目模闆,app.c、eunomia-include、ewasm-skel.h 這些檔案會被生成。ewasm-skel.h 是被打包為頭檔案的核心程式,app.c 是使用者态程式的模闆檔案,我們可以修改它來進行自定義的資料處理,這裡不做修改。sudo lmp gen-wasm-skel
$ sudo lmp gen-wasm-skel
make
BPF .output/client.bpf.o
...
使用建構使用者态程式,生成 app.wasm 檔案sudo lmp build-wasm
$ sudo lmp build-wasm
make
BPF .output/client.bpf.o
...
使用運作使用者态程式,json 格式的輸出為通用的資料處理做好了準備:lmp run app.wasm
$ lmp run app.wasm
running and waiting for the ebpf events from ring buffer...
{"pid":112665,"ppid":109955,"exit_code":0,"duration_ns":0,"comm":"bash","filename":"/bin/bash","exit_event":0}
{"pid":112667,"ppid":112665,"exit_code":0,"duration_ns":0,"comm":"lesspipe","filename":"/usr/bin/lesspipe","exit_event":0}
{"pid":112668,"ppid":112667,"exit_code":0,"duration_ns":0,"comm":"basename","filename":"/usr/bin/basename","exit_event":0}
...
另一個例子:使用 eBPF 列印程序記憶體使用狀況
可以将 bootstrap.bpf.c 重命名為 procstat.bpf.c,将 bootstrap.bpf.h 重命名為 procstat.bpf.h,然後編譯運作。對應的源代碼如下:
procstat.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include "procstat.h"
char LICENSE[] SEC("license") = "Dual BSD/GPL";
struct {
__uint(type, BPF_MAP_TYPE_RINGBUF);
__uint(max_entries, 256 * 1024);
} rb SEC(".maps");
SEC("kprobe/finish_task_switch")
int BPF_KPROBE(finish_task_switch, struct task_struct *prev)
{
struct event *e;
struct mm_rss_stat rss = {};
struct mm_struct *mms;
long long *t;
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
if (!e)
return 0;
e->pid = BPF_CORE_READ(prev, pid);
e->vsize = BPF_CORE_READ(prev, mm, total_vm);
e->Vdata = BPF_CORE_READ(prev, mm, data_vm);
e->Vstk = BPF_CORE_READ(prev, mm, stack_vm);
e->nvcsw = BPF_CORE_READ(prev, nvcsw);
e->nivcsw = BPF_CORE_READ(prev, nivcsw);
rss = BPF_CORE_READ(prev, mm, rss_stat);
t = (long long *)(rss.count);
e->rssfile = *t;
e->rssanon = *(t + 1);
e->vswap = *(t + 2);
e->rssshmem = *(t + 3);
e->size = *t + *(t + 1) + *(t + 3);
bpf_ringbuf_submit(e, 0);
return 0;
}
過程
#ifndef __BOOTSTRAP_H
#define __BOOTSTRAP_H
#define TASK_COMM_LEN 16
#define MAX_FILENAME_LEN 127
struct event {
/*程序記憶體狀态報告*/
pid_t pid;
long nvcsw;
long nivcsw;
long vsize; //虛拟記憶體
long size; //實體記憶體
long long rssanon; //匿名頁面
long long rssfile; //檔案頁面
long long rssshmem; //共享頁面
long long vswap; //交換頁面
long long Hpages; //hugetlbPages
long Vdata; //Private data segments
long Vstk; //User stack
long long VPTE;
};
#endif /* __BOOTSTRAP_H */
具體的上報事件資訊在 event 結構體中定義:
挂載點與挂載原因分析:
- 首先,擷取程序級别記憶體使用資訊需要擷取到程序的 task_struct 結構體,其中在 mm_struct 成員中存在一個儲存程序目前記憶體使用狀态的數組結構,是以有關程序的大部分記憶體使用資訊都可以通過這個數組獲得。
- 其次,需要注意函數的插入點,插入點的選取關系到資料準确性是否得到保證,而在程序的記憶體申請,釋放,規整等代碼路徑上都存在頁面狀态改變,但是數量資訊還沒有更新的相關結構中的情況,如果插入點這兩者中間,資料就會和實際情況存在差異,所有在確定可以擷取到程序 PCB 的前提下,選擇在程序排程代碼路徑上考慮。而 finish_task_switch 函數是新一個程序第一個執行的函數,做的事卻是給上一個被排程出去的程序做收尾工作,是以這個函數的參數是上一個程序的 PCB,從這塊獲得上一個程序的記憶體就可以確定在它沒有再次被排程上 CPU 執行的這段時間内的記憶體穩定性資料。
- 是以最後選擇将程式挂載到 finish_task_switch 函數上。資料來源有兩部分,一個是 mm_struc 結構本身存在的狀态資訊,另一個是在 mm_rss_stat 結構中。
也可以在 bolipi 的平台中線上編譯,線上體驗運作 eBPF 程式:https://bolipi.com/ebpf/home/online
完整的代碼、文檔和運作結果可以在 LMP 中 eBPF_Supermarket 處找到:eBPF_Supermarket/Memory_Subsystem/memstat/procstat^[2]^
相關背景
LMP 項目的成立初衷是:
- 面向 eBPF 初學者和愛好者,提供 eBPF 學習資料、程式/項目案例,建構 eBPF 學習社群
- 成為 eBPF 工具集散地,我們相信每一位 eBPF 初學者和愛好者都有無限的創造力
- 孵化 eBPF 想法、相關工具、項目
LMP 目前分為四個子項目:
- eBPF_Supermarket 中包含了大量由個人開發者編寫的 eBPF 工具,覆寫了網絡、性能分析、安全等多種功能;
- eBPF_Hub 是規範化的 eBPF 程式庫,可以随時下載下傳運作;
- eBPF_Visualization 是為 eBPF 程式管理而開發的 web 管理系統,聚焦 eBPF 資料可視化和核心可視化;
- eBPF_Documentation 為社群收集、梳理和原創的 eBPF 相關資料和文檔。
目前 LMP 項目也存在一些問題,例如對于 eBPF 工具的開發者,存在非常多而且複雜的使用者态可視化、展示方案,有許多套系統提供可視化的實作并且有多種語言混合,缺乏展示标準、也難以進行可視化的整合等。是以,我們希望嘗試借助 eunomia-bpf 提供的符合 OCI 标準的 WASM 和 eBPF 程式,提供标準化、高可擴充性的基于 eBPF 的可視化、資料展示、分析平台,利用 ORAS 簡化擴充 eBPF 的分發、加載、運作能力,為 eBPF 工具的開發者和使用者提供更加簡單、高效的體驗。
網絡裝配
WebAssembly 是一種新的編碼方式,可以在現代的網絡浏覽器中運作 - 它是一種低級的類彙編語言,具有緊湊的二進制格式,可以接近原生的性能運作,并為諸如 c\c++ 等語言提供一個編譯目标,以便它們可以在 Web 上運作。它也被設計為可以與 JavaScript 共存,允許兩者一起工作。而且,更棒的是,這是通過 W3C WebAssembly Community Group 開發的一項網絡标準,并得到了來自各大主要浏覽器廠商的積極參與。
盡管 WebAssembly 是為運作在 Web 上設計的,它也可以在其它的環境中良好地運作。包括從用作測試的最小化 shell ,到完全的應用環境 —— 例如:在資料中心的伺服器、物聯網(IoT)裝置或者是移動/桌面應用程式。甚至,運作嵌入在較大程式裡的 WebAssembly 也是可行的。通常,通過維持不需要 Web API 的非 Web 路徑,WebAssembly 能夠在許多平台上用作便攜式的二進制格式,為移植性、工具和語言無關性帶來巨大的好處。(因為它支援 c\c++ 級語義)
WASM 的編譯和部署流程如下:
OCI(開放容器倡議)
開放容器協定(OCI)是一個輕量級,開放的治理結構,為容器技術定義了規範和标準。在 Linux 基金會的支援下成立,由各大軟體企業構成,緻力于圍繞容器格式和運作時建立開放的行業标準。其中包括了使用 Container Registries 進行工作的 API,正式名稱為 OCI 分發規範(又名“distribution-spec”)。這個釋出規範是基于 Docker 公司最初釋出的開源注冊伺服器編寫的,它存在于 GitHub 的distribution/distribution^[3]^ (現在是CNCF^[4]^ 項目)上。
OCI 目前提出的規範有如下這些:
其中 runtime 和 image 的規範都已經正式釋出,而 distribution 的還在工作之中。runtime 規範中介紹了如何運作解壓縮到磁盤上的 'Filesystem Bundle'[8]。在 OCI 标準下,運作一個容器的過程就是下載下傳一個 OCI 的鏡像,将其解壓到某個 中,然後某個 OCI Runtime 就會運作這個 Bundle。Filesystem Bundle
伴随着 image spec 與分發 spec 的演化,人們開始逐漸認識到除了 Container Images 之外,Registries 還能夠用來分發 Kubernetes Deployment Files, Helm Charts, docker-compose, CNAB^[9]^ 等産物。它們可以共用同一套 API,同一套存儲,将 Registries 作為一個雲存儲系統。這就為帶來了 OCI Artifacts 的概念,使用者能夠把所有的産物都存儲在 OCI 相容的 Registiry 當中并進行分發。為此,Microsoft 将 oras^[10]^ 作為一個 client 端實作捐贈給了社群,包括 Harbor 在内的多個項目都在積極的參與。
ORAS(OCI 系統資料庫作為存儲)
Registries 正在逐漸演變為通用的元件存儲庫。為了實作這一目标,ORAS 項目提供了一種将 OCI Artifacts 從 OCI Registries 送出和拉取的方法。正在尋求通用 Registries 用戶端的使用者可以從ORAS CLI^[11]^ 中得到幫助,而開發人員可以在ORAS 用戶端的開發庫^[12]^ 之上建構自己的用戶端。
ORAS 的工作原理與您可能已經熟悉的工具(如 docker)類似。它允許您向 OCI Registries 推送(上傳)和提取(下載下傳)内容,并處理登入(身份驗證)和令牌流(授權)。ORAS 的不同之處在于将焦點從容器映像轉移到其他類型的元件上。
是以,鼓勵新的 OCI Artifacts 的作者定義他們自己的元件媒體類型,以使得他們的使用者知道如何對其進行操作。
如果您希望立即開始釋出 OCI Artifacts,請檢視ORAS CLI^[13]^ 。希望提供給自己使用者體驗的開發人員應該使用一個 ORAS 用戶端開發庫。
未來的發展方向
未來 LMP 會專注于更多的基于 eBPF 的應用工具和實踐的開發:
- 進一步完善 ORAS 和 OCI 鏡像相關的支援;
- 重構并遷移現有的 eBPF 工具,提供完整的、開箱即用的分析工具元件,例如性能工程等方面;
- 探索和孵化更多的 eBPF 想法、相關工具、項目;
我們所基于的 eunomia-bpf 項目也會繼續完善,專注于提供一個底層的 eBPF 開發平台和運作時基礎設施,力求帶來更好的開發和移植體驗:
- 測試更多平台和核心版本的支援,目前已經在 和 上成功移植并運作,接下來會對低核心版本、Android、RISC-V 等平台,以及嵌入式、邊緣計算相關的裝置進行更進一步的測試;也許在未來,我們還可以提供 Windows 上的 eBPF 程式支援和類似的開發體驗;ARM64``x86_64
- 提供标準化、穩定的 JSON 和 WASM 接口協定規範以及 OCI 鏡像規範,不和任何的供應商或雲服務綁定。如果不使用 eunomia-bpf 相關的底層運作時,或使用自定義的 WASM 運作時,也可以通過标準化的接口來使用 LMP 中已經有的大量 eBPF 程式生态。
- 提供更友好的使用者态開發接口,以及更多的使用者态開發語言 SDK,例如 Go、Rust、Python 等;
- 進行更多關于 WASM 和 eBPF 結合的探索;
原文作者:技術簡說