Linux 新技術基石 ​eBPF and XDP

hi,大家好,今天給大家分享的是 Linux 最近特別火的新技術,當前最流行 Linux 內核技術是什麼?那就是 eBPF 技術,著名開源的 Cilium 把 eBPF 技術帶飛,國內 Linux,容器雲原生等領域愛好者也燃起一股學習 eBPF 的熱潮。

新技術出現的歷史原因

廉頗老矣,尚能飯否

iptables/netfilter

iptables/netfilter 是上個時代 Linux 網絡提供的優秀的防火牆技術,擴展性強,能夠滿足當時大部分網絡應用需求,如果不知道 iptables/netfilter 是什麼,請參考之前文章:一個奇葩的網絡問題,把技術磚家 "搞蒙了" ,裏面對 iptables/netfilter 技術有詳細介紹。

但該框架也存在很多明顯問題:

netfilter 框架在 IP 層,報文需要經過鏈路層,IP 層才能被處理,如果是需要丟棄報文,會白白浪費很多 CPU 資源,影響整體性能;

如上圖所示,極端情況下,報文需要依次遍歷所有規則,才能匹配中,極大影響報文處理性能;

netfilter 框架類似一套可以自由添加策略規則專家系統,並沒有對添加規則進行合併優化,這些都嚴重依賴操作人員技術水平,隨着規模的增大,規則數量 n 成指數級增長,而報文處理又是 0(n)複雜度,最終性能會直線下降。

內核協議棧

隨着互聯網流量越來愈大, 網卡性能越來越強,Linux 內核協議棧在 10Mbps/100Mbps 網卡的慢速時代是沒有任何問題的,那個時候應用程序大部分時間在等網卡送上來數據。

現在到了 1000Mbps/10Gbps/40Gbps 網卡的時代,數據被很快地收入,協議棧複雜處理邏輯,效率捉襟見肘,把大量報文堵在內核裏。

各類鏈表在多 CPU 環境下的同步開銷。

不可睡眠的軟中斷路徑過長。

sk_buff 的分配和釋放。

內存拷貝的開銷。

上下文切換造成的 cache miss。

於是,內核協議棧各種優化措施應着需求而來:

網卡 RSS,多隊列。

中斷線程化。

分割鎖粒度。

Busypoll。

但卻都是見招拆招,治標不治本。問題的根源不是這些機制需要優化,而是這些機制需要推倒重構。蒸汽機車剛出來的時候,馬車伕爲了保持競爭優勢,不是去換一匹昂貴的快馬,而是賣掉馬去買一臺蒸汽機裝上。基本就是這個意思。

重構的思路很顯然有兩個:

upload 方法:別讓應用程序等內核了,讓應用程序自己去網卡直接拉數據。

offload 方法:別讓內核處理網絡邏輯了,讓網卡自己處理。

總之,繞過內核就對了,內核協議棧揹負太多歷史包袱。

DPDK 讓用戶態程序直接處理網絡流,bypass 掉內核,使用獨立的 CPU 專門幹這個事。

XDP 讓灌入網卡的 eBPF 程序直接處理網絡流,bypass 掉內核,使用網卡 NPU 專門幹這個事。

如此一來,內核協議棧就不再參與數據平面的事了,留下來專門處理諸如路由協議,遠程登錄等控制平面和管理平面的數據流。

改善 iptables/netfilter 的規模瓶頸,提高 Linux 內核協議棧 IO 性能,內核需要提供新解決方案,那就是 eBPF/XDP 框架,讓我們來看一看,這套框架是如何解決問題的。

eBPF 到底是什麼?

eBPF 的歷史

BPF 是 Linux 內核中高度靈活和高效的類似虛擬機的技術,允許以安全的方式在各個掛鉤點執行字節碼。它用於許多 Linux 內核子系統,最突出的是網絡、跟蹤和安全(例如沙箱)。

BPF 架構

