iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0

目前為止,我們建立了完整的 Observability 2.0 架構:從資料收集(OTLP)、儲存(Parquet + Iceberg or S3 Table)、處理(Schema 設計與轉換)到視覺化(Grafana)。但這些架構都有一個共同前提,就是應用程式必須主動暴露 metrics、logs 或 traces。

如果應用程式沒有辦法主動暴露 signal 怎麼辦?再來,如果你想監控的不是應用程式,而是 Kernel 層級的網路封包、系統呼叫、或 CPU 排程行為呢?傳統的監控工具很難做到這些。

在鐵人賽的倒數四天,我們會介紹 eBPF (extended Berkeley Packet Filter),一個能在 Linux Kernel 中安全執行程式的技術。雖然短短三到四天的篇幅,要從零開始講述 eBPF,可能沒辦法講得全面與深入,但也希望藉由不同的視角,讓我對可觀測性工程有更全面的認識。

BPF: eBPF 的前身

BPF (Berkeley Packet Filter, 為了和eBPF 做出區別也稱 Classic BPF) 是 eBFP 的前身,它是一個用在 Unix-like 系統上截取封包的架構。

在 BPF 尚未問世之前,截取封包的方式非常繁瑣且耗費系統效能:

  1. 網卡收到封包後,Kernel 會將每一個封包都複製一份給 user space 的應用程式(例如 tcpdump)
  2. 應用程式收到所有封包後,再用自己的邏輯判斷哪些封包是需要的、哪些要丟棄
  3. 個封包都需要從 kernel mode 切換到 user mode,並進行記憶體複製,即使最後 99% 的封包都會被丟棄

舉例來說,如果你想用 tcpdump 抓取 port 80 的 HTTP 封包

tcpdump port 80

BPF 則可以解決這樣的問題,讓 User space 中的應用程式透過額外的程式,告訴作業系統要過濾掉哪些網路封包。這樣的手法讓封包可以在 kernel 層就決定是否過濾,而不用將每個封包丟到 User space 進行處理。

只不過,BPF 的能力就僅限於網路封包的處理,並且其支援的指令集非常有限。因此在 2014 年,Linux kernel 引入 eBPF(extended BPF)的概念,擴展更多的功能。

輕鬆擴展 kernel 功能的 eBPF

相較於 BPF 的單一功能,eBPF 允許開發者在作業系統 kernel 中執行自訂程式,同時確保這些程式是安全且受控的。能夠安全且高效地擴展 kernel 的功能,而不需要修改 kernel 原始碼或載入 kernel 模組。 從歷史來看,作業系統 kernel 一直是實作可觀測性、安全性和網路功能的理想場所,因為 kernel 具有監督和控制整個系統的能力。
https://ithelp.ithome.com.tw/upload/images/20251011/201779618OMR1UOByc.png

傳統上,想要在 kernel 層級加入新功能的創新速度,遠遠落後於在應用層實作的功能。 eBPF 從根本上改變了這個局面。透過 eBPF,開發者可以在不修改 kernel 原始碼的情況下,動態載入程式到 kernel 中執行。這帶來了幾個關鍵優勢:

1. 不需要等待 kernel 版本更新

由於 eBPF 是在作業系統運行時,才額外添加功能上去,所以開發者就不需要等待 kernel 本身的版本更新,就可以加入新的監控功能。這個支援在 Linux kernel 3.18 版本以後就可以使用。

2. 安全性保證

雖然 eBPF 程式運行在 kernel space,但它不會像傳統的 kernel module 一樣有安全風險。載入 kernel module 如果有 bug,可能會導致 kernel panic、系統當機、記憶體洩漏、安全漏洞等風險。

eBPF 則透過 verifier 機制在載入前進行嚴格檢查,確保以下幾件事:

  • 程式不會進入無窮迴圈(檢查所有分支都有結束條件)
  • 不會存取非法記憶體位址(檢查所有記憶體存取都在合法範圍)
  • 程式複雜度有上限(限制指令數量)
  • 不會呼叫未授權的 kernel 函數
  • 如果程式無法通過檢查,kernel 會拒絕載入,確保系統穩定性不受影響。

3. 高效能

eBPF bytecode 在載入時會透過 JIT (Just-In-Time) compiler 編譯成原生機器碼,執行效能接近手寫的 kernel code。同時,eBPF 可以在 kernel 內直接做資料聚合和過濾,只把最終結果傳回 user space,大幅減少 context switch 和記憶體複製的開銷。

那麼 eBPF 是怎麼動態載入到 Kernel 上面的?在這邊,我們就必須談到 Hook 的概念。

eBPF 的 Hook Points

eBPF 程式是事件驅動(event-driven)的。當 kernel 或應用程式執行到特定的 hook point 時就會被觸發。預先定義的 hooks 包括:

  • 系統呼叫 (System Calls): 當應用程式呼叫 open(), read(), write() 等系統呼叫時觸發
  • 函數進入/離開 (Function Entry/Exit): 在特定函數開始執行或結束時觸發,可用於追蹤執行時間和呼叫關係
  • Kernel Tracepoints: Kernel 中預先定義的穩定追蹤點,例如排程事件、記憶體分配等
  • 網路事件 (Network Events): 封包的接收、發送、處理等網路相關事件

如果以上預先定義的 hooks 無法滿足需求,也可以建立 Kernel Probe (kprobe) 或者 User Probe (uprobe),兩者分別的用途就是分別掛載在 kernel 或者 user space 中的函式上。
https://ithelp.ithome.com.tw/upload/images/20251011/20177961R631JZeZS8.png

eBPF 可以掛載的各種 hook points,從 user space 的 process、system call 層、到 kernel 內部的 VFS、TCP/IP、網路設備層,幾乎涵蓋了系統的每個層級。

透過這些 hook points,eBPF 程式可以掛載在 kernel 或應用程式中的幾乎任何位置,實現全方位的可觀測性。

結語

eBPF 讓我們能在不修改 kernel 或應用程式的情況下,收集系統各個層級的可觀測性資料。從 BPF 最初的網路封包過濾,到 eBPF 擴展到系統呼叫、kernel 函數、甚至 user space 的追蹤,eBPF 補足了傳統應用層監控看不到的盲點

而透過多樣化的 hook points,eBPF 可以在系統的關鍵時刻收集資料,實現從網路層到應用層的全方位可觀測性。接下來兩天,我們會看看如何使用 eBPF 工具收集實際的可觀測性資料,以及要如何將 eBPF 的資料和 OpenTelemetry 進行整合。

參考資料

eBPF Documentation - What is eBPF?

Ian Chen - 初探 Linux Kernel 中的 BPF 與 XDP 技術:以 Tiny Load Balancer 為例

Linux kernel Documentation - Classic BPF vs eBPF

Linux kernel Documentation - eBPF verifier


上一篇
Day 26 - 使用 Grafana 將可觀測性資料視覺化
下一篇
Day 28 - 使用 bpftrace 實現 eBPF 追蹤
系列文
被稱作Server Restart Engineer的我,也想了解如何實踐可觀測性工程30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言