大規模微服務利器:eBPF - Kubernetes
hi, 大家好,微服務,雲原生近來大熱,在企業積極進行數字化轉型,全面提升效率的今天,幾乎無人否認雲原生代表着雲計算的 “下一個時代”,IT 大廠們都不約而同的將其視爲未來雲應用的發展方向,從網絡方向來看,eBPF 可能會成爲“爲雲而生” 下一代網絡技術,今天分享一篇經典 eBPF 文章,希望大家喜歡。
譯者序
本文翻譯自 2020 年 Daniel Borkmann 在 KubeCon 的一篇分享: eBPF and Kubernetes: Little Helper Minions for Scaling Microservices, 視頻見油管。翻譯已獲得 Daniel 授權。
Daniel 是 eBPF 兩位 maintainer 之一,目前在 eBPF commits 榜單上排名第一,也是 Cilium 的核心開發者之一。
本文內容的時間跨度有 8 年,覆蓋了 eBPF 發展的整個歷史,非常值得一讀。時間限制, Daniel 很多地方只是點到,沒有展開。譯文中加了一些延展閱讀,有需要的同學可以參考。
由於譯者水平有限,本文不免存在遺漏或錯誤之處。如有疑問,請查閱原文。
http://arthurchiao.art/blog/ebpf-and-k8s-zh
-
譯者序
-
1 eBPF 正在吞噬世界
-
1.1 Kubernetes 已經是雲操作系統
-
1.2 兩個清晰的容器技術趨勢
-
2 內核面臨的挑戰
-
2.1 複雜度性不斷增長,性能和可擴展性新需求
-
2.2 永遠保持後向兼容
-
2.3 Feature creeping normality
-
3 eBPF 降世:重新定義數據平面(datapth)
-
4 eBPF 長什麼樣,怎麼用?Cilium eBPF networking 案例研究
-
4.1 Cilium eBPF 流程
-
4.2 eBPF 特點
-
5 溫故:kube-proxy 包轉發路徑
-
6 知新:Cilium eBPF 包轉發路徑
-
7 eBPF 年鑑
-
不知疲倦的增長和 eBPF 的第三個方向:Linux security modules
-
bpftrace
-
BPF 專著《BPF Performance Tools》
-
Cilium 1.6 發佈
-
BPF live-patching
-
第一次 bpfconf:受邀請才能參加的 BPF 內核專家會議
-
BPF backend 合併到 GCC
-
Cilium 1.0 發佈
-
BTF(Byte Type Format)
-
Linux Plumbers 會議開闢 BPF/XDP 主題
-
新 socket 類型:AF_XDP
-
bpffilter
-
eBPF 成爲內核獨立子系統
-
kTLS & eBPF
-
bpftool & libbpf
-
BPF to BPF function calls
-
eBPF 開始大規模應用於生產環境
-
eBPF 添加了一個新 fast path:XDP
-
Cilium 項目發佈
-
eBPF 分成兩個方向:networking & tracing
-
eBPF backend 合併到 LLVM 3.7
-
支持將 eBPF attach 到 kprobes
-
通過 cls_bpf,tc 變得完全可編程
-
爲 tc 添加了一個 lockless ingress & egress hook 點
-
添加了很多 verifer 和 eBPF 輔助代碼(helper)
-
bcc 項目發佈
-
第一個 eBPF patch 合併到內核
-
Kubernetes 提交第一個 commit
-
前浪工具和子系統
-
eBPF 與前浪的區別
-
eBPF:第一個(巨型)patch
-
2013
-
2014
-
2015
-
2016
-
2017
-
2017 ~ 2018
-
2018
-
2018 ~ 2019
-
2019 ~ 2020
-
8 eBPF:過去 50 年操作系統最大的變革
-
9 eBPF 數字榜單(截至 2020.07)
-
10 業界趨勢
-
11 eBPF 革命:燃燒到 Kubernetes 社區
-
網絡吞吐
-
CPU 利用率
-
東西向流量
-
南北向流量
-
11.1 幹掉 kube-proxy/iptables
-
11.2 Cilium 的 Service load balancing 設計
-
11.3 XDP/eBPF vs kube-proxy 性能對比
-
12 eBPF 和 Kubernetes:未來展望
-
13 結束語
以下是譯文。
1 eBPF 正在吞噬世界
1.1 Kubernetes 已經是雲操作系統
Kubernetes 正在吞噬世界(eating the world)。越來越多的企業開始遷移到容器平臺上 ,而 Kubernetes 已經是公認的雲操作系統(Cloud OS)。從技術層面來說,
-
Linux 內核是一切的堅實基礎,例如,內核提供了 cgroup、namespace 等特性。
-
Kubernetes CNI 插件串聯起了關鍵路徑(critical path)上的組件。例如,從網絡的 視角看,包括,
-
廣義的 Pod 連通性:一個容器創建之後,CNI 插件會給它創建網絡設備,移動到容 器的網絡命名空間。
-
IPAM:CNI 向 IPAM 發送請求,爲容器分配 IP 地址,然後配置路由。
-
Kubernetes 的 Service 處理和負載均衡功能。
-
網絡策略的生效(network policy enforcement)。
-
監控和排障。
1.2 兩個清晰的容器技術趨勢
今天我們能清晰地看到兩個技術發展趨勢:
-
容器的部署密度越來越高(increasing Pod density)。
-
容器的生命週期越來越短(decreasing Pod lifespan)。甚至短到秒級或毫秒級。
大家有興趣的可以查閱相關調查:
-
CNCF’19 survey report
-
sysdig’19 container usage report
2 內核面臨的挑戰
從操作系統內核的角度看,我們面臨很多挑戰。
2.1 複雜度性不斷增長,性能和可擴展性新需求
內核,或者說通用內核(general kernel),必須在子系統複雜度不斷增長( increasing complexity of kernel subsystems)的前提下,滿足這些性能和可擴展性 需求(performance & scalability requirements)。
2.2 永遠保持後向兼容
Linus torvalds 的名言大家都知道:never break user space。
這對用戶來說是好事,但對內核開發者來說意味着:我們必須保證在引入新代碼時,五年前 甚至十幾年前的老代碼仍然能正常工作。
顯然,這會使原本就複雜的內核變得更加複雜,對於網絡來說,這意味着快速收發包路徑 (fast path)將受到影響。
2.3 Feature creeping normality
開發者和用戶不斷往內核加入新功能,導致內核非常複雜,現在已經沒有一個人能理解所有東西了。
Wikipedia 對 creeping normality 的定義:
Def. creeping normality: … is a process by which a major change can be accepted as normal and acceptable if it happens slowly through small, often unnoticeable, increments of change. The change could otherwise be regarded as objectionable if it took place in a single step or short period.
應用到這裏,意思就是:內核不允許一次性引入非常大的改動,只能將它們拆 分成數量衆多的小 patch,每次合併的 patch 保證系統後向兼容,並且對系統的影響非 常小。
來看 Linus torvalds 的原話:
Linus Torvalds on crazy new kernel features:
So I can work with crazy people, that’s not the problem. They just need to sell their crazy stuff to me using non-crazy arguments, and in small and well-defined pieces. When I ask for killer features, I want them to lull me into a safe and cozy world where the stuff they are pushing is actually useful to mainline people first.
In other words, every new crazy feature should be hidden in a nice solid “Trojan Horse” gift: something that looks obviously good at first sight.
Linus Torvalds,
https://lore.kernel.org/lkml/alpine.LFD.2.00.1001251002430.3574@localhost.localdomain/
3 eBPF 降世:重新定義數據平面(datapth)
這就是我們最開始想將 eBPF 合併到內核時遇到的問題:改動太大,功能太新(a crazy new kernel feature)。
但是,eBPF 帶來的好處也是無與倫比的。
首先,從長期看,eBPF 這項新功能會減少未來的 feature creeping normality。因爲用戶或開發者希望內核實現的功能,以後不需要再通過改內核的方式來實現了。只需要一段 eBPF 代碼,實時動態加載到內核就行了。
其次,因爲 eBPF,內核也不會再引入那些影響 fast path 的蹩腳甚至 hardcode 代碼 ,從而也避免了性能的下降。
第三,eBPF 還使得內核完全可編程,安全地可編程(fully and safely programmable ),用戶編寫的 eBPF 程序不會導致內核 crash。另外,eBPF 設計用來解決真實世界 中的線上問題,而且我們現在仍然在堅守這個初衷。
4 eBPF 長什麼樣,怎麼用?Cilium eBPF networking 案例研究
eBPF 程序長什麼樣?如下圖所示,和 C 語言差不多,一般由用戶空間 application 或 agent 來生成。
4.1 Cilium eBPF 流程
下面我們將看看 Cilium 是如何用 eBPF 實現容器網絡方案的。
如上圖所示,幾個步驟:
-
Cilium agent 生成 eBPF 程序。
-
用 LLVM 編譯 eBPF 程序,生成 eBPF 對象文件(object file,
*.o
)。 -
用 eBPF loader 將對象文件加載到 Linux 內核。
-
校驗器(verifier)對 eBPF 指令會進行合法性驗證,以確保程序是安全的,例如 ,無非法內存訪問、不會 crash 內核、不會有無限循環等。
-
對象文件被即時編譯(JIT)爲能直接在底層平臺(例如 x86)運行的 native code。
-
如果要在內核和用戶態之間共享狀態,BPF 程序可以使用 BPF map,這種一種共享存儲 ,BPF 側和用戶側都可以訪問。
-
BPF 程序就緒,等待事件觸發其執行。對於這個例子,就是有數據包到達網絡設備時,觸發 BPF 程序的執行。
-
BPF 程序對收到的包進行處理,例如 mangle。最後返回一個裁決(verdict)結果。
-
根據裁決結果,如果是 DROP,這個包將被丟棄;如果是 PASS,包會被送到更網絡棧的 更上層繼續處理;如果是重定向,就發送給其他設備。
4.2 eBPF 特點
-
最重要的一點:不能 crash 內核。
-
執行起來,與內核模塊(kernel module)一樣快。
-
提供穩定的 API。
這意味着什麼?簡單來說,如果一段 BPF 程序能在老內核上執行,那它一定也能繼續在新 內核上執行,而無需做任何修改。
這就像是內核空間與用戶空間的契約,內核保證對用戶空間應用的兼容性,類似地,內核也 會保證 eBPF 程序的兼容性。
5 溫故:kube-proxy 包轉發路徑
從網絡角度看,使用傳統的 kube-proxy 處理 Kubernetes Service 時,包在內核中的 轉發路徑是怎樣的?如下圖所示:
步驟:
-
網卡收到一個包(通過 DMA 放到 ring-buffer)。
-
包經過 XDP hook 點。
-
內核給包分配內存,此時纔有了大家熟悉的
skb
(包的內核結構體表示),然後 送到內核協議棧。 -
包經過 GRO 處理,對分片包進行重組。
-
包進入 tc(traffic control)的 ingress hook。接下來,所有橙色的框都是 Netfilter 處理點。
-
Netfilter:在
PREROUTING
hook 點處理raw
table 裏的 iptables 規則。 -
包經過內核的連接跟蹤(conntrack)模塊。
-
Netfilter:在
PREROUTING
hook 點處理mangle
table 的 iptables 規則。 -
Netfilter:在
PREROUTING
hook 點處理nat
table 的 iptables 規則。 -
進行路由判斷(FIB:Forwarding Information Base,路由條目的內核表示,譯者注) 。接下來又是四個 Netfilter 處理點。
-
Netfilter:在
FORWARD
hook 點處理mangle
table 裏的 iptables 規則。 -
Netfilter:在
FORWARD
hook 點處理filter
table 裏的 iptables 規則。 -
Netfilter:在
POSTROUTING
hook 點處理mangle
table 裏的 iptables 規則。 -
Netfilter:在
POSTROUTING
hook 點處理nat
table 裏的 iptables 規則。 -
包到達 TC egress hook 點,會進行出方向(egress)的判斷,例如判斷這個包是到本 地設備,還是到主機外。
-
對大包進行分片。根據 step 15 判斷的結果,這個包接下來可能會:
-
發送到一個本機 veth 設備,或者一個本機 service endpoint,
-
或者,如果目的 IP 是主機外,就通過網卡發出去。
相關閱讀,有助於理解以上過程:
Cracking Kubernetes Node Proxy (aka kube-proxy)
(譯) 深入理解 iptables 和 netfilter 架構
連接跟蹤(conntrack):原理、應用及 Linux 內核實現
(譯) 深入理解 Cilium 的 eBPF 收發包路徑(datapath)
譯者注。
6 知新:Cilium eBPF 包轉發路徑
作爲對比,再來看下 Cilium eBPF 中的包轉發路徑:
建議和 (譯) 深入理解 Cilium 的 eBPF 收發包路徑(datapath) 對照看。
譯者注。
對比可以看出,Cilium eBPF datapath 做了短路處理:從 tc ingress 直接 shortcut 到 tc egress,節省了 9 箇中間步驟(總共 17 個)。更重要的是:這個 datapath 繞過了 整個 Netfilter 框架(橘黃色的框們),Netfilter 在大流量情況下性能是很差的。
去掉那些不用的框之後,Cilium eBPF datapath 長這樣:
Cilium/eBPF 還能走的更遠。例如,如果包的目的端是另一臺主機上的 service endpoint,那你可以直接在 XDP 框中完成包的重定向(收包 1->2
,在步驟 2
中對包 進行修改,再通過 2->1
發送出去),將其發送出去,如下圖所示:
可以看到,這種情況下包都沒有進入內核協議棧(準確地說,都沒有創建 skb)就被轉 發出去了,性能可想而知。
XDP 是 eXpress DataPath 的縮寫,支持在網卡驅動中運行 eBPF 代碼,而無需將包送 到複雜的協議棧進行處理,因此處理代價很小,速度極快。
7 eBPF 年鑑
eBPF 是如何誕生的呢?我最初開始講起。這裏 “最初” 我指的是 2013 年之前。
2013
前浪工具和子系統
回顧一下當時的 “SDN” 藍圖。
-
當時有 OpenvSwitch(OVS)、
tc
(Traffic control),以及內核中的 Netfilter 子系 統(包括iptables
、ipvs
、nftalbes
工具),可以用這些工具對 datapath 進行 “編程”:。 -
BPF 當時用於
tcpdump
,在內核中儘量前面的位置抓包,它不會 crash 內核;此 外,它還用於 seccomp,對系統調用進行過濾(system call filtering),但當時 使用的非常受限,遠不是今天我們已經在用的樣子。 -
此外就是前面提到的 feature creeping 問題,以及 tc 和 netfilter 的代碼重複問題,因爲這兩個子系統是競爭關係。
-
OVS 當時被認爲是內核中最先進的數據平面,但它最大的問題是:與內核中其他網 絡模塊的集成不好【譯者注 1】。此外,很多核心的內核開發者也比較牴觸 OVS,覺得它很怪。
【譯者注 1】
例如,OVS 的 internal port、patch port 用 tcpdump 都是 抓不到包的,排障非常不方便。
eBPF 與前浪的區別
對比 eBPF 和這些已經存在很多年的工具:
- tc、OVS、netfilter 可以對 datapath 進行 “編程”:但前提是 datapath 知道你想做什 麼(but only if the datapath knows what you want to do)。
- 只能利用這些工具或模塊提供的既有功能。
- eBPF 能夠讓你創建新的 datapath(eBPF lets you create the datapath instead)。
eBPF 就是內核本身的代碼,想象空間無限,並且熱加載到內核;換句話說,一旦加 載到內核,內核的行爲就變了。
在 eBPF 之前,改變內核行爲這件事情,只能通過修改內核再重新編譯,或者開發內 核模塊才能實現。
譯者注
eBPF:第一個(巨型)patch
-
描述 eBPF 的 RFC 引起了廣泛討論,但普遍認爲侵入性太強了(改動太大)。
-
另外,當時 nftables (inspired by BPF) 正在上升期,它是一個與 eBPF 有點類似的 BPF 解釋器,大家不想同時維護兩個解釋器。
最終這個 patch 被拒絕了。
被拒的另外一個原因是前面提到的,沒有遵循 “大改動小提交” 原則,全部代碼放到了一個 patch。Linus 會瘋的。
2014
第一個 eBPF patch 合併到內核
-
用一個擴展(extended)指令集逐步、全面替換原來老的 BPF 解釋器。
-
自動新老 BPF 轉換:in-kernel translation。
-
後續 patch 將 eBPF 暴露給 UAPI,並添加了 verifier 代碼和 JIT 代碼。
-
更多後續 patch,從核心代碼中移除老的 BPF。
我們也從那時開始,順理成章地成爲了 eBPF 的 maintainer。
Kubernetes 提交第一個 commit
巧合的是,對後來影響深遠的 Kubernetes,也在這一年提交了第一個 commit:
2015
eBPF 分成兩個方向:networking & tracing
到了 2015 年,eBPF 開發分成了兩個方向:
-
networking
-
tracing
eBPF backend 合併到 LLVM 3.7
這一年的一個重要里程碑是 eBPF backend 合併到了 upstream LLVM 編譯器套件,因此你 現在才能用 clang 編譯 eBPF 代碼。
支持將 eBPF attach 到 kprobes
這是 tracing 的第一個使用案例。
Alexei 主要負責 tracing 部分,他添加了一個 patch,支持加載 eBPF 用來做 tracing, 能獲取系統的觀測數據。
通過 cls_bpf,tc 變得完全可編程
我主要負責 networking 部分,使 tc 子系統可編程,這樣我們就能用 eBPF 來靈活的對 datapath 進行編程,獲得一個高性能 datapath。
爲 tc 添加了一個 lockless ingress & egress hook 點
譯註:可參考:
深入理解 tc ebpf 的 direct-action (da) 模式(2020)
爲容器時代設計的高級 eBPF 內核特性(FOSDEM, 2021)
添加了很多 verifer 和 eBPF 輔助代碼(helper)
使用更方便。
bcc 項目發佈
作爲 tracing frontend for eBPF。
2016
eBPF 添加了一個新 fast path:XDP
-
XDP 合併到內核,支持在驅動的 ingress 層 attach BPF 程序。
-
nfp 最爲第一家網卡及驅動,支持將 eBPF 程序 offload 到 cls_bpf & XDP hook 點。
Cilium 項目發佈
Cilium 最開始的目標是 docker 網絡解決方案。
-
通過 eBPF 實現高效的 label-based policy、NAT64、tunnel mesh、容器連通性。
-
整個 datapath & forwarding 邏輯全用 eBPF 實現,不再需要 Docker 或 OVS 橋接設備。
2017
eBPF 開始大規模應用於生產環境
2016 ~ 2017 年,eBPF 開始應用於生產環境:
-
Netflix on eBPF for tracing: ‘Linux BPF superpowers’
-
Facebook 公佈了生產環境 XDP+eBPF 使用案例(DDoS & LB)
-
用 XDP/eBPF 重寫了原來基於 IPVS 的 L4LB,性能
10x
。 -
eBPF 經受住了嚴苛的考驗:從 2017 開始,每個進入 facebook.com 的包,都是經過了 XDP & eBPF 處理的。
- Cloudflare 將 XDP+BPF 集成到了它們的 DDoS mitigation 產品。
-
成功將其組件從基於 Netfilter 遷移到基於 eBPF。
-
到 2018 年,它們的 XDP L4LB 完全接管生產環境。
-
擴展閱讀:(譯) Cloudflare 邊緣網絡架構:無處不在的 BPF(2019)
譯者注:基於 XDP/eBPF 的 L4LB 原理都是類似的,簡單來說,
通過 BGP 宣告 VIP
通過 ECMP 做物理鏈路高可用
通過 XDP/eBPF 代碼做重定向,將請求轉發到後端(VIP -> Backend)
對此感興趣可參考入門級介紹:L4LB for Kubernetes: Theory and Practice with Cilium+BGP+ECMP
2017 ~ 2018
eBPF 成爲內核獨立子系統
隨着 eBPF 社區的發展,feature 和 patch 越來越多,爲了管理這些 patch,Alexei、我和 networking 的一位 maintainer David Miller 經過討論,決定將 eBPF 作爲獨立的內核子 系統。
-
eBPF patch 合併到
bpf
&bpf-next
kernel trees on git.kernel.org -
拆分 eBPF 郵件列表:
bpf@vger.kernel.org
(archive at:lore.kernel.org/bpf/
) -
eBPF PR 經內核網絡部分的 maintainer David S. Miller 提交給 Linus Torvalds
kTLS & eBPF
kTLS & eBPF for introspection and ability for in-kernel TLS policy enforcement
kTLS 是將 TLS 處理 offload 到內核,例如,將加解密過程從 openssl 下放到內核進 行,以使得內核具備更強的可觀測性(gain visibility)。
有了 kTLS,就可以用 eBPF 查看數據和狀態,在內核應用安全策略。 目前 openssl 已經完全原生支持這個功能。
bpftool & libbpf
爲了檢查內核內 eBPF 的狀態(introspection)、查看內核加載了哪些 BPF 程序等, 我們添加了一個新工具 bpftool。現在這個工具已經功能非常強大了。
同樣,爲了方便用戶空間應用使用 eBPF,我們提供了用戶空間 API(user space API for applications) libbpf
。這是一個 C 庫,接管了所有加載工作,這樣用戶就不需要 自己處理複雜的加載過程了。
BPF to BPF function calls
增加了一個 BPF 函數調用另一個 BPF 函數的支持,使得 BPF 程序的編寫更加靈活。
2018
Cilium 1.0 發佈
這標誌着 BPF 革命之火燃燒到了 Kubernetes networking & security 領域。
Cilium 此時支持的功能:
-
K8s CNI
-
Identity-based L3-L7 policy
-
ClusterIP Services
BTF(Byte Type Format)
內核添加了一個稱爲 BTF 的組件。這是一種元數據格式,和 DWARF 這樣的 debugging data 類似。但 BTF 的 size 要小的多,而更重要的是,有史以來,內核第一次變得可自 描述了(self-descriptive)。什麼意思?
想象一下當前正在運行中的內核,它內置了自己的數據格式(its own data format) 和內部數據結構(internal structures),你能用工具來查看這些東西(you can introspect them)。還是不太懂?這麼說吧,BTF 是後來的 “一次編譯、到處運行”、 熱補丁(live-patching)、BPF global data 處理等等所有這些 BPF 特性的基礎。
新的特性不斷加入,它們都依賴 BTF 提供富元數據(rich metadata)這個基礎。
更多 BTF 內容,可參考 (譯) Cilium:BPF 和 XDP 參考指南(2019)
譯者注
Linux Plumbers 會議開闢 BPF/XDP 主題
這一年,Linux Plumbers 會議第一次開闢了專門討論 BPF/XDP 的微型分會,我們 一起組織這場會議。其中,Networking Track 一半以上的議題都涉及 BPF 和 XDP 主題,因爲這是一個非常振奮人心的特性,越來越多的人用它來解決實際問題。
新 socket 類型:AF_XDP
內核添加了一個新 socket 類型 AF_XDP
。它提供的能力是:在零拷貝( zero-copy)的前提下將包從網卡驅動送到用戶空間。
回憶前面的內容,數據包到達網卡後,先經過 XDP,然後才爲這個包分配內存。因此在 XDP 層直接將包送到用戶態是無需拷貝的。
譯者注
AF_XDP
提供的能力與 DPDK 有點類似,不過
-
DPDK 需要重寫網卡驅動,需要額外維護用戶空間的驅動代碼。
-
AF_XDP
在複用內核網卡驅動的情況下,能達到與 DPDK 一樣的性能。
而且由於複用了內核基礎設施,所有的網絡管理工具還都是可以用的,因此非常方便, 而 DPDK 這種 bypass 內核的方案導致絕大大部分現有工具都用不了了。
由於所有這些操作都是發生在 XDP 層的,因此它稱爲 AF_XDP
。插入到這裏的 BPF 代碼 能直接將包送到 socket。
bpffilter
開始了 bpffilter prototype,作用是通過用戶空間驅動(userspace driver),將 iptables 規則轉換成 eBPF 代碼。
這是將 iptables 轉換成 eBPF 的第一次嘗試,整個過程對用戶都是無感知的,其中的某些 組件現在還在用,用於在其他方面擴展內核的功能。
2018 ~ 2019
bpftrace
Brendan 發佈了 bpftrace 工具,作爲 DTrace 2.0 for Linux。
BPF 專著《BPF Performance Tools》
Berendan 寫了一本 800 多頁的 BPF 書。
Cilium 1.6 發佈
第一次支持完全乾掉基於 iptables 的 kube-proxy,全部功能基於 eBPF。
這個版本其實是有問題的,例如 1.6 發佈之後我們發現 externalIPs 的實現是有問題 ,社區在後面的版本修復了這個問題。在修復之前,還是得用 kube-proxy:https://github.com/cilium/cilium/issues/9285
譯者注
BPF live-patching
添加了一些內核新特性,例如尾調用(tail call),這使得 eBPF 核心基礎 設施第一次實現了熱加載。這個功能幫我們極大地優化了 datapath。
另一個重要功能是 BPF trampolines,這裏就不展開了,感興趣的可以搜索相關資料,我只 能說這是另一個振奮人心的技術。
第一次 bpfconf:受邀請才能參加的 BPF 內核專家會議
如題,這是 BPF 內核專家交換想法和討論問題的會議。與 Linux Plumbers 會議互補。
BPF backend 合併到 GCC
前面提到,BPF backend 很早就合併到 LLVM/Clang,現在,它終於合併到 GCC 了。至此,GCC 和 LLVM 這兩個最主要的編譯器套件都支持了 BPF backend。
此外,BPF 開始支持有限循環(bounded loops),在此之前,是不支持循環的,以防止程 序無限執行。
2019 ~ 2020
不知疲倦的增長和 eBPF 的第三個方向:Linux security modules
-
Google 貢獻了 BPF LSM(安全),部署在了他們的數據中心服務器上。
-
BPF verifier 防護 Spectre 漏洞(2018 年轟動世界的 CPU bug):even verifying safety on speculative program paths。
-
主流雲廠商開始通過 SRIOV 支持 XDP:AWS (ena driver), Azure (hv_netvsc driver), …
-
Cilium 1.8 支持基於 XDP 的 Service 負載均衡和 host network policies。
-
Facebook 開發了基於 BPF 的 TCP 擁塞控制模塊。
-
Microsoft 基於 BPF 重寫了將他們的 Windows monitoring 工具。
8 eBPF:過去 50 年操作系統最大的變革
Brendan 說在他的職業生涯中,eBPF 是他見過的操作系統中最大的變革之一(one of the biggest operating system changes),他爲能身處其中而感到非常興奮。
我接下來只能用數字證明:Brendan 的興奮是沒錯的。
9 eBPF 數字榜單(截至 2020.07)
eBPF 內核社區截至 7 月份的一些數據:
-
347 個貢獻者,貢獻了 4,935 個 patch 到 BPF 子系統。
-
BPF 內核郵件列表日均 50 封郵件 (高峯經常超過日均 100)。
-
23,395 mails since mailing list git archive was added in Feb 2019
-
每天合併 4 個新 patch。patch 接受率 30% 左右。
-
30 個程序(different program),27 種 BPF map 類型,141 個 BPF helpers,超過 3,500 個測試。
-
2 個 BPF kernel maintainers & team of 6 senior core reviewers。
-
主要貢獻來自:Isovalent(Cilium), Facebook and Google
毫無疑問,這是內核裏發展最快的子系統!
10 業界趨勢
注意貢獻榜排名第一的就是演講者本人,譯者注。
列舉幾個生產環境大規模使用 BPF 的大廠:
-
Facebook:L4LB、DDoS、tracing。
-
Netflix:BPF 重度用戶,例如生產環境 tracing。
-
Google:Android、服務器安全以及其他很多方面。
-
Cloudflare:L4LB、DDoS。
-
Cilium
上圖中,右下角是前 Netfilter 維護者 Rusty Russel 說的一句,業界對 eBPF 的受認可程度可窺一斑。
11 eBPF 革命:燃燒到 Kubernetes 社區
eBPF 已無處不在,而你還在使用 iptables?
11.1 幹掉 kube-proxy/iptables
不用再忍受 iptables 複雜冗長的規則和差勁的性能了,以前沒得選,現在你可以做個好人:
$ kubectl -n kube-system delete ds kube-proxy
作爲例子,我們來看看 Cilium 是怎麼做 Service 的負載均衡的。
Service 細節實現可參考 Cracking Kubernetes Node Proxy (aka kube-proxy)。
譯者注。
11.2 Cilium 的 Service load balancing 設計
如上圖所示,主要涉及兩部分:
-
在 socket 層運行的 BPF 程序
-
在 XDP 和 tc 層運行的 BPF 程序
東西向流量
我們先來看 socker 層。
如上圖所示,
Socket 層的 BPF 程序主要處理 Cilium 節點的東西向流量(E-W)。
-
將 Service 的
IP:Port
映射到具體的 backend pods,並做負載均衡。 -
當應用發起 connect、sendmsg、recvmsg 等請求(系統調用)時,攔截這些請求, 並根據請求的
IP:Port
映射到後端 pod,直接發送過去。反向進行相反的變換。
這裏實現的好處:性能更高。
-
不需要包級別(packet leve)的地址轉換(NAT)。在系統調用時,還沒有創建包,因此性能更高。
-
省去了 kube-proxy 路徑中的很多中間節點(intermediate node hops)
可以看出,應用對這種攔截和重定向是無感知的(符合 k8s Service 的設計)。
南北向流量
再來看從 k8s 集羣外進入節點,或者從節點出 k8s 集羣的流量(external traffic), 即南北向流量(N-S):
區分集羣外流量的一個原因是:Pod IP 很多情況下都是不可路由的(與跨主機選用的網 絡方案有關),只在集羣內有效,即,集羣外訪問 Pod IP 是不通的。
因此,如果 Pod 流量直接從 node 出宿主機,必須確保它能正常回來。而 node IP 一般都是全局可達的,集羣外也可以訪問,所以常見的解決方案就是:在 Pod 通過 node 出集羣時,對其進行 SNAT,將源 IP 地址換成 node IP 地址;應答包回來時,再進行相 反的 DNAT,這樣包就能回到 Pod 了。
譯者注
如上圖所示,集羣外來的流量到達 node 時,由 XDP 和 tc 層的 BPF 程序進行處理, 它們做的事情與 socket 層的差不多,將 Service 的 IP:Port
映射到後端的 PodIP:Port
,如果 backend pod 不在本 node,就通過網絡再發出去。發出去的流程我們 在前面 Cilium eBPF 包轉發路徑
講過了。
這裏 BPF 做的事情:執行 DNAT。這個功能可以在 XDP 層做,也可以在 TC 層做,但 在 XDP 層代價更小,性能也更高。
總結起來,這裏的核心理念就是:
-
將東西向流量放在離 socket 層儘量近的地方做。
-
將南北向流量放在離驅動(XDP 和 tc)層儘量近的地方做。
11.3 XDP/eBPF vs kube-proxy 性能對比
網絡吞吐
測試環境:兩臺物理節點,一個發包,一個收包,收到的包做 Service loadbalancing 轉發給後端 Pods。
可以看出:
-
Cilium XDP eBPF 模式能處理接收到的全部 10Mpps(packets per second)。
-
Cilium tc eBPF 模式能處理 3.5Mpps。
-
kube-proxy iptables 只能處理 2.3Mpps,因爲它的 hook 點在收發包路徑上更後面的位置。
-
kube-proxy ipvs 模式這裏表現更差,它相比 iptables 的優勢要在 backend 數量很多的時候才能體現出來。
CPU 利用率
我們生成了 1Mpps、2Mpps 和 4Mpps 流量,空閒 CPU 佔比(可以被應用使用的 CPU)結果如下:
結論與上面吞吐類似。
-
XDP 性能最好,是因爲 XDP BPF 在驅動層執行,不需要將包 push 到內核協議棧。
-
kube-proxy 不管是 iptables 還是 ipvs 模式,都在處理軟中斷(softirq)上消耗了大量 CPU。
12 eBPF 和 Kubernetes:未來展望
“The Linux kernel continues its march towards becoming BPF runtime-powered microkernel.”
“Linux 內核繼續朝着成爲 BPF runtime-powered microkernel 而前進”。這是一個非 常有趣的思考角度。
-
設想在將來,Linux 只會保留一個非常小的核心內核(tiny core kernel),其他所有 內核功能都由用戶定義,並用 BPF 實現(而不再是開發內核模塊的方式)。
-
這樣可以減少受攻擊面,因爲此時的核心內核非常小;另外,所有 BPF 代碼都會經過 verifer 校驗。
-
極大減少 ‘static’ feature creep,資源(例如 CPU)可以用在更有意義的地方。
-
設想一下,未來 Kubernetes 可能會內置 custom BPF-tailored extensions,能根據用戶的應用自動適配(optimize needs for user workloads);例如,判斷 pod 是跑在數據中心,還是在嵌入式系統上。
BPF will replace Linux.
我們的目標是星辰大海,與此相比,kube-proxy replacement 只是最微不足道的開端。
13 結束語
-
Try it out:https://cilium.link/kubeproxy-free
-
Contribute:https://github.com/cilium/cilium
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/xfZ-X4elBBnsG2xnpAB9ow