本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

在 Kubernetes 中使用 eBPF

引言

Kubernetes 提供了一套高階 API 和元件,幾乎隱藏了所有在系統層面發生的複雜且對我們一些人來說很有趣的細節。應用程式開發人員無需瞭解機器的 IP 表、cgroups、namespaces、seccomp,或者現在甚至應用程式所執行的 容器執行時。但在底層,Kubernetes 及其所依賴的技術(例如容器執行時)大量利用了 Linux 的核心功能。

本文重點介紹 Linux 的一項核心功能,它越來越多地用於網路、安全和審計,以及跟蹤和監控工具。此功能稱為 擴充套件 Berkeley Packet Filter (eBPF)

注意: 在本文中,我們同時使用兩個縮寫詞:eBPF 和 BPF。前者用於擴充套件 BPF 功能,後者用於“經典”BPF 功能。

什麼是 BPF?

BPF 是一個駐留在 Linux 核心中的微型虛擬機器,用於執行 BPF 程式。在執行之前,BPF 程式透過 bpf() 系統呼叫載入,並進行安全性驗證:檢查迴圈、程式碼大小等。BPF 程式附加到核心物件,並在這些物件上發生事件時執行——例如,當網路介面發出資料包時。

BPF 的超能力

BPF 程式本質上是事件驅動的,這是一個極其強大的概念,它在事件發生時在核心中執行程式碼。Netflix 的 Brendan Gregg 將 BPF 稱為 Linux 超能力

eBPF 中的“e”

傳統上,BPF 只能附加到套接字進行套接字過濾。BPF 的第一個用例是在 tcpdump 中。當你執行 tcpdump 時,過濾器被編譯成一個 BPF 程式,並附加到一個原始的 AF_PACKET 套接字上,以便打印出過濾後的資料包。

但多年來,eBPF 增加了附加到其他核心物件的能力。除了套接字過濾,一些支援的附加點有:

  • Kprobes(和使用者空間等效的 uprobes)
  • Tracepoints
  • 用於分類或操作的網路排程器或 qdiscs (tc)
  • XDP (eXpress Data Path) 這些以及其他更新的功能,如核心輔助函式和可用於與使用者空間通訊的共享資料結構(對映),擴充套件了 BPF 的功能。

eBPF 在 Kubernetes 中的現有用例

一些開源的 Kubernetes 工具已經使用了 eBPF,許多用例值得更仔細地研究,特別是在網路、監控和安全工具等領域。

使用 Cilium 進行動態網路控制和可見性

Cilium 是一個網路專案,它大量使用 eBPF 超能力來路由和過濾基於容器的系統的網路流量。透過使用 eBPF,Cilium 可以動態生成和應用規則——甚至可以在裝置級別使用 XDP——而無需更改 Linux 核心本身。

Cilium Agent 在每個主機上執行。它不管理 IP 表,而是將網路策略定義轉換為 BPF 程式,這些程式載入到核心中並附加到容器的虛擬乙太網裝置。這些程式在每個傳送或接收的資料包上執行——規則被應用。

此圖顯示了 Cilium 專案的工作原理

根據所應用的網路規則,BPF 程式可以使用 tcXDP 附加。透過使用 XDP,Cilium 可以在儘可能最低的點附加 BPF 程式,這也是網路軟體堆疊中效能最佳的點。

如果您想了解 Cilium 如何使用 eBPF,請檢視該專案的 BPF 和 XDP 參考指南

在 Weave Scope 中跟蹤 TCP 連線

Weave Scope 是一個用於監控、視覺化和與基於容器的系統互動的工具。就本文而言,我們將重點關注 Weave Scope 如何獲取 TCP 連線。

Weave Scope 採用一個在叢集的每個節點上執行的代理。該代理監控系統,生成報告並將其傳送到應用程式伺服器。應用程式伺服器編譯接收到的報告,並在 Weave Scope UI 中呈現結果。

為了準確地繪製容器之間的連線,代理將一個 BPF 程式附加到跟蹤套接字事件(連線的開啟和關閉)的 kprobes。BPF 程式 tcptracer-bpf 被編譯成 ELF 物件檔案並使用 gobpf 載入。

