日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

聊聊风口上的 eBPF

發(fā)布時間:2024/4/11 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊风口上的 eBPF 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

eBPF 是一個用于訪問 Linux 內(nèi)核服務和硬件的新技術,由于其靈活性和高性能等特點,被迅速用于網(wǎng)絡、出錯、跟蹤以及防火墻等多場景。目前國內(nèi)已有少數(shù)企業(yè)開始嘗試將 eBPF 引入生產(chǎn)實踐,又拍云也是其中一個。專為技術開發(fā)者提供知識分享的 Open Talk 公開課邀請了又拍云開發(fā)工程師周晨約直播分享 eBPF 的學習經(jīng)驗與開發(fā)心得,并對其分享內(nèi)容進行整理,下拉至文末點擊閱讀原文可回看原視頻。

大家好,今天分享的主題是《eBPF 探索之旅》,圍繞三部分展開:

  • eBPF 是什么

  • eBPF 能做什么

  • 如何編寫 eBPF 程序

認識 eBPF

eBPF 是什么,從字面上來看是擴展伯克利包處理器,那伯克利包處理器是什么呢?

在此之前先來了解一個性能優(yōu)秀的常用抓包工具:tcpdump

tcpdump

圖中展示了兩個常用指令

指令一:指定 IP 和端口,可以抓到 IP 為 220.173.103.227,端口為 80 的包

指令二:加上 grep,可以過濾出帶有 route 字段的數(shù)據(jù)

那么 tcpdump 又是如何做到通過用戶提供的規(guī)則處理網(wǎng)絡上收到的包,再 copy 給用戶的呢?如果放在用戶層,就需要在系統(tǒng)里所有 socket 讀寫的時候做一層處理,把規(guī)則放上去,這樣做難度太大。而 tcpdump 是基于 libpcap 庫實現(xiàn)的,libpcap 能做到在驅(qū)動將包交給內(nèi)核網(wǎng)絡時,把包取過來,通過用戶傳給 libpcap 的規(guī)則將需要的網(wǎng)絡包 copy 一份給用戶,再把包傳給內(nèi)核網(wǎng)絡棧,而之所以 libpcap 能做到這點全靠 BPF。

BPF

BPF 是基于寄存器虛擬機實現(xiàn)的,支持 jit,比基于棧實現(xiàn)的性能高很多。它能載入用戶態(tài)代碼并且在內(nèi)核環(huán)境下運行,內(nèi)核提供 BPF 相關的接口,用戶可以將代碼編譯成字節(jié)碼,通過 BPF 接口加載到 BPF 虛擬機中,當然用戶代碼跑在內(nèi)核環(huán)境中是有風險的,如有處理不當,可能會導致內(nèi)核崩潰。因此在用戶代碼跑在內(nèi)核環(huán)境之前,內(nèi)核會先做一層嚴格的檢驗,確保沒問題才會被成功加載到內(nèi)核環(huán)境中。

eBPF:BPF 的擴展

回到 eBPF,它作為一個 BPF 的擴展,都擴展了些什么呢?

  • 首先在功能上,不僅僅局限于網(wǎng)絡,它能夠借助 kprobe 獲取內(nèi)核函數(shù)運行信息,這樣調(diào)試內(nèi)核就不需要 gdb 或者加入內(nèi)核探點重新編譯內(nèi)核。

  • 可以借助 uprobe 獲取用戶函數(shù)的運行信息,kprobe 和 uprobe 不僅能獲取函數(shù)運營信息,還可以獲取代碼執(zhí)行到了哪一行時的寄存器以及棧信息,其原理可以理解為在某個指令打斷點,當 cpu 執(zhí)行到這個斷點的時候,cpu 會保存當前的寄存器信息,然后單步執(zhí)行斷點持載的 handler,也是想要在內(nèi)核中執(zhí)行的邏輯,執(zhí)行完成后 cpu 會回到這個斷點的位置,恢復寄存器的狀態(tài),然后繼續(xù)運行下去。

  • 支持 tracepoint,即在寫代碼中加入 trace 點,獲取執(zhí)行到這點時的信息。

  • 可以嵌入到 perf_event 中。我們熟知的 XDP 以及 tc 都是基于 eBPF 實現(xiàn)的,并且在性能上有著不俗的表現(xiàn)。

