BumbleBee: 如絲般順滑構建、交付和運行 eBPF 程序

本文地址://www.ebpf.top/post/bumblebee

1. 前言

不久前,Solo.io 公司在官網博客宣布了開源了一個名稱為 BumbleBee 的新項目。該項目專註於簡化構建 eBPF 工具的門檻和優化使用體驗,通過將 eBPF 程序打包成 OCI 鏡像,帶來了與使用 Docker 一致的體驗的構建、分發和運行 eBPF 程序。

BumbleBee 目的是讓我們專註於編寫 eBPF 代碼,其負責自動生成與 eBPF 程序相關的用戶空間的代碼功能,包括加載 eBPF 程序和將 eBPF 程序的數據作為日誌、指標和直方圖進行展示。

那麼我們為什麼需要 BumbleBee 項目來管理 eBPF 程序呢?這需要從 eBPF 技術的特點講起。

2. 構建和分發 eBPF 工具的挑戰

eBPF 技術被稱之為近 50 年來操作系統最大的變革,解決了 Linux 內核在上游開發、合併和發行功能緩慢的窘境。 eBPF 技術為內核提供了不通過上游實現內核的定製功能的能力,當前已經在可觀測、網絡和安全等多個領域得到了廣泛的應用,尤其是在雲原生的技術潮流中,eBPF 技術發揮的能力也越來越重要,諸如當前風頭正盛的 Cilium 項目。

但是開發、構建和分發 eBPF 一直以來都是一個高門檻的工作,社區先後推出了 BCC、BPFTrace 等前端綁定工作,大大降低了編寫和使用 eBPF 技術的門檻,但是這些工具的源碼交付方式,需要運行 eBPF 程序的 Linux 系統上安裝配套的編譯環境,對於分髮帶來了不少的麻煩,同時將內核適配的問題在運行時才能驗證,也不利於提前發現和解決問題。

近年來,為解決不同內核版本中 eBPF 程序的分發和運行問題,社區基於 BTF 技術推出了 CO-RE 功能(「一次編譯,到處運行」),一定程度上實現了通過 eBPF 二進制位元組碼分發,同時也解決了運行在不同內核上的移植問題,但是對於如何打包和分發 eBPF 二進制代碼還未有統一簡潔的方式。除了 eBPF 程序,目前我們還需要編寫用於加載 eBPF 程序和用於讀取 eBPF 程序產生數據的各種代碼,這往往涉及到通過源碼複製粘貼來解決部分問題。

此外,libbpf-bootstrap 通過 bpftool 工具生成相關的腳手架代碼,一定程度上解決了通用代碼重複編寫的問題,但是對於構建、分發和運行 eBPF 程序上提供的幫助有限。

3. BumbleBee 簡介

BumbleBee 項目正是 Solo 公司在企業服務網格 Gloo-Mesh 項目中為方便應用 eBPF 技術而誕生中,其用於解決在構建、分發和運行 eBPF 程序遇到的重複性挑戰,

當前該項目還在早期(當前版本 0.0.9),提供的功能場景(Network 和 FileSystem)有限,但是基於特定模板能力來構建 OCI 鏡像的思路,為我們在管理 eBPF 程序方面提供了一種高效簡潔的實現,值得我們關注。

使用 BumbleBee 工具的前置依賴: 運行的 eBPF 的操作系統開啟了 BTF 支持,編寫的 eBPF 代碼也需要使用 CO-RE 相關函數,關於 CO-RE 相關的技術可以參考這裡

BumbleBee 提供了與 Docker 一致的體驗感覺。下圖是 Docker 的高層次示意圖,BumbleBee 工具完全參考了這個流程。

3.1 構建

BumbleBee 打造 “恰到好處” 的 eBPF 工具鏈,將 eBPF 程序的構建過程自動化,讓你專註於代碼本身。 BumbleBee 的 eBPF 代碼打包成一個 OCI 標準鏡像,這樣就可以在基礎設施中進行分發。

下述命令可實現將 eBPF 程序 probe.c 的直接編譯和打包成鏡像 my_probe:v1 。

$ bee build probe.c username/my_probe:v1

3.2 發佈

利用 BTF 和 OCI 打包能力,BumbleBee 編寫的 eBPF 代碼是可移植的,並且可以嵌入到現有的發佈工作流程中。 通過將 eBPF 代碼構建的鏡像,推送到任何符合 OCI 標準的鏡像倉庫,就可以實現發佈給其他用戶使用。

下述命令實現了將鏡像發佈至鏡像倉庫的功能,使用時可直接使用 bee run 基於鏡像運行。

# 推送
$ bee push username/my_probe:v1 

# 拉取
$ bee pull username/my_probe:v1 