(順便提一下,Weave Scope 還有一個利用 eBPF 的外掛:HTTP 統計資訊。)

要了解更多關於其工作原理以及為何如此做的資訊,請閱讀 Kinvolk 團隊為 Weaveworks 部落格撰寫的 這篇詳盡的文章。您還可以觀看關於該主題的 最新演講

使用 seccomp-bpf 限制系統呼叫

Linux 提供了超過 300 個可用的系統呼叫(read、write、open、close 等)——或濫用。大多數應用程式只需要一小部分系統呼叫即可正常執行。seccomp 是一種 Linux 安全機制,用於限制應用程式可以使用的系統呼叫集,從而限制潛在的濫用。

seccomp 的原始實現限制性很強。一旦應用,如果應用程式試圖做任何超出讀寫已開啟檔案的操作,seccomp 就會發送一個 SIGKILL 訊號。

seccomp-bpf 支援更復雜的過濾器和更廣泛的操作。seccomp-bpf,也稱為 seccomp 模式 2,允許以 BPF 程式的ATOR形式應用自定義過濾器。當 BPF 程式載入時,過濾器將應用於每個系統呼叫,並採取適當的操作(允許、終止、捕獲等)。

seccomp-bpf 在 Kubernetes 工具中廣泛使用,並在 Kubernetes 本身中暴露。例如,seccomp-bpf 在 Docker 中用於應用自定義的 seccomp 安全配置檔案,在 rkt 中用於應用 seccomp 隔離器,以及在 Kubernetes 本身的 安全上下文 中。

但在所有這些情況下,BPF 的使用都隱藏在 libseccomp 後面。在幕後,libseccomp 根據提供給它的規則生成 BPF 程式碼。一旦生成,BPF 程式就會載入並應用規則。

eBPF 在 Kubernetes 中的潛在用例

eBPF 是一種相對較新的 Linux 技術。因此,仍有許多用途尚未探索。eBPF 本身也在不斷發展:eBPF 中正在新增新功能,這將帶來目前不可能實現的新用例。在以下部分中,我們將探討其中一些最近才成為可能的功能以及即將出現的功能。我們希望這些功能能夠被開源工具所利用。

Pod 和容器級別的網路統計資訊

BPF 套接字過濾並非新鮮事,但基於 cgroup 的 BPF 套接字過濾是。Linux 4.10 中引入的 cgroup-bpf 允許將 eBPF 程式附加到 cgroup。一旦附加,程式將對進入或退出 cgroup 中任何程序的所有資料包執行。

cgroup 是一個程序的分層分組。cgroup 在 Kubernetes 中,這種分組存在於容器級別。使用 cgroup-bpf 的一個想法是安裝 BPF 程式,以收集每個 Pod 和/或每個容器的詳細網路統計資訊。

通常,這些統計資訊是透過定期檢查 Linux /sys 目錄中的相關檔案或使用 Netlink 來收集的。透過使用附加到 cgroup 的 BPF 程式,我們可以獲得更詳細的統計資訊:例如,TCP 埠 443 上的資料包/位元組數,或者來自 IP 10.2.3.4 的資料包/位元組數。一般來說,由於 BPF 程式具有核心上下文,它們可以安全有效地向用戶空間提供更詳細的資訊。

為了探索這個想法,Kinvolk 團隊實現了一個概念驗證專案:https://github.com/kinvolk/cgnet。該專案將一個 BPF 程式附加到每個 cgroup,並將資訊匯出到 Prometheus

當然,還有其他有趣的可能,比如進行實際的資料包過濾。但目前阻礙這一點的是 Docker 和 Kubernetes 中對 cgroup v2 的支援——cgroup-bpf 所必需的——在 Docker 和 Kubernetes 中。

應用程式應用的 LSM

Linux 安全模組 (LSM) 為 Linux 核心中的安全策略實現了一個通用框架。SELinuxAppArmor 是其中的例子。這兩者都在系統全域性範圍內實現規則,將配置安全策略的責任放在管理員身上。