BPF 是一個通用目的 RISC 指令集,其最初的設計目標是:用 C 語言的一個子集編 寫程序,然後用一個編譯器後端(例如 LLVM)將其編譯成 BPF 指令,稍後內核再通 過一個位於內核中的(in-kernel)即時編譯器(JIT Compiler)將 BPF 指令映射成處理器的原生指令(opcode ),以取得在內核中的最佳執行性能。

BPF 指令

儘管 BPF 自 1992 年就存在,擴展的 Berkeley Packet Filter (eBPF) 版本首次出現在 Kernel3.18 中,如今被稱爲 “經典”BPF (cBPF) 的版本已過時。許多人都知道 cBPF 是 tcpdump 使用的數據包過濾語言。現在 Linux 內核只運行 eBPF,並且加載的 cBPF 字節碼在程序執行之前被透明地轉換爲內核中的 eBPF 表示。除非指出 eBPF 和 cBPF 之間的明確區別,一般現在說的 BPF 就是指 eBPF。

eBPF 總體設計

eBPF 總體設計包括以下幾個部分:

eBPF Runtime

eBPF Hooks

eBPF Maps

Map 類型

作用

eBPF Helpers

有哪些 Helpers?

eBPF Tail and Function Calls

尾調用有什麼用?

● 將程序鏈接在一起

● 將程序拆分爲獨立的邏輯組件

● 使 BPF 程序可組合

函數調用有什麼用?

● 重用內部的功能程序

● 減少程序大小(避免內聯)

eBPF JIT Compiler

eBPF 可以做什麼?

eBPF 開源 Projects

Cilium

 

Cilium 在它的 datapath 中重度使用了 BPF 技術

對比傳統容器網絡(採用 iptables/netfilter):

BCC(BPF Compiler Collection)

BCC 是一個框架,它使用戶能夠編寫嵌入其中的 eBPF 程序的 Python 程序。該框架主要針對涉及應用程序和系統分析 / 跟蹤的用例,其中 eBPF 程序用於收集統計信息或生成事件,用戶空間中的對應部分收集數據並以人類可讀的形式顯示。運行 python 程序將生成 eBPF 字節碼並將其加載到內核中。

bpftrace

bpftrace 是一種用於 Linux eBPF 的高級跟蹤語言,可在最近的 Linux 內核 (4.x) 中使用。bpftrace 使用 LLVM 作爲後端將腳本編譯爲 eBPF 字節碼,並利用 BCC 與 Linux eBPF 子系統以及現有的 Linux 跟蹤功能進行交互:內核動態跟蹤 (kprobes)、用戶級動態跟蹤 (uprobes) 和跟蹤點. bpftrace 語言的靈感來自 awk、C 和前身跟蹤器,例如 DTrace 和 SystemTap。

eBPF Go 庫

eBPF Go 庫提供了一個通用的 eBPF 庫,它將獲取 eBPF 字節碼的過程與 eBPF 程序的加載和管理解耦。eBPF 程序通常是通過編寫高級語言創建的,然後使用 clang/LLVM 編譯器編譯爲 eBPF 字節碼。

libbpf C/C++ 庫

libbpf 庫是一個基於 C/C++ 的通用 eBPF 庫,它有助於解耦從 clang/LLVM 編譯器生成的 eBPF 目標文件加載到內核中,並通過提供易於使用的庫 API 來抽象與 BPF 系統調用的交互應用程序。

那 XDP 又是什麼?

XDP 的全稱是:eXpress Data Path

XDP 是 Linux 內核中提供高性能、可編程的網絡數據包處理框架。

XDP 整體框架

XDP 總體設計

XDP 總體設計包括以下幾個部分:

XDP 驅動

網卡驅動中 XDP 程序的一個掛載點,每當網卡接收到一個數據包就會執行這個 XDP 程序;XDP 程序可以對數據包進行逐層解析、按規則進行過濾,或者對數據包進行封裝或者解封裝,修改字段對數據包進行轉發等;

