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

在 Kubernetes 中使用 eBPF

簡介

Kubernetes 提供高階 API 和一組元件,幾乎隱藏了系統層級發生的複雜且(對我們某些人來說)有趣的細節。應用程式開發人員不需要了解機器的 IP 表、cgroup、命名空間、seccomp,或者現在甚至不需要了解他們的應用程式在其上執行的容器執行階段。但在底層,Kubernetes 及其所依賴的技術(例如,容器執行階段)大量利用核心 Linux 功能。

本文重點介紹一個核心 Linux 功能,該功能越來越多地用於網路、安全和稽核,以及追蹤和監控工具。此功能稱為 擴展 Berkeley 封包過濾器 (eBPF)

注意: 在本文中,我們同時使用縮寫:eBPF 和 BPF。前者用於擴展的 BPF 功能,後者用於「經典」的 BPF 功能。

什麼是 BPF?

BPF 是一個駐留在 Linux 核心中的迷你 VM,它執行 BPF 程式。在執行之前,BPF 程式會透過 bpf() 系統調用載入,並驗證其安全性:檢查迴圈、程式碼大小等。BPF 程式會附加到核心物件,並在這些物件上發生事件時執行,例如,當網路介面發出封包時。

BPF 超能力

BPF 程式依定義是事件驅動的,這是一個非常強大的概念,並且在事件發生時在核心中執行程式碼。Netflix 的 Brendan Gregg 將 BPF 稱為 Linux 超能力

eBPF 中的「e」

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

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

  • Kprobes(以及使用者空間等效項 uprobes)
  • 追蹤點
  • 用於分類或操作的網路排程器或 qdisc (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 程式附加到追蹤套接字事件的 kprobe:開啟和關閉連線。BPF 程式 tcptracer-bpf 被編譯成 ELF 物件檔案,並使用 gobpf 載入。

(作為旁注,Weave Scope 還有一個使用 eBPF 的外掛程式:HTTP 統計。)

要了解更多關於其工作原理以及為何以這種方式完成的資訊,請閱讀 這篇廣泛的文章,該文章由 Kinvolk 團隊為 Weaveworks Blog 撰寫。您也可以觀看關於該主題的 最近的演講

使用 seccomp-bpf 限制系統調用

Linux 有超過 300 個系統調用(讀取、寫入、開啟、關閉等)可用於使用或誤用。大多數應用程式只需要一小部分系統調用即可正常運作。seccomp 是一種 Linux 安全機制,用於限制應用程式可以使用的系統調用集,從而限制潛在的誤用。

seccomp 的原始實作非常嚴格。一旦應用,如果應用程式嘗試執行超出讀取和寫入已開啟檔案的操作,seccomp 會傳送 SIGKILL 訊號。

seccomp-bpf 啟用了更複雜的過濾器和更廣泛的操作範圍。Seccomp-bpf,也稱為 seccomp 模式 2,允許以 BPF 程式的形式應用自訂過濾器。載入 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 在其他方面是一個進程的階層式分組。在 Kubernetes 中,此分組在容器層級找到。利用 cgroup-bpf 的一個想法是安裝 BPF 程式,以收集詳細的每個 Pod 和/或每個容器的網路統計資訊。

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

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

當然,還有其他有趣的可能,例如執行實際的封包過濾。但目前阻礙這一點的障礙是在 Docker 和 Kubernetes 中擁有 cgroup v2 支援(cgroup-bpf 需要)。

應用程式應用的 LSM

Linux 安全模組 (LSM) 為 Linux 核心中的安全原則實作通用框架。SELinuxAppArmor 是這些範例。這兩者都在系統全域範圍內實作規則,將配置安全原則的責任放在管理員身上。

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

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

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

第一個參數是「char *」,指向記憶體位址的指標,例如 0xab004718

透過 seccomp,BPF 程式只能存取系統調用的參數,但無法取消引用指標,因此無法根據檔案做出安全決策。seccomp 也使用經典 BPF,這表示它無法使用 eBPF 映射,這是與使用者空間介接的機制。此限制表示無法根據 eBPF 映射中的配置在 seccomp-bpf 中更改安全原則。

使用 Landlock 的 BPF 程式不會接收系統調用的參數,而是接收對核心物件的參考。在上面的範例中,這表示它將具有對檔案的參考,因此它不需要取消引用指標、考慮相對路徑或執行 chroot。

用例:基於 Kubernetes 的無伺服器框架中的 Landlock

在 Kubernetes 中,部署單元是 Pod。Pod 和容器是主要的隔離單元。但是,在無伺服器框架中,部署的主要單元是一個函數。理想情況下,部署單元等於隔離單元。這使像 KubelessOpenFaaS 這樣的無伺服器框架陷入困境:針對隔離單元還是部署進行優化?

為了實現最佳的隔離,每個函數調用都必須在其自己的容器中發生——但對隔離有利的並不總是對效能有利。相反地,如果我們在同一個容器中執行函數調用,我們會增加衝突的可能性。

透過使用 Landlock,我們可以將同一個容器中的函數調用彼此隔離,例如,使一個函數調用建立的臨時檔案對於下一個函數調用不可存取。Landlock 與基於 Kubernetes 的無伺服器框架等技術之間的整合將是進一步探索的成熟領域。

使用 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 的所有事情。