eBPF 的功能

  • 系統(tǒng)性能監(jiān)控/分析工具:能夠?qū)崿F(xiàn)性能監(jiān)控工具、分析工具等常用的系統(tǒng)分析工具,比如 sysstate 工具集,里面提供了 vmstate,pidstat 等多種工具,一些常用的 top、netstat(netstat 可被 SS 替換掉),uptime、iostat 等這些工具多數(shù)都是從 /proc、/sys、/dev 中獲取的會對系統(tǒng)產(chǎn)生一定的開銷,不適合頻繁的調(diào)用。比如在使用 top 的時候通過 cpu 排序可以看到 top cpu 占用也是挺高的,使用 eBPF 可以在開銷相對小的情況下獲取系統(tǒng)信息,定時將 eBPF 采集的數(shù)據(jù) copy 到用戶態(tài),然后將其發(fā)送到分析監(jiān)控平臺。

  • 用戶程序活體分析:做用戶程序活體分析,比如 openresty 中 lua 火焰圖繪制,程序內(nèi)存使用監(jiān)控,cdn 服務異常請求分析,程序運行狀態(tài)的查看,這些操作都可以在程序無感的情況下做到,可以有效提供服務質(zhì)量。

  • 防御攻擊:比如 DDoS 攻擊,DDoS 攻擊主要是在第七層、第三層以及第四層。第七層的攻擊如 http 攻擊,需要應用服務這邊處理。第四層攻擊,如 tcp syn 可以通過 iptable 拒絕異常的 ip,當然前提是能發(fā)現(xiàn)以及難點是如何區(qū)分正常流量和攻擊流量,簡單的防攻擊會導致一些誤傷,另外 tcp syn 也可以通過內(nèi)核參數(shù)保護應用服務。第 3 層攻擊,如 icmp。對于攻擊一般會通過一些特殊的途徑去發(fā)現(xiàn)攻擊,而攻擊的防御則可以通過 XDP 直接在網(wǎng)絡包未到網(wǎng)絡棧之前就處理掉,性能非常的優(yōu)秀。

  • 流控:可以控制網(wǎng)絡傳輸速率,比如 tc。

  • 替換 iptable:在 k8s 中 iptable 的規(guī)則往往會相當龐大,而 iptable 規(guī)則越多,性能也越差,使用 eBP 就可以解決,關于這方面有很多開源的實踐可以參考。

  • 服務調(diào)優(yōu):如下圖所示,在 cdn 服務中難免會出現(xiàn)一些指標突刺的情況,這種突刺拉高整體的指標,對于這種突刺時常會因為找不到切入點而無從下手,eBPF 存在這種潛力能幫助分析解決該問題,當 eBPF 發(fā)現(xiàn)網(wǎng)絡抖動,會主動采集當時應用的運行狀態(tài)。

