epbf part1 過去,現在與未來
這是bpf系列的第一篇文章,每個都將基于先前的概念與關系去介紹例子與實現。
第一篇文章將會探索bpf的歷史,目前狀態,以及未來軌跡。這樣做的目的是以便于使得bpf跟當前的
狀態與功能更相符,就像許多的軟件工程一樣,如果沒有背景,看起來可能會很奇怪。
直接正文:
擴展的BPF,簡短說ebpf,很難完整的解釋與理解。很大程度上是因為它與ebpf先前的用途有很大的不同,
然而,可以說,最大的問題在于它的名字,當有人第一次學習bpf時并不會知道bpf是什麼以及什么又是擴展(extend)
,伯克利也不利于區分出美國加利福利亞的伯克利,唯一有意義的單詞是“封包過濾”,這也不會表明bpf可以用來做的不僅僅是網絡過濾,
不過,社區在創造ebpf時也沒預料到會傳播多遠。
在ebpf的核心,是位于內核的高效率虛擬機,原本是用于高效的網絡幀過濾,
使這個虛擬機,從而使bpf成為處理內核事件的理想引擎。基于這一事實,目前有十二種不同類型的bpf程序。
在寫這篇文章時,他們當中的許多并不是跟網絡有關。
這篇文章,將會過一遍bpf的歷史(以及繼承者ebpf),ebpf的當前狀態,以及將來的用途,
這篇文章不需要讀者具備bpf知識,只要中等的linux熟悉度,便可以對于ebpf從概念到使用以及如何演進獲得一個堅實的理解。
術語:
bpf:伯克利封包過濾,freeBSD創建。
ebpf:擴展的伯克利封包過濾,linux創建。
ebpf-map:通用術語用來描述各種不同類型的bpf數據存儲。bpf文檔描述所有的ebpf數據存儲類型為maps即使有個種類型,其中一些并不是map。
因此,本文使用術語ebpf-map來明確指定ebpf的數據存儲類型。同時不會使其與epbf自己的文檔完全脫節。
network-Frame(網絡幀):osi模型中鏈路層的數據單元,例如Ethernete。
network-Frame(網絡幀):osi模型中鏈路層的數據單元,例如Ethernete。
network-package:osi網絡層,例如ipv4.
network-Segment: osi中的傳輸層,例如tcp。
bpf與freeBSD:過去
想要在linux理解bpf最好是從一個不同的操作系統開始,1993年的論文“BSD封包過濾-一種新的架構用于捕獲用戶空間封包, McCanne & Jacobson”
在1993年美國加利福尼亞圣地亞哥舉行的冬季usenix會議上發表,由Steven McCanne 與 Van Jacobson共同編寫,
那時候,兩位都效力于勞倫斯伯克利國家實驗室。在論文中,McCanne and Jacobson描述了BSD封包過濾,或者叫BPF,包括他在內核中的位置,以及作為虛擬機的實現。
是什么讓bpf跟他的前輩們如此不同呢,比如CMU/stanford packet filter(cspf)
是使用經過考量的內存模型,然后通過內核中高效的虛擬機將其公開。通過這樣,bpf過濾器可以做高效流量過濾同時在過濾用的代碼與內核之間保持一個邊界。
在他們的文章中,McCanne and Jacobson提供了如下的圖標去幫助理解,主要的是,BPF在過濾器與用戶空間采用了一個緩沖區去避免了匹配到的包在用戶空間與內核空間之間
昂貴的上下文切換。
McCanne and Jacobson做的最主要的工作通過以下5點從新定義并設計了虛擬機:
1:必須是協議獨立的,當添加新協議時,內核不應該被修改。
2:必須是通用的,指令集應當足夠豐富以便于處理沒有預料的使用。
3:盡量減少數據包的數據引用。
4:解碼語句應當由單個C switch語句完成。
5:抽象的寄存器應當保留在物理內存中。
這強調了可擴展性,通用性,對于性能的追求,可能是ebpf超過其他bpf實現的原因。目前bpf的虛擬機仍然簡單明了,它包含了一個累加器,
索引寄存器,一個暫存器,以及一個隱式程序計數器。這可以從以下的例子中看出來,在寄存器代碼中呈現,如下是匹配所有的ip包,右邊有每行代碼的解釋。
ldh [12] //將ethertype字段加載到寄存器當中。
jeq #ip包, L1,L2 //將寄存器ip與ethertype ip進行比較。
L1:ret #TRUE //如果進行比較為true,則返回true。
L2:ret #0 //如果比較是false,則返回0.
僅采用了4個虛擬機指令,BPF就實現了一個非常有用的ip包過濾,為了指明這種設計的效率是多么高,最初的bpf拒絕一個封包實現需要依靠184個
cpu指令,這是從bpf_tap調用開始到結束的指令指令計數的,這意味著它既測量了BPF實現又測量了BPF filter。bpf使處理器能夠與新圖形計算器
中的處理器相媲美,通過安全的內核虛擬機,能夠處理理論上最大2.6Gbps的網絡流量。可以通過與上下文切換的性能來進行對比,在 SPARCstation 2 上,
McCanne and Jacobson進行了測量,the hardware used by McCanne and Jacobson to do these measurements, doing a context switch could take anywhere between 2,840 instructions and 16,000+ instructions (equivalent 71 microseconds and 400+ microseconds),
取決于資源對于競爭進程的數量需要交換的上下文。
最終,有兩件事情在McCanne and Jacobson的parper上需要注意,在論文發表時,bpf已經誕生有兩年了,這值得注意是因為這表明bpf發展是漸進的,
隨著技術成功的發展。第二是,當中有提到,tcpdump是bpf程序中最為廣泛采用的。這意味著,被廣泛采用的tcpdump已經被使用了超過21年了,
我之所以提到這個是因為沒有其他文章談到bpf家族使用歷史。當McCanne and Jacobson發表文章時,alpha階段的實現bpf并不是一個好主意,它已經被廣泛的測試
使用并且演進為了其他的工具。
McCanne and Jacobson的文章只有11頁,感興趣的話值得一讀。
ebpf and linux: 現狀
在2014年發表的linux內核版本3.18其中包含了第一個BPF的實現,簡短說來,是一個內核虛機,就像bpf,但提供了關鍵的改進,
ebpf比bpf更快了,得益于jit對于ebpf代碼的編譯。第二點是,被設計為了處理內核通用的信息,這允許了內核開發人員將ebpf整合到內存中以用于
他們認為合適的用途。第三,包括了高效的全局數據存儲叫做ebpf map,這允許在事件之間被存儲,從而可以聚合用于包括統計和上下文感知用途。
值得一提的是,自從ebpf被創建以來,這里與許多的數據存儲類型,并不是所有的類型都是map,但本文也折中采用map來指代。
但隨著這些能力,內核開發者迅速的被各種各樣的方式使用到內核中,在一年半不到的時間內使用覆蓋了網絡監控,網絡包封裝,以及系統監控,因此ebpf程序
可以被用來切換網絡流量,測量硬盤讀取延遲,或者追蹤cpu緩存丟失。
理解bpf亮點最快的方法以及目前的實現是瀏覽一遍使用bpf需要什么,bpf的整個流程可以分為一下三個不同的階段。
1:創建bpf程序為字節碼,目前創建bpf程序的標準方式是先編寫c代碼,然后在一個ELF的文件里面生成ebpf字節碼,然而,ebpf虛擬機的設計
有非常完善的文檔以及代碼,許多工具都開源了,因此,對于起步來說,他完全可以手動編寫一個ebpf程序的寄存器代碼,或者是寫一個自定義的
編譯器。由于ebpf極其簡單的設計,程序的所有函數,除了入口點函數,都必須內聯以供llvm編譯。本系列將在未發布的文章中更詳細的介紹這點,與此同時,
如果想要尋求更多的有關信息,請查閱文末擴展閱讀。
2:把程序加載進內核并創建必要的ebpf-map。這在linux系統中是通過bpf系統調用完成的,這個調用允許程序伴隨bpf類型聲明被加載。在寫作這篇文章
時,ebpf程序有以下幾種用法,作為socket filter,kprobe handler,traffic controll scheduler,traffic control action,tracepoint handler,traffic
eXpress Data Path,performance monitor,cgroup restriction,以及light wight tunnel,調用同樣被用于初始化bpf-map,第二篇文章將會詳細介紹這些。
第三部分通過使用linux下工具來完成工作,主要是來自于iproute2的tc與ip,
3: 將被加載的程序連接到系統當中,不同的bpf程序可以被用到加載到不同的linux系統中,因此不同的系統也執行不同的加載過程。
當bpf程序被attach之后,它就會運行并開始filtering,analyzing,或者捕獲信息,這取決以它創建的用途,從這里開始,用戶空間的程序可以管理bpf程序,
包括從bpf-map中讀取狀態,以及如果是要操作ebpf來改變程序的狀態,對于更多信息,請查閱文末擴展閱讀。
上述3個步驟在概念上很簡潔但實際使用中非常微妙。同時,當前的許多工具確實為使用ebpf提供了很多的幫助。即使可能使用起來感覺相反,就像linux中許多事情一樣,
相比于在內核中使用ebpf程序明顯要好于在內核外部不依靠任何工具。重要的是要記住,ebpf的通用性既是導致其驚人靈活性的原因,也帶來了復雜性,
對于那些想要圍繞bpf構建新功能的人來說明,目前linux內核提供了一個框架而不是工具箱之外。
做一個快速的概括,ebpf自從2014年進入到內核當中的用來做高效的事件處理并且已經有了廣泛的使用,感謝ebpf-map,用ebpf編寫的程序可以維護一個狀態從而跨事件聚合信息,并且具有動態行為,
因為其閃電般的速度與性能,以及極簡的實現,別越來越廣泛的使用,現在我們來看下未來對于ebpf程序的期望吧。
Smart NICs與內核空間程序:未來
只花費了少數幾年,ebpf就已經整合進了linux的內核中,這也讓ebpf的內核變得更有趣且方向更不確定,但這里確實有兩個方向,一個是對于bpf程序的使用,
二個是將ebpf作為工具。已經的將來ebpf的用途是確定的,即是將bpf程序用于安全,高效,以及內核的事件處理,因為ebpf是依靠運行它的虛機
去定義的,這也為ebpf使用在出內核的其他地方提供了潛力,在寫作這篇文章時,最有趣的例子是智能網卡。
智能網卡是一種允許處理網絡流量的網卡,在不同程度上,將負載卸載到網卡本身,這個想法大約在兩千年左右形成,當一些nic支持卸載校驗和與分段時,
但是直到最近才出現了部分或者全部的數據平面卸載,這些新的nic有許多的名字,包括智能服務適配器,但通用的特性是可編程功能,以及大量的內存用于存儲狀態,
這樣的一個例子是Netronome 的 Agilio CX 系列網卡,具有10Gbps至40Gbps端口與ARM11處理器,2GB的DDR3內存,以及100多個專業處理核心。
因為他們具備大量的處理能力,最近智能網卡變成了ebpf的目標,通過采用ebpf,他們提供了各種各樣的用途,包括緩解DDos攻擊,提供動態網絡路由,切換,負載均衡等,
例如,在2016年的netdev 1.2會議上,Netronome 的 Jakub Kicinski 和 Nic Viljoen發表了一份名為通過ebpf/xdp卸載到智能網卡,Nic Viljoen 在其中陳述了一些非常粗略的早期基準,即在 Netronome SmartNIC 上為每個 FPC 實現每秒 300 萬個數據包,
正如 Viljoen 繼續指出的那樣,每個 SmartNIC 都有 72 到 120 個這樣的 FPC,假設但可能不切實際,最大 eBPF 吞吐量為 4.3 Tbps
最終是bpf作為一種技術的未來,因為ebpf的限制與簡單的實現,它提供了非常便攜與高效的方式去處理事件,除此之外,bpf還強制改變了問題解決的思路,
它以除了目標以及有狀態的代碼,而是選擇功能與高效的數據結構去存儲狀態,這種范式極大的縮減了程序設計的可能性,但這樣做同樣讓他跟任何一種程序設計的
方法兼容,因此ebpf可以被用作同步,異步,并行,或者分布式的(取決于數據存儲時協作的需求)。以及其他程序設計的方式。基于這一點事實,
我認為適合bpf的名字是功能虛擬機,簡稱FVM。
bpf帶來的技術變革可能也是bpf最容易遺忘的東西,通過JIT編譯將字節碼編譯為機器碼在內核中執行,由于內核對用戶態程序硬件隔離的巨大成本,
有時性能的損失達到了25%到33%,通過bpf被避免了,這意味著通過在內核空間虛擬機上運行用戶程序可以將運行速度提高 50%!這個想法,事實上,并不是一個新的想法,
在2014年,Gary Bernhardt在pycon上有一個有趣而又引人入勝的談話,標題為“javascript的誕生與死亡”,在當中,他引用了硬件隔離帶來的統計性能損耗,并且解釋到,
在假象的未來,所有軟件中的大多數都運行在一個內核的javascrit虛擬機中,在這樣的未來中,軟件的可移植性遠遠不是什么問題,因為軟件并不編譯為特定的硬件架構,而是javascript,
他甚至繼續談到了文檔模型的實現(DOM),數據存儲在瀏覽器當中來保存狀態用于渲染,現在運行在窗口系統也移動到內核中,這在概念上跟ebpf是非常接近的,理論上在將來可以被用到類似的事情,
雖然他在某些方面很輕松,不可否認的是Bernhardt的演講是基于事實并且也是今后計算機程序通過內核空間虛擬機完成隔離而不是硬件隔離的方向。
我們只需要拭目以待,看看ebpf是否可以成為這一變革的擁護者!
結語:ebpf發展非常迅速,并且確實是給人一種magic的感覺,哈哈。
- 文章翻譯
- Large-scale cluster management at Google with Borg
- Borg Omega and kubernetes
- scaling kubernetes to 7500 nodes
- bpf 的過去,未來與現在
- Demystifying Istio Circuit Breaking
- 知識圖譜
- skill level up graph
- 一、運維常用技能
- 1.0 Vim (編輯器)
- 1.1 Nginx & Tengine(Web服務)
- 基礎
- 1.2 zabbix
- 定義
- 登錄和配置用戶
- 1.3 RabbitMQ(消息隊列)
- 原理
- RabbitMQ(安裝)
- 1.4虛擬化技術
- KVM
- 1.5 Tomcat(Web中間件)
- 1.6Jenkins
- pipline
- 1.7 Docker
- network
- 1.8 Keepalived(負載均衡高可用)
- 1.9 Memcache(分布式緩存)
- 1.10 Zookeeper(分布式協調系統)
- 1.11 GitLab(版本控制)
- 1.12 Jenkins(運維自動化)
- 1.13 WAF(Web防火墻)
- 1.14 HAproxy負載均衡
- 1.15 NFS(文件傳輸)
- 1.16 Vim(編輯器)
- 1.17 Cobbler(自動化部署)
- 二、常用數據庫
- 2.1 MySQL(關系型數據庫)
- mysql主從復制
- 2.2 Mongodb(數據分析)
- 2.3 Redis(非關系數據庫)
- 三、自動化運維工具
- 3.1 Cobbler(系統自動化部署)
- 3.2 Ansible(自動化部署)
- 3.3 Puppet(自動化部署)
- 3.4 SaltStack(自動化運維)
- 四、存儲
- 4.1 GFS(文件型存儲)
- 4.2 Ceph(后端存儲)
- 五、運維監控工具
- 5.1 云鏡
- 5.2 ELK
- 六、運維云平臺
- 6.1 Kubernetes
- 6.2 OpenStack
- 介紹
- 安裝
- 七、Devops運維
- 7.1 理念
- 7.2 Devops運維實戰
- 八、編程語言
- 8.1 Shell
- 書籍《Wicked Cool Shell Scripts》
- 8.2 Python
- 8.3 C
- 8.4 Java
- leecode算法與數據結構
- 九、雜記
- 高優先級技能
- 知識點
- JD搜集
- 明顯的短板
- 1.0 Python
- 1.1 Kubernetes
- 1.18.2 《kubernetes in action》
- 遺漏知識點
- 1.18.3 GCP、azure、aliyun
- Azure文檔
- 1.18.5 《program with kubernetes》
- Istio
- HELM
- 《Kubernetes best practice》
- Kubernetes源碼學習
- Scheduler源碼
- 調度器入口
- 調度器框架
- Node篩選算法
- Node優先級算法
- pod搶占調度
- 入口
- 主要代碼結構
- new
- 文章翻譯
- Flannel
- 從二進制集群搭建
- 信息收集
- docker優化
- 1.2 shell
- 面試題
- grep awk sed 常見用法
- shell實踐
- 1.3 Data structure(數據結構)
- Calico
- Aliyun文檔以及重點模塊
- git
- 大數據組件
- 前端,后端,web框架
- cgroup,namespace
- 內核
- Linux搜集
- crontab
- centos7常用優化配置
- centos Mariadb
- eBPF
- ebpf的前世今生
- Linux性能問題排查與分析
- 性能分析搜集
- 性能分析常用10條
- 網絡性能優化
- 文本處理命令
- sql
- Iptables
- python面試題
- iptables
- iptables詳細
- zabbix面試題,proj
- prometheus
- web中間件
- nginx
- Haproxy
- grep sed awk
- Linux常用命令
- 云平臺
- 書籍Linux應用技巧
- kafka
- kafka面試題
- ETCD
- Jenkins
- 3天補充的點
- K8s源碼
- K8s
- k8s實操
- etcd
- test
- BPF
- PSFTP使用
- StackOverflow問答精選
- 問題
- 我對于學習思考
- 修改ssh超時時間
- 課程目錄
- 運維與運維開發
- The Person
- 個人雜談
- mysql主從復制
- 對于工作的認識與思考