BPF 虛擬機

並沒有在圖裏畫出來,一個 XDP 程序首先是由用戶編寫用受限制的 C 語言編寫的,然後通過 clang 前端編譯生成 BPF 字節碼,字節碼加載到內核之後運行在 eBPF 虛擬機上,虛擬機通過即時編譯將 XDP 字節碼編譯成底層二進制指令;eBPF 虛擬機支持 XDP 程序的動態加載和卸載;

BPF maps

存儲鍵值對,作爲用戶態程序和內核態 XDP 程序、內核態 XDP 程序之間的通信媒介,類似於進程間通信的共享內存訪問;用戶態程序可以在 BPF 映射中預定義規則,XDP 程序匹配映射中的規則對數據包進行過濾等;XDP 程序將數據包統計信息存入 BPF 映射,用戶態程序可訪問 BPF 映射獲取數據包統計信息;

BPF 程序校驗器

XDP 程序肯定是我們自己編寫的,那麼如何確保 XDP 程序加載到內核之後不會導致內核崩潰或者帶來其他的安全問題呢?程序校驗器就是在將 XDP 字節碼加載到內核之前對字節碼進行安全檢查,比如判斷是否有循環,程序長度是否超過限制,程序內存訪問是否越界,程序是否包含不可達的指令;

XDP Action

XDP 用於報文的處理,支持如下 action:

enum xdp_action {
    XDP_ABORTED = 0,
    XDP_DROP,
    XDP_PASS,
    XDP_TX,
    XDP_REDIRECT,
};

AF_XDP

AF_XDP 是爲高性能數據包處理而優化的地址族,AF_XDP 套接字使 XDP 程序可以將幀重定向到用戶空間應用程序中的內存緩衝區。

XDP 設計原則

XDP 技術優勢

及時處理

高性能優化

指令虛擬機

可擴展模型

可編程性

XDP 工作模式

XDP 有三種工作模式,默認是 native(原生)模式,當討論 XDP 時通常隱含的都是指這 種模式。

XDP vs DPDK

相對於 DPDK,XDP:

優點

缺點

注意 XDP 的性能提升是有代價的,它犧牲了通用型和公平性

如何選擇?

XDP 適合場景

XDP 例子

下面是一個最小的完整 XDP 程序,實現丟棄包的功能(xdp-example.c):

#include <linux/bpf.h>

#ifndef __section
# define __section(NAME)                  \
   __attribute__((section(NAME), used))
#endif

__section("prog")
int xdp_drop(struct xdp_md *ctx)
{
    return XDP_DROP;
}

char __license[] __section("license") = "GPL";

用下面的命令編譯並加載到內核:

$ clang -O2 -Wall -target bpf -c xdp-example.c -o xdp-example.o
$ ip link set dev em1 xdp obj xdp-example.o

以上命令將一個 XDP 程序 attach 到一個網絡設備,需要是 Linux 4.11 內核中支持 XDP 的設備,或者 4.12+ 版本的內核。

最後

eBPF/XDP 作爲 Linux 革新技術正在悄悄改變着 Linux 網絡發展模式。

eBPF 正在將 Linux 內核轉變爲微內核,越來越多的新內核功能採用 eBPF 實現,讓新增內核功能更加快捷高效。

總體而言,基於業界基準測試結果,eBPF 顯然是解決具有挑戰性的雲原生需求的最佳技術。

參考 & 延伸閱讀

The eXpress Data Path: 

Fast Programmable Packet Processing in the Operating System Kernel

https://docs.cilium.io/en/v1.6/bpf/

bpf-rethinkingthelinuxkernel-200303183208

https://ebpf.io/what-is-ebpf/

https://www.kernel.org/doc/html/latest/networking/af_xdp.html

https://cilium.io/blog/2021/05/11/cni-benchmark

https://blog.csdn.net/dog250/article/details/107243696

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/VmDfYDVlz7PVN6sz6HrIgg