Landlock 是另一個正在開發中的 LSM,它將與 SELinux 和 AppArmor 共存。一個初始的補丁集已經提交給 Linux 核心,目前處於早期開發階段。與其他 LSM 的主要區別在於,Landlock 旨在允許非特權應用程式構建自己的沙箱,從而有效地限制自身而不是使用全域性配置。透過 Landlock,應用程式可以載入一個 BPF 程式,並在程序執行特定操作時執行它。例如,當應用程式使用 open() 系統呼叫開啟檔案時,核心將執行 BPF 程式,並且根據 BPF 程式的返回值,該操作將被接受或拒絕。

在某些方面,它與 seccomp-bpf 類似:seccomp-bpf 使用 BPF 程式允許非特權程序限制它們可以執行的系統呼叫。Landlock 將更強大並提供更大的靈活性。考慮以下系統呼叫:

C  
fd = open(“myfile.txt”, O\_RDWR);

第一個引數是“char *”,一個指向記憶體地址的指標,例如 0xab004718

使用 seccomp,BPF 程式只能訪問系統呼叫的引數,但不能解引用指標,因此無法根據檔案做出安全決策。seccomp 也使用經典的 BPF,這意味著它不能使用 eBPF 對映(用於與使用者空間互動的機制)。此限制意味著安全策略不能根據 eBPF 對映中的配置在 seccomp-bpf 中更改。

Landlock 中的 BPF 程式不接收系統呼叫的引數,而是接收對核心物件的引用。在上面的示例中,這意味著它將擁有對檔案的引用,因此無需解引用指標、考慮相對路徑或執行 chroot。

用例:Kubernetes-based 無伺服器框架中的 Landlock

在 Kubernetes 中,部署的單位是 Pod。Pod 和容器是隔離的主要單位。然而,在無伺服器框架中,部署的主要單位是函式。理想情況下,部署的單位等於隔離的單位。這使得像 KubelessOpenFaaS 這樣的無伺服器框架陷入困境:是最佳化隔離單位還是部署單位?

為了實現最佳隔離,每個函式呼叫都必須發生在自己的容器中——但有利於隔離的東西並不總是對效能有利。反之,如果我們在同一個容器中執行函式呼叫,我們就會增加衝突的可能性。

透過使用 Landlock,我們可以將同一容器內的函式呼叫彼此隔離,例如,使得一個函式呼叫建立的臨時檔案對下一個函式呼叫不可訪問。Landlock 與 Kubernetes-based 無伺服器框架等技術的整合將是進一步探索的肥沃領域。

使用 eBPF 審計 kubectl-exec

在 Kubernetes 1.7 中,審計提案開始被採納。目前處於預穩定狀態,計劃在 1.10 版本中達到穩定。顧名思義,它允許管理員記錄和審計 Kubernetes 叢集中發生的事件。

雖然這些事件記錄 Kubernetes 事件,但它們目前不提供某些人可能需要的可見性級別。例如,雖然我們可以看到有人使用 kubectl exec 進入容器,但我們無法看到在該會話中執行了哪些命令。使用 eBPF,可以附加一個 BPF 程式,該程式將記錄在 kubectl exec 會話中執行的任何命令,並將這些命令傳遞給一個記錄這些事件的使用者空間程式。然後,我們可以重放該會話,並知道發生的精確事件序列。

瞭解更多 eBPF

如果您有興趣瞭解更多關於 eBPF 的資訊,這裡有一些資源:

結論

我們才剛剛開始看到 eBPF 的 Linux 超能力被應用於 Kubernetes 工具和技術中。毫無疑問,eBPF 的使用將不斷增加。我們在此強調的只是您未來可能遇到的一小部分。真正令人興奮的是,我們將看到這些技術如何以我們尚未想到的方式被使用。敬請期待!

Kinvolk 團隊將在奧斯汀 KubeCon 的 Kinvolk 展位恭候。歡迎前來與我們討論所有關於 Kubernetes、Linux、容器執行時以及 eBPF 的事情。