3.3 運行

使用 BumbleBee 提供的 CLI 界面和保存在鏡像倉庫中的鏡像,我們可快速在其他地方運行。 BumbleBee 不但構建了用戶空間代碼,而且可以利用 eBPF map,來展示日誌、指標和柱狀圖信息。 BumbleBee 使用了 BTF 格式自審能力,獲知到需要顯示哪些數據類型。

$ bee run my_probe:v1

下面讓我們通過一個完整的樣例,來體驗 BumbleBee 帶給我們管理 eBPF 程序的便利。

4. 完整體驗

4.1 bee 安裝

首先我們需要一個運行支持 BTF 內核的 Linux 操作系統,這裡推薦直接使用 ubuntu 2110 版本,搭載的內核已經默認支持了 BTF。如果你選擇使用 Vagrant 來管理虛擬機,BumbleBee 倉庫中提供的 Vagrantfile 文件可以直接使用。或者你可以使用 mulipass 工具直接啟動一個 ubuntu 2110 版本的系統。

這裡使用倉庫提供的腳本安裝,當然也可以直接通過 git clone 倉庫的方式進行。

為了快速體驗,避免某些場景中的權限問題,這裡建議直接使用 root 用戶進行安裝。

ubuntu@ubuntu21-10:~# curl -sL //run.solo.io/bee/install | BUMBLEBEE_VERSION=v0.0.9 sh
Attempting to download bee version v0.0.9
Downloading bee-linux-amd64...
Download complete!, validating checksum...
Checksum valid.
bee was successfully installed 🎉

Add the bumblebee CLI to your path with:
  export PATH=$HOME/.bumblebee/bin:$PATH

Now run:
  bee init     # Initialize simple eBPF program to run with bee
Please see visit the bumblebee website for more info:  //github.com/solo-io/bumblebee

安裝完成後,bee 的主要命令如下:

# bee --help
Usage:
  bee [command]

Available Commands:
  build       Build a BPF program, and save it to an OCI image representation.
  completion  generate the autocompletion script for the specified shell
  describe    Describe a BPF program via it's OCI ref
  help        Help about any command
  init        Initialize a sample BPF program
  list
  login       Log in so you can push images to the remote server.
  pull
  push
  run         Run a BPF program file or OCI image.
  tag
  version

Flags:
  -c, --config stringArray   path to auth configs
      --config-dir string    Directory to bumblebee configuration (default "/root/.bumblebee")
  -h, --help                 help for bee
      --insecure             allow connections to SSL registry without certs
  -p, --password string      registry password
      --plain-http           use plain http and not https
      --storage string       Directory to store OCI images locally (default "/root/.bumblebee/store")
  -u, --username string      registry username
  -v, --verbose              verbose output

Use "bee [command] --help" for more information about a command.

4.2 Bee init 生成 eBPF 程序腳手架

Bee init 命令可通過問題嚮導模式生成 eBPF 代碼腳手架,功能與 libbpf-bootstrap 有些類似,但是通過嚮導的方式進行更加容易上手。

$ export PATH=$HOME/.bumblebee/bin:$PATH
# ebpf-test && cd ebpf-test
# bee init
Use the arrow keys to navigate: ↓ ↑ → ←  
? What language do you wish to use for the filter: # 步驟 選擇編寫 eBPF 代碼的語言
  ▸ C   # 當前僅支持 C,Rust 可能在未來支持
---------------------------------------------  # 步驟 2 選擇 eBPF 程序類型
 INFO  Selected Language: C
Use the arrow keys to navigate: ↓ ↑ → ←
? What type of program to initialize: 
  ▸ Network        # 選擇編寫 eBPF 程序的類型,當前支持 Network 和 File System
    File system    # 生成的模板分別對應於 tcp_connet 和 open 函數
--------------------------------------------- # 步驟 3  選擇 map 類型 
 INFO  Selected Language: C
 INFO  Selected Program Type: Network
Use the arrow keys to navigate: ↓ ↑ → ←
? What type of map should we initialize: 
  ▸ RingBuffer     
    HashMap
 --------------------------------------------- # 步驟 4 選擇 map 導出類型
 INFO  Selected Language: C
 INFO  Selected Program Type: Network
 INFO  Selected Map Type: HashMap
Use the arrow keys to navigate: ↓ ↑ → ←  
? What type of output would you like from your map:
  ▸ print    # map 數據的展現方式,日誌打印、計數或者指標導出
    counter
    gauge
 ---------------------------------------------  # 步驟 5 eBPF 程序保存文件名
 INFO  Selected Language: C 
 INFO  Selected Program Type: Network
 INFO  Selected Map Type: HashMap
 INFO  Selected Output Type: print