eBPF 程序?qū)嵺`

編寫 eBPF 程序的內(nèi)核最低也要是 3.15,此版本剛好可以支持 eBPF ,但這時 eBPF 支持的特性比較少,不建議使用,最好是 4.8 以上的內(nèi)核,內(nèi)核越新 eBPF 支持的功能就越成熟。另外像 kprobe、uprobe、traceport 相關的參數(shù)要開起來,否則只能用 BPF的某些特性,而無法使用eBPF 的特性,相當于是空殼。通過路徑 /lib/modules/uname-r/source/.config 或者在 /boot/ 下查找對應版本的內(nèi)核 config 來查看系統(tǒng)是否開啟了所需的參數(shù)。

編寫 eBPF 程序的對環(huán)境也有一定的要求。eBPF 代碼需要編譯成 llvm 的字節(jié)碼,才能夠在 eBPF 及虛擬機中運行,因此需要安裝 llvm 以及 clang,安裝好之后可以通過 llc 來查看是否支持 BPF。

eBPF 代碼示例

內(nèi)核、環(huán)境都準備好后就可以開始編寫工作了。如果是不借助任何工具直接手寫一個 eBPF 程序會非常的困難,因為內(nèi)核提供的文檔對如何編寫 eBPF 程序的說明是比較缺乏的。當然內(nèi)核也有提供工具,在內(nèi)核包中的 bpftool 工具。推薦是使用工具 bcc,它能夠降低寫 BPF 程序的難度,提供了python、lua 的前端。以 python 為例,只需要寫好需要載入 eBPF 的 C代碼,再通過 bcc 提供的 BPF 類就可以將代碼載入到 eBPF 虛擬機中,執(zhí)行 python 程序,代碼就可以運行起來了。

圖中是 bcc 工具的使用例子,代碼非常簡單,導入一下 BPF,進行 BPF 初始化。

  • text 是要執(zhí)行的代碼,里面是一個函數(shù)

  • kprobe__schedule 內(nèi)容是調(diào)用 bpf_trace_printk(“hello world\n”);return 0

  • kprobe__schedule 的含義是用 kprobe的 特性在內(nèi)核調(diào)用 schedule 函數(shù)的時候調(diào)用 bpf_trace_printk,打出 hello world

  • bpf_trace_printk 會把這些輸出到 /sys/kernel/debug/tracing/trace_pipe 里,后面的 trace_print 就可以把數(shù)據(jù)打印出來

下面是通過 kprobe 監(jiān)控機器 tcp(ipv4)的連接狀態(tài)變化。首先需要知道 tcp 狀態(tài)變化時內(nèi)核會調(diào)用哪些函數(shù)。除了 time-wait 狀態(tài)之外,其他狀態(tài)基本上是通過 tcp_set_state 設置的。在 time-wait 階段的時候,內(nèi)核會創(chuàng)建一個新的結(jié)構體去存 time-wait 的 socket,內(nèi)核考慮到內(nèi)存的開銷問題,之前的 socket 會釋放掉。先不考慮 time-wait。

接下來看看具體的代碼,上圖中是載入到 eBPF 的 C 代碼。

  • 最上面的 BPF_HASH 表示創(chuàng)建一個 BPF 提供的 HASH 表;last 是 HASH 表的名稱;struct sock* 是指 key 的大小,這里表示指針大小;uint64_t 是 value 的大小,為 64 位;最后的 10240 表示 map 最多能夠放多少個元素。

  • 往下是一個結(jié)構體 bcc_tcp_state,可以看到后面有一個 BPF_PERF_OUTPUT,它是利用到了 perf ring buffer 的一個特性。

  • 再下面是函數(shù) get_tcp_state_change,該函數(shù)會在內(nèi)核調(diào)用 tcp_set_state 的時候調(diào)用。

通過內(nèi)核的幾個參數(shù),內(nèi)核的結(jié)構體 socket,以及這個函數(shù)傳進來的一些 state,可以獲取當時 tcp 連接的狀態(tài)轉(zhuǎn)化情況,上圖函數(shù)的第一個參數(shù) ctx 實際上是寄存器,后面是要介入函數(shù)的兩個參數(shù)。這里會把一些 tcp 的狀態(tài)存起來,使用 perf_submit 將這些狀態(tài)更新到 perf ring buffer 中,就可以在用戶態(tài)把 perf ring buffer 東西給讀出來,這就是 tcp 的一些狀態(tài)變化。

上圖是 python 代碼。

  • 首先把 C 代碼讀進來,通過調(diào)用 bpf 初始化,將代碼編譯成 eBPF 字節(jié)碼,載入到 eBPF 虛擬機中運行。

  • 下面是 attach_kprobe,就是在內(nèi)核調(diào)用 tcp,event 是指內(nèi)核在調(diào)用 tcp_set_state 的時候,fn_name 是指內(nèi)核在調(diào)用 tcp_set_state 時會執(zhí)行 get_tcp_state_change 函數(shù),就是前面 C 代碼中的函數(shù)。

  • 打開 perf ring buffer,即后面調(diào)用的 bpf[“state_events”].open_perf_buffer,里面的參數(shù)是一個 Callback 函數(shù),在ring buffer 有數(shù)據(jù)的時候就會調(diào)用一次 print_state,也就是說在 C 代碼中調(diào)用 perf_sumbit 時候就可以調(diào)用一次 print_tcpstats 函數(shù),并會輸出存入的數(shù)據(jù)。

  • 最下面調(diào)用了 perf_buffer_poll的功能,只會在 ring buffer 有消息時被喚醒,再調(diào)用 Callback 函數(shù),這樣就不會無謂地浪費 CPU。

利用 uprobe 查看應用服務信息

上圖是通過 uprobe 查看 nginx 請求分布的情況。首先要看 nginx 創(chuàng)建請求的位置,是在 ngx_http_create_request,和之前一樣寫一個要嵌入 eBPF 虛擬機的 C 代碼,還是創(chuàng)建一個 HASH 表,名稱是 req_distr,key 是 32 位大小,value 是 64 位,核心函數(shù)是 check_ngx_http_create_request,在 nginx 調(diào)用該函數(shù)時,會執(zhí)行這個鉤子函數(shù),函數(shù)內(nèi)部調(diào)用的是 count_req。把 PID 和 PID 上創(chuàng)建的請求次數(shù)對應起來,當 PID 調(diào)用過 ngx_http_create_request 時,請求計數(shù)就會 +1。如此也就可以看到整個請求在各個 work 上的分布情況。

圖中是 python 代碼,同樣把 C 代碼讀進來,并調(diào)用 bbf 把代碼編譯成 llvm 字節(jié)碼,載入到 eBPF 虛擬機中,再調(diào)用 attach_uprobe。name 是指 nginx 的一個二進制文件,sym 是指要在哪個函數(shù)中打個斷點,上圖是 ngx_http_create_request 函數(shù)。fn_name 是在 ngx_http_create_request 函數(shù)執(zhí)行的時候需要調(diào)用的函數(shù)。另外需要注意二進制文件必須要把編譯符號開放出來,比如編譯的時加個 -g,否則會找不到這個函數(shù)。最下面是簡單地獲取 HASH 表,去輸出 HASH 表的 key 和 value,這樣就能看到 pid 對應的 request 數(shù)量,pid 也就會對應著 worker,如此就能夠查看到運行 nginx 的請求分布情況。

查看運行中的 eBPF 程序與 map

可以通過內(nèi)核包中 bpftool 提供的 bpftool 工具查看,它的目錄是在 /lib/modules/uname-r/tools/bpf/bpftool 中,需要自己編譯一下,在 /lib/modules/uname-r/tools 下執(zhí)行 make-C/bpf/bpftool 就可以了。

上圖是 bpftool 工具查看 map(前面 BPF_HASH 創(chuàng)建的)情況的效果,-p 參數(shù),能夠展示得好看一些。prog 參數(shù)可以把在虛擬機中跑的程序給展示出來。這樣就能看到到底運行了那些 eBPF 程序以及申請的 map。

eBPF 在又拍云的發(fā)展

  • 完善 cdn 系統(tǒng)監(jiān)控體系

  • 強化 cdn 業(yè)務鏈路 traceing,提高服務水平,提供更多的性能分析的途徑

  • 解決 cdn 服務中遇到的某些難以解決的問題 注:目前通過 systemtap 可以解決

  • 將 XDP 引入又拍云邊緣機器,給予防范 DDoS 攻擊提供幫助

  • 替換 tcpdump 工具,加快抓包效率,減少抓包時對系統(tǒng)性能的影響

總結(jié)

以上是生活随笔為你收集整理的聊聊风口上的 eBPF的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。