✔ BPF Program File Location: probe.c  
 ---------------------------------------------- # 最終完成整個代碼生成嚮導
 INFO  Selected Language: C
 INFO  Selected Program Type: Network
 INFO  Selected Map Type: HashMap
 INFO  Selected Output Type: print
 INFO  Selected Output Type: BPF Program File Location probe.c
 SUCCESS  Successfully wrote skeleton BPF program  
 
 
# ls -hl
total 4.0K
-rw-rw-r-- 1 ubuntu ubuntu 2.0K Feb 11 11:33 probe.c

通過 init 命令生成的 probe.c 文件格式大體如下:

#include "vmlinux.h"
#include "bpf/bpf_helpers.h"
#include "bpf/bpf_core_read.h"
#include "bpf/bpf_tracing.h"
#include "solo_types.h"

// 1. Change the license if necessary 
char __license[] SEC("license") = "Dual MIT/GPL";

struct event_t {
	// 2. Add ringbuf struct data here.
} __attribute__((packed));

// This is the definition for the global map which both our
// bpf program and user space program can access.
// More info and map types can be found here: //www.man7.org/linux/man-pages/man2/bpf.2.html
struct {
	__uint(max_entries, 1 << 24);
	__uint(type, BPF_MAP_TYPE_RINGBUF);
	__type(value, struct event_t);
} events SEC(".maps.print");


SEC("kprobe/tcp_v4_connect")
int BPF_KPROBE(tcp_v4_connect, struct sock *sk)
{
	// Init event pointer
	struct event_t *event;

	// Reserve a spot in the ringbuffer for our event
	event = bpf_ringbuf_reserve(&events, sizeof(struct event_t), 0);
	if (!event) {
		return 0;
	}

	// 3. set data for our event,
	// For example:
	// event->pid = bpf_get_current_pid_tgid();

	bpf_ringbuf_submit(event, 0);

	return 0;
}

基於生成的代碼模板,我們需要填寫自己的邏輯,這裡不是重點,先略過相關代碼,完整代碼可在官方開始文檔中查看。

4.3 構建 eBPF 程序

構建過程需要使用 Docker 或者類型 Docker 的容器引擎,需要提前進行安裝。

# apt  install docker.io  # 安裝 docker

# bee build probe.c my_probe:v1
 SUCCESS  Successfully compiled "probe.c" and wrote it to "probe.o"
 SUCCESS  Saved BPF OCI image to my_probe:v1 

整個構建過程中我們不需要再涉及 clang 等相關編譯命令,只需要通過 bee build 命令輸入 eBPF 程序文件名和期望生成的鏡像即可,編譯完成後,eBPF 程序的二進制位元組碼 probe.o 會自動添加到鏡像 my_probe:v1 中,我們可以使用 bee tag 完成鏡像倉庫的重新定義。

4.4 發佈 eBPF 程序

我們可以通過 bee tag 和 push 子命令完成進行鏡像倉庫的發佈工作。

# bee tag my_probe:v1 dwh0403/my_probe:v1
# bee login
# bee push dwh0403/my_probe:v1

看一下上述的幾條命令,是不是有些似曾相識的感覺?

4.5 運行 eBPF 程序

構建鏡像後,在本地可直接通過 bee run 來運行,運行後 bee 會自動啟動 TUI 界面,來展示我們編寫 eBPF 程序中的 map 內容,自動生成的 map 名字有些特殊後綴用於 bee TUI 用戶空間的程序來讀取對應 map 中數據進行展示,比如生成代碼模板中的SEC(".maps.print"),表示該 map 用於打印。

# bee run my_probe:v1
 SUCCESS  Fetching program from registry: my_probe:v1
 SUCCESS  Loading BPF program and maps into Kernel
 SUCCESS  Linking BPF functions to associated probe/tracepoint
 INFO  Rendering TUI..

run

5. 總結

至此,我們完成了整個項目功能的體驗,bee init 工具可通過嚮導模式幫助我們生成 eBPF 代碼框架,儘管功能還有些單薄,但是對於我們特定場景的使用不失是一種快速便捷的方式。

bee build/push/run 等子命令,將編譯的命令、打包鏡像、發佈鏡像和運行鏡像的等諸多步驟進行了極大的精簡,非常易用,極大地降低了構建、發佈和運行 eBPF 程序的重複成本,不得不為作者的思路點贊。

由於通過 bee 生成的工具基於特定場景,功能豐富度還有限,對於編寫複雜情況下的 eBPF 程序和功能豐富的用戶空間程序還不能適用,但是其構建、發佈和運行的整體思路(甚至部分基礎功能)卻是我們可以直接使用或者借鑒的。

6. 參考資料

Tags: