使用 eBPF 零代碼修改繪制全景應(yīng)用拓?fù)?/h1>
本文為 DeepFlow 在首屆云原生社區(qū)可觀測(cè)性峰會(huì)上的演講實(shí)錄。回看鏈接[1],PPT下載[2]。
很高興有機(jī)會(huì)在第一屆可觀測(cè)性峰會(huì)上向大家介紹我們的產(chǎn)品 DeepFlow,我相信它會(huì)是今天 eBPF 含量最高的一個(gè)分享。DeepFlow 的能力很多,而我今天的分享會(huì)聚焦于一個(gè)點(diǎn)上說透,希望大家由此感知到 eBPF 帶給可觀測(cè)性的變革。那么我今天要分享的內(nèi)容就是,DeepFlow 如何利用 eBPF 技術(shù),在不改代碼、不改配置、不重啟進(jìn)程的前提下,自動(dòng)繪制云原生應(yīng)用的全景拓?fù)?/span>。全景應(yīng)用拓?fù)淠芙鉀Q我們很多問題:觀測(cè)任意服務(wù)的全景依賴關(guān)系、觀測(cè)整個(gè)應(yīng)用的瓶頸路徑、定位整個(gè)應(yīng)用中的故障位置。
在開始之前,我大概也介紹一下自己的背景:從清華畢業(yè)以來,我一直在云杉網(wǎng)絡(luò)工作,我們從 2016 年開始開發(fā) DeepFlow,我們基于 eBPF 等創(chuàng)新技術(shù)打通云基礎(chǔ)設(shè)施和云原生應(yīng)用的全棧環(huán)節(jié),零侵?jǐn)_的實(shí)現(xiàn)應(yīng)用的可觀測(cè)性。今天的分享分為四個(gè)部分:第一部分介紹傳統(tǒng)的解決方案如何繪制應(yīng)用拓?fù)?;第二部分介紹如何用 eBPF 完全零侵?jǐn)_的實(shí)現(xiàn)應(yīng)用拓?fù)涞挠?jì)算;第三部分介紹如何利用 eBPF 實(shí)現(xiàn)進(jìn)程、資源、服務(wù)等標(biāo)簽的注入,使得開發(fā)、運(yùn)維、運(yùn)營(yíng)都能從自己熟悉的視角查看這個(gè)拓?fù)?;最后第四部分介紹一個(gè)全鏈路壓測(cè)的 Demo 和我們?cè)诳蛻籼幍膸讉€(gè)實(shí)戰(zhàn)案例。
01|傳統(tǒng)解決方案的問題
首先我們知道,在應(yīng)用拓?fù)渲?,?jié)點(diǎn)可以展示為不同的粒度,例如按服務(wù)、按實(shí)例、按應(yīng)用等不同粒度展現(xiàn)。例如對(duì)于服務(wù)粒度,一個(gè)節(jié)點(diǎn)代表一個(gè)服務(wù),它由多個(gè)實(shí)例組成。節(jié)點(diǎn)之間的連線代表調(diào)用關(guān)系,通常也對(duì)應(yīng)了一系列的指標(biāo),表示服務(wù)之間調(diào)用的吞吐、異常、時(shí)延等性能。在云原生時(shí)代,云基礎(chǔ)設(shè)施、微服務(wù)拆分等因素會(huì)帶來很多挑戰(zhàn),導(dǎo)致繪制一個(gè)全景的應(yīng)用拓?fù)渥兊梅浅FD難。
傳統(tǒng)的解決方案我們很熟悉,通過埋點(diǎn)、插碼的方式,修改代碼來輸出類似于 Span 的調(diào)用信息,通過聚合 Span 得到應(yīng)用拓?fù)?。即使是采用自?dòng)化的 Java 字節(jié)碼注入等技術(shù),雖然看起來不用改代碼,但還是要修改啟動(dòng)參數(shù),意味著服務(wù)要重新發(fā)布,至少要重啟服務(wù)進(jìn)程。
插碼方案的困難 – 難以升級(jí)
在云原生場(chǎng)景下,使用這樣的方法來獲取應(yīng)用拓?fù)渥兊酶永щy。隨著服務(wù)拆得越來越微小,每個(gè)服務(wù)的開發(fā)人員的自由度變得越來越大,因此可能會(huì)出現(xiàn)各種新奇的語言、框架。相對(duì)于 Java agent 而言,其他語言基本都會(huì)涉及到代碼修改、重編譯重發(fā)布。當(dāng)然一般我們都會(huì)將這部分邏輯實(shí)現(xiàn)在一個(gè) SDK 中,然而 SDK 的升級(jí)也是一個(gè)痛苦甚至絕望的過程,業(yè)務(wù)方都不愿意升級(jí),升級(jí)意味著發(fā)版,發(fā)版可能就意味著故障。
那有方法能避免修改代碼嗎?云原生時(shí)代我們還有一個(gè)選項(xiàng) —— 使用服務(wù)網(wǎng)格(Service Mesh)。例如 Istio 在 K8s 及非容器環(huán)境下可構(gòu)建一個(gè)服務(wù)網(wǎng)絡(luò)。
網(wǎng)格方案的困難 – 難以全覆蓋
但服務(wù)網(wǎng)格的問題在于并不能覆蓋所有協(xié)議,例如 Istio 主要覆蓋 HTTP/gRPC,而其他大量各種各樣的 Protobuf/Thrift RPC,以及 MySQL/Redis/Kafka/MQTT 等中間件訪問都無法覆蓋到。另外從因果關(guān)系來講,我們可以因?yàn)檫x擇了服務(wù)網(wǎng)格而順帶實(shí)現(xiàn)一部分可觀測(cè)性,但肯定不會(huì)因?yàn)橐?shí)現(xiàn)可觀測(cè)性而引入服務(wù)網(wǎng)格這樣一個(gè)帶有侵入性的技術(shù)。
除此之外,在云原生時(shí)代,即使我們?nèi)ジ臉I(yè)務(wù)代碼、上服務(wù)網(wǎng)格,仍然無法獲取到一個(gè)完整的應(yīng)用拓?fù)洹1热缭苹A(chǔ)設(shè)施中的 Open vSwitch,K8s 中的 IPVS,Ingress 位置的 Nginx 網(wǎng)關(guān),這些都是看不到的。
下面出場(chǎng)的就是我們今天要講的主角,eBPF,它是一個(gè)非?;馃岬男录夹g(shù)。我們來看看它是不是能解決我們今天聚焦的問題,能否將應(yīng)用拓?fù)洚嬋?、畫?zhǔn),能為開發(fā)、運(yùn)維等不同團(tuán)隊(duì)的人提供一個(gè)統(tǒng)一的視圖,消除他們的 Gap。我們知道 eBPF 有很多很好的特性,它不需要業(yè)務(wù)改代碼、不需要服務(wù)修改啟動(dòng)參數(shù)、不需要重啟服務(wù)進(jìn)程,它是編程語言無關(guān)的,而且能覆蓋到云基礎(chǔ)設(shè)施和云原生應(yīng)用的整個(gè)技術(shù)棧。DeepFlow 基于 eBPF 技術(shù)也享受到了很多這方面的紅利,我們的客戶做 POC、社區(qū)的用戶試用都非常絲滑,不用去考慮運(yùn)維窗口、實(shí)施周期,DeepFlow 的社區(qū)版只需一條命令,五分鐘即可開啟全景、全棧的可觀測(cè)性。
DeepFlow 軟件架構(gòu)
這里也用一頁(yè) PPT 來簡(jiǎn)單介紹一下 DeepFlow 社區(qū)版的軟件架構(gòu)。我們開源至今還不到一年,目前在社區(qū)受到了不少關(guān)注。上圖中間藍(lán)色的部分是 DeepFlow 的兩個(gè)主要組件:Agent 負(fù)責(zé)采集數(shù)據(jù),利用 eBPF 的零侵?jǐn)_特性,覆蓋各種各樣的云原生技術(shù)棧;Server 負(fù)責(zé)富化數(shù)據(jù),將標(biāo)簽與數(shù)據(jù)關(guān)聯(lián),并存儲(chǔ)至實(shí)時(shí)數(shù)倉(cāng)中。在北向,DeepFlow 提供 SQL、PromQL、OTLP 等接口,通過 DeepFlow 自己的 GUI 展現(xiàn)所有可觀測(cè)性數(shù)據(jù),也兼容 Grafana、Prometheus、OpenTelemetry Collector 等生態(tài)。在南向,DeepFlow 可集成 Prometheus 等指標(biāo)數(shù)據(jù)、SkyWalking 等追蹤數(shù)據(jù)、Pyrosope 等 Profile 數(shù)據(jù)。
AutoTracing AutoTagging
今天我們要聚焦的一點(diǎn),就是 DeepFlow 如何使用 eBPF 生成全景應(yīng)用拓?fù)?。DeepFlow 的兩個(gè)核心能力 AutoTracing 和 AutoTagging 為應(yīng)用拓?fù)涞纳傻於藞?jiān)實(shí)的基礎(chǔ)。在沒有任何代碼修改、不重啟任何業(yè)務(wù)進(jìn)程的情況下,我們實(shí)現(xiàn)了全景應(yīng)用拓?fù)涞睦L制。首先,我們可通過 eBPF 從網(wǎng)絡(luò)包、內(nèi)核 Socket Data 中獲取原始比特流;接下來我們分析 Raw Data 中的 IP、端口等信息,使用 eBPF 將原始數(shù)據(jù)與進(jìn)程、資源、服務(wù)等信息關(guān)聯(lián),以便繪制不同團(tuán)隊(duì)不同視角的應(yīng)用拓?fù)?,這也是今天分享的第三部分;然后我們會(huì)從原始數(shù)據(jù)中提取應(yīng)用調(diào)用協(xié)議,聚合調(diào)用中的請(qǐng)求和響應(yīng),計(jì)算調(diào)用的吞吐、時(shí)延和異常,以及其他系統(tǒng)和網(wǎng)絡(luò)性能指標(biāo);最后,基于這類 Request Scope 的 Span 數(shù)據(jù),我們可以聚合生成全景應(yīng)用拓?fù)?,也可以通過關(guān)聯(lián)關(guān)系的計(jì)算生成分布式追蹤火焰圖。
今天我們主要講的是應(yīng)用拓?fù)涞睦L制,對(duì)于使用 eBPF 實(shí)現(xiàn)全自動(dòng)的分布式追蹤這個(gè)話題,DeepFlow 社區(qū)的博客 https://deepflow.io/blog 上有很多資料。
02|eBPF 零侵?jǐn)_計(jì)算全景應(yīng)用拓?fù)?/h1>
Universal Application Topology
首先讓我們看一個(gè)效果圖,這是 DeepFlow 展示的一個(gè)全景應(yīng)用拓?fù)?。這個(gè)拓?fù)淇赡艽蠹冶容^熟悉,它是 OpenTelemetry 的一個(gè) Demo,它 Fork 自 Google Cloud Platform 的一個(gè)項(xiàng)目,它是一個(gè)小型的電商應(yīng)用。從圖中可以看到,DeepFlow 可以獲取所有服務(wù)之間的調(diào)用關(guān)系以及相應(yīng)的性能指標(biāo),這都是通過 eBPF 實(shí)現(xiàn)的,沒有做任何的代碼修改和進(jìn)程重啟,展現(xiàn)這些結(jié)果之前我們關(guān)閉了所有 OTel Instrumentation。
在這一節(jié),我們將聚焦四個(gè)問題:第一個(gè)問題是如何采集原始數(shù)據(jù);第二個(gè)問題是如何解析應(yīng)用協(xié)議,eBPF做了什么;第三個(gè)問題是如何計(jì)算全棧性能指標(biāo);第四個(gè)問題是如何適配低版本內(nèi)核,許多用戶的內(nèi)核版本可能是3.10。
數(shù)據(jù)采集:DeepFlow 同時(shí)用到了 eBPF 和它的前身,有 30 年歷史的 Classic BPF(cBPF)。在云原生環(huán)境中,應(yīng)用進(jìn)程運(yùn)行在容器 Pod 內(nèi),容器可能存在于多個(gè)節(jié)點(diǎn)上,并通過網(wǎng)關(guān)連接到數(shù)據(jù)庫(kù)等其他服務(wù)。我們可以使用 eBPF 技術(shù)來覆蓋所有的端節(jié)點(diǎn),并使用 cBPF 覆蓋所有的中間轉(zhuǎn)發(fā)節(jié)點(diǎn)。從應(yīng)用進(jìn)程(端節(jié)點(diǎn))層面,我們使用 kprobe 和 tracepoint 覆蓋所有的 TCP/UDP socket 讀寫操作;并使用 uprobe 來掛載到應(yīng)用程序內(nèi)的核心函數(shù)上,比如 OpenSSL/Golang 的 TLS/SSL 函數(shù),來獲取加密或壓縮之前的數(shù)據(jù)。除此之外,在兩個(gè)服務(wù)之間還有許多中間轉(zhuǎn)發(fā)路徑,例如 IPVS、iptables、OvS 等,此時(shí)需要使用 cBPF 來獲取網(wǎng)絡(luò)包數(shù)據(jù),因?yàn)檫@部分流量不會(huì)有用戶態(tài)應(yīng)用程序去讀寫,會(huì)直接在內(nèi)核里查表轉(zhuǎn)發(fā)。
eBPF Probes
這里列舉了 DeepFlow 中主要使用到的 eBPF Probe,包括最左邊的 kprobe,以及中間最高性能的 tracepoint,以及最右邊解決加密和壓縮數(shù)據(jù)的 uprobe。其中 tracepoint 滿足了 DeepFlow 中的絕大多數(shù)需求。
協(xié)議解析:在獲取原始數(shù)據(jù)方面,我們使用 eBPF 和 cBPF 來捕獲字節(jié)流,但此時(shí)我們無法看到任何可理解的信息。接下來,我們要做的就是從這些字節(jié)流中提取出我們關(guān)心的應(yīng)用協(xié)議。大多數(shù)協(xié)議都是明文的,例如 HTTP、RPC、SQL 等,我們可以直接遵照協(xié)議規(guī)范來解析它們的內(nèi)容。對(duì)于一些特殊的協(xié)議,例如 HTTP2 之類的壓縮協(xié)議,我們需要進(jìn)行更復(fù)雜的處理。我們可以使用 tracepoint 或 kprobe 來提取未壓縮的字段,但此時(shí)對(duì)于已經(jīng)壓縮的頭部字段,還原它們是一項(xiàng)困難的工作,因此我們會(huì)選擇使用 uprobe 作為補(bǔ)充來采集壓縮協(xié)議的數(shù)據(jù)。另外對(duì)于加密流量也無法從內(nèi)核函數(shù)中獲取明文,我們通過 uprobe 直接獲取加密之前的數(shù)據(jù)。最后,對(duì)于私有協(xié)議,它們沒有可遵循的通用規(guī)范,或者雖然遵循了 Protobuf/Thrift 等標(biāo)準(zhǔn)但無法提供協(xié)議定義文件,此時(shí)我們提供了基于 WASM 的插件化的可編程接口,在未來也有計(jì)劃提供基于 LUA 的插件機(jī)制。
在協(xié)議解析層面還需要考慮的一個(gè)事情是流重組。例如一個(gè) HTTP2/gRPC 請(qǐng)求的頭部字段可能會(huì)分為多次系統(tǒng)調(diào)用讀寫,或者分拆為多個(gè)網(wǎng)絡(luò)包收發(fā),此時(shí)我們需要還原完整請(qǐng)求或響應(yīng)頭,一遍重組一遍嘗試解析。
eBPF WASM
特別提一下我們對(duì)私有協(xié)議的識(shí)別,通過結(jié)合 eBPF 和 WebAssembly 的能力,提供一個(gè)靈活的插件機(jī)制。插件在 DeepFlow 中解決兩個(gè)問題:提供對(duì)私有協(xié)議的解析能力、提供對(duì)任意協(xié)議的業(yè)務(wù)字段解析能力。商用軟件供應(yīng)商或業(yè)務(wù)開發(fā)人員可以輕松添加自定義插件來解析協(xié)議,將可觀測(cè)性從 IT 層面提升到業(yè)務(wù)層面。這樣的方式使得我們可以自定義提取出一些業(yè)務(wù)指標(biāo)或業(yè)務(wù)標(biāo)簽,例如訂單量、用戶 ID、交易 ID、車機(jī) ID 等信息。這些業(yè)務(wù)信息對(duì)于不同的業(yè)務(wù)場(chǎng)景都是獨(dú)特的,我們將這個(gè)靈活性給到了業(yè)務(wù)方,使得業(yè)務(wù)方可以快速實(shí)現(xiàn)零侵?jǐn)_的可觀測(cè)性。
RED Metrics
性能指標(biāo):我們可以通過數(shù)請(qǐng)求和響應(yīng)的個(gè)數(shù)來獲取吞吐量,同時(shí)還可以根據(jù)響應(yīng)狀態(tài)碼、耗時(shí)等信息計(jì)算 Error 和 Delay 指標(biāo)。吞吐和異常的計(jì)算相對(duì)簡(jiǎn)單,只需要基于單個(gè)請(qǐng)求或單個(gè)響應(yīng)進(jìn)行處理即可。最困難的是對(duì)時(shí)延的計(jì)算,我們需要使用 eBPF 技術(shù)來關(guān)聯(lián)請(qǐng)求和響應(yīng)這兩個(gè)動(dòng)作。這個(gè)過程又分為兩步:從 Socket/Packet Data 中基于 <sip, dip, sport, dport, protocol> 五元組聚合出 TCP/UDP Flow;然后在 Flow 上下文中匹配應(yīng)用協(xié)議的每一個(gè) Request 和 Response。而對(duì)于第二步,一個(gè) Flow 中一般會(huì)有多個(gè)請(qǐng)求和響應(yīng),匹配邏輯我們以 HTTP 協(xié)議為例解釋:
- 對(duì)于串行協(xié)議如 HTTP 1.0,直接匹配臨近的請(qǐng)求和響應(yīng)即可
- 對(duì)于并發(fā)協(xié)議如 HTTP 2.0,需要提取協(xié)議頭中的 StreamID 進(jìn)行請(qǐng)求和響應(yīng)的配對(duì)
- 還有一種特殊情況即 HTTP 1.1 中的管道機(jī)制,他是一種偽并發(fā)協(xié)議,我們可以利用它的 FIFO 特點(diǎn)完成請(qǐng)求和響應(yīng)的配對(duì)
Network Metrics
但是,在云原生環(huán)境下,僅僅只統(tǒng)計(jì)應(yīng)用層 RED 往往不夠。例如我們會(huì)發(fā)現(xiàn)客戶端側(cè)看到的時(shí)延是 3 秒,而服務(wù)端側(cè)看到的時(shí)延是 3 毫秒;或者客戶端和服務(wù)端側(cè)都看不到有請(qǐng)求,但下游服務(wù)卻報(bào)錯(cuò)了。針對(duì)這些場(chǎng)景,我們基于 Flow 數(shù)據(jù)計(jì)算得到了網(wǎng)絡(luò)層面的吞吐、異常、時(shí)延。業(yè)務(wù)開發(fā)發(fā)現(xiàn)時(shí)延高時(shí),可以快速查看網(wǎng)絡(luò)層時(shí)延來判斷到底是業(yè)務(wù)自身的問題還是基礎(chǔ)設(shè)施問題;另外在發(fā)現(xiàn)請(qǐng)求報(bào)錯(cuò)時(shí)可以快速查看是否建連或者傳輸異常了。
針對(duì)時(shí)延我們分的更細(xì)致,通過從 Packet Data 中重建 TCP 狀態(tài)機(jī)來更加精細(xì)的展現(xiàn)各個(gè)層面引入的時(shí)延。在生產(chǎn)環(huán)境中我們會(huì)發(fā)現(xiàn)高時(shí)延的原因一般分為幾個(gè)方面:
- 建連慢:由于防火墻、負(fù)載均衡、網(wǎng)關(guān)的影響,導(dǎo)致 TCP 建連過程慢,這一過程又進(jìn)一步拆分為了到底是客戶側(cè)建連慢還是服務(wù)側(cè)建連慢
- 框架慢:由于業(yè)務(wù)代碼在建連后的慢處理,客戶端在建連后等待了一段時(shí)間才發(fā)送請(qǐng)求,這段等待時(shí)間我們會(huì)刻畫為客戶端等待時(shí)延,它能夠方面定位到底是框架/庫(kù)層面的問題,還是業(yè)務(wù)代碼的問題
- 系統(tǒng)慢:由于操作系統(tǒng)處理不及時(shí),例如系統(tǒng)負(fù)載高等,對(duì)請(qǐng)求的 TCP ACK 回復(fù)慢,從而導(dǎo)致 TCP 無法高效增大窗口大小
- 傳輸慢:網(wǎng)絡(luò)重傳、零窗等也是導(dǎo)致高時(shí)延的一些可能原因
在云原生環(huán)境下,還有一個(gè)特點(diǎn)是網(wǎng)絡(luò)路徑非常長(zhǎng),例如兩個(gè)服務(wù)之間的通信可能依次經(jīng)過容器 Pod 網(wǎng)卡、容器 Node 網(wǎng)卡、KVM 主機(jī)網(wǎng)卡等。路徑的復(fù)雜性也是引發(fā)傳輸慢的主要原因。由于 DeepFlow 同時(shí)使用 cBPF,因此可以從所有中間轉(zhuǎn)發(fā)路徑中觀測(cè)到應(yīng)用層和網(wǎng)絡(luò)層的時(shí)延,然后通過對(duì)比逐跳時(shí)延來定位到底是哪一跳開始出現(xiàn)了問題。
System Metrics
除了網(wǎng)絡(luò)層面以外,造成慢調(diào)用的還有一個(gè)重要原因是 IO 慢。例如 Client 訪問 DB 時(shí)可能出現(xiàn)時(shí)延抖動(dòng),而這些抖動(dòng)通常是由 DB 的文件讀寫慢導(dǎo)致。再例如大數(shù)據(jù)場(chǎng)景下一批 Worker 中可能總有那么一兩個(gè) Worker 完成的慢,排查后會(huì)發(fā)現(xiàn)通常是由于文件讀寫慢導(dǎo)致。這里 DeepFlow 的做法是觀測(cè)所有與應(yīng)用調(diào)用相關(guān)的 IO 時(shí)延,并記錄所有的慢 IO 事件。實(shí)現(xiàn)層面,文件 IO 與 Socket IO 事件可通過 tracepoint/kprobe hook 同樣一組函數(shù)獲取到,通過 FD 的類型可以進(jìn)行區(qū)分。依靠這樣的能力,我們能快速的定位到 deepflow-server 寫入 clickhouse-server 偶發(fā)性慢是由于文件 IO 時(shí)延抖動(dòng)造成,且能定位到具體的文件路徑。
低版內(nèi)核:最后我們總結(jié)一下本節(jié)。我們希望在低版本內(nèi)核中 DeepFlow 也能展現(xiàn)更多的能力。以 Kernel 4.14 為界,DeepFlow 在 4.14 的環(huán)境下可以基于 eBPF 實(shí)現(xiàn)全功能,包括加密數(shù)據(jù)的觀測(cè)、應(yīng)用進(jìn)程的性能指標(biāo)、系統(tǒng)層面文件 IO 性能指標(biāo),以及全自動(dòng)的分布式追蹤能力。而在 4.14 以下的內(nèi)核環(huán)境中,我們基于 cBPF 也實(shí)現(xiàn)了大部分的能力,包括對(duì)于明文協(xié)議、私有協(xié)議的觀測(cè),對(duì)于壓縮協(xié)議部分頭部字段的觀測(cè),以及對(duì)于應(yīng)用性能、網(wǎng)絡(luò)性能指標(biāo)的采集,對(duì)于性能指標(biāo)的全棧路徑追蹤。
03|eBPF 自動(dòng)關(guān)聯(lián)服務(wù)和資源標(biāo)簽
講到這里實(shí)際上我們整個(gè)目標(biāo)只完成了一半。我們從原始數(shù)據(jù)中提取出來了指標(biāo)、調(diào)用關(guān)系、調(diào)用鏈,但還需要以開發(fā)者熟悉的方式展現(xiàn)出來。Packet 和 Socket Data 中只有 IP 和端口號(hào)信息,這是開發(fā)和運(yùn)維都無法理解的。我們希望所有的數(shù)據(jù)都能從實(shí)例、服務(wù)、業(yè)務(wù)等維度按需展示。
在這個(gè)問題上我們也遇到了一些挑戰(zhàn):我會(huì)先介紹從哪里采集標(biāo)簽,以及采集什么樣的標(biāo)簽;然后討論輪詢采集的方式會(huì)帶來哪些問題;接下來我會(huì)介紹一下基于 eBPF 的事件觸發(fā)的方案,來避免輪詢的缺陷;最后同樣的也會(huì)介紹一下我們?cè)诘桶姹緝?nèi)核環(huán)境下的一些能力。
標(biāo)簽數(shù)據(jù):首先僅通過 IP 地址是不能完整關(guān)聯(lián)客戶端和服務(wù)端的服務(wù)信息的,這是因?yàn)閷?shí)際環(huán)境中普遍存在 NAT,包括 SNAT、DNAT、FULLNAT 等。實(shí)際上通過單側(cè)的 IP Port 也難以準(zhǔn)確關(guān)聯(lián),這是因?yàn)?Client Port Reuse 也是一個(gè)普遍存在的現(xiàn)象。因此,在 DeepFlow 中使用五元組來將通信端點(diǎn)關(guān)聯(lián)至服務(wù)。具體來講,我們會(huì)通過 K8s apiserver 獲取 IP 對(duì)應(yīng)的容器資源標(biāo)簽;通過 CMDB 及腳本化的插件獲取 PID 對(duì)應(yīng)的業(yè)務(wù)標(biāo)簽信息;然后再依靠下面要講的一些機(jī)制將 IP Port 的五元組與 PID 關(guān)聯(lián)起來,最終實(shí)現(xiàn)資源、服務(wù)、業(yè)務(wù)標(biāo)簽的自動(dòng)注入。
輪詢方案:我們首先能想到的是通過輪詢 /proc/pid/net/ 文件夾來獲取 PID 與 Socket 五元組的關(guān)聯(lián)。每個(gè) agent 獲取本機(jī)的關(guān)聯(lián)信息,并通過 server 交換得到全局的關(guān)聯(lián)信息,從而使得每個(gè) agent 能獨(dú)立的為客戶端和服務(wù)端標(biāo)注雙端的 PID 信息。
輪詢方案也會(huì)碰到一些挑戰(zhàn),例如 LVS 場(chǎng)景下,/proc 下的 Socket 信息指向的是 LVS,但從業(yè)務(wù)上來講我們希望獲取 LVS 背后的 RS 的進(jìn)程信息。DeepFlow 通過同步 LVS 轉(zhuǎn)發(fā)規(guī)則解決這個(gè)問題。具體來講,基于 LVS 規(guī)則和包頭中嵌入的 TOA(TCP Option Address)字段,可以快速的在 RS 上定位客戶端的 PID,并通過 LVS 轉(zhuǎn)發(fā)規(guī)則快速的在客戶端側(cè)定位服務(wù)端的 PID。
觸發(fā)方案:當(dāng)時(shí)輪詢總會(huì)存在時(shí)間間隔,因此永遠(yuǎn)無法解決短連接的監(jiān)控問題。在這方面 DeepFlow 也做了一些工作,這就是下面我們要介紹的觸發(fā)式方案。
受 TOA 的啟發(fā),我們基于 eBPF 實(shí)現(xiàn)了一個(gè) TOT(TCP Option Tracing)的機(jī)制,即在 TCP 包頭 Option 字段中嵌入額外的信息,表示發(fā)送方的 IP 和 PID。這樣的話我們就能將源發(fā)的進(jìn)程信息告知對(duì)端了。為了實(shí)現(xiàn)這樣的機(jī)制,我們使用 eBPF sockops 和 tracepoint,一共 Hook 了五個(gè)函數(shù)。我們會(huì)在每個(gè) TCP SYN、SYN-ACK 包中注入 TOT 信息,使得連接新建時(shí)即能標(biāo)記進(jìn)程信息。同時(shí)也會(huì)概率性的抽取 TCP PSH 包注入 TOT,使得對(duì)于長(zhǎng)連接,即使 agent 的啟動(dòng)時(shí)間滯后于業(yè)務(wù)進(jìn)程,也能及時(shí)獲取到進(jìn)程信息。
低版內(nèi)核:同樣這里也分享一下我們?cè)诘桶姹緝?nèi)核上做的一些工作。首先輪詢的方案依靠掃描 /proc 文件夾實(shí)現(xiàn),因此是能適配所有 2.6 的內(nèi)核環(huán)境的。在 3.10 及以上的內(nèi)核中,我們也提供了一個(gè)精巧的 ko 模塊來實(shí)現(xiàn) TOT 的注入,這個(gè)內(nèi)核模塊僅有 227 行代碼,我們也進(jìn)行了開源,歡迎大家使用。
04|Demo – 持續(xù)觀測(cè)全鏈路壓測(cè)性能瓶頸
OTel Demo
最后通過一個(gè) Demo 介紹一下這些工作的效果。我們?nèi)匀皇且?OTel 的電商 Demo 為例,仍然是關(guān)閉了其中的所有 OTel Instrumentation。選擇這個(gè) Demo 的理由在于,他是一個(gè)典型的微服務(wù)架構(gòu)的應(yīng)用,且盡力模擬了比較真實(shí)的電商場(chǎng)景,微服務(wù)的實(shí)現(xiàn)語言涵蓋了十二種之多,也包含了 PostgreSQL、Redis、Kafka、Envoy 等中間件。
首先我們可以看到,不修改代碼、不修改啟動(dòng)參數(shù)、不重啟進(jìn)程,我們已經(jīng)能自動(dòng)繪制微服務(wù)粒度的全景應(yīng)用。當(dāng)我們注入一個(gè) 1.5K QPS 的壓力時(shí),能清晰的在拓?fù)渲锌吹狡款i鏈路。沿著 frontend 服務(wù)一路往下,也能快速的定位瓶頸服務(wù) ProductCatalog。接下來我們分為三次,分別將 ProductCatalog 擴(kuò)容至 2x、4x、8x 副本數(shù),擴(kuò)容的過程中也可清晰的看到瓶頸鏈路在逐漸消失,知道最終所有服務(wù)的時(shí)延恢復(fù)正常。
除了這個(gè) Demo 意外,這里也分享幾個(gè)我們?cè)诳蛻籼幍膶?shí)戰(zhàn)案例:
- 某造車新勢(shì)力客戶,使用 DeepFlow 從數(shù)萬 Pod 中在 5 分鐘內(nèi)定位 RDS 訪問量最大的 Pod、所屬服務(wù)、負(fù)責(zé)該服務(wù)的團(tuán)隊(duì)。
- 某股份制銀行客戶,信用卡核心業(yè)務(wù)上線受阻,壓測(cè)性能上不去,使用 DeepFlow 在 5 分鐘內(nèi)發(fā)現(xiàn)兩個(gè)服務(wù)之間 API 網(wǎng)關(guān)是性能瓶頸,進(jìn)而發(fā)現(xiàn)緩存設(shè)置不合理。
- 某互聯(lián)網(wǎng)客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位服務(wù)間 K8s CNI 性能瓶頸,識(shí)別由于環(huán)路造成的某個(gè)服務(wù)上下云訪問時(shí)延周期性飆升的問題,云廠商兩周無解。
- 某證券客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位 ARP 故障導(dǎo)致 Pod 無法上線的問題,「瞬間」結(jié)束業(yè)務(wù)、系統(tǒng)、網(wǎng)絡(luò)多部門「會(huì)商」。
- 某基礎(chǔ)設(shè)施軟件客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位 Rust 客戶端使用 Tokio 不合理導(dǎo)致的 gRPC 偶發(fā)性超大時(shí)延,終結(jié)了 QA 及多個(gè)開發(fā)團(tuán)隊(duì)之間踢來踢去的 Bug。
- 某四大行客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位某個(gè) NFVGW 實(shí)例對(duì)特定服務(wù)流量不轉(zhuǎn)發(fā)導(dǎo)致的客戶端頻繁重試。
從這些案例中我們能發(fā)現(xiàn),依靠 eBPF 技術(shù)的零侵?jǐn)_特性,我們能完整的覆蓋應(yīng)用的全景調(diào)用拓?fù)洌夷苷宫F(xiàn)任意調(diào)用路徑中的全棧性能指標(biāo)。得益于這些可觀測(cè)性能力,我們能快速的定位 RDS、API 網(wǎng)關(guān)、K8s CNI、ARP 故障、Rust Tokio 庫(kù)、NFVGW 造成的故障。
Distributed Profile
這是最后一張PPT,我想和大家分享一下 DeepFlow 對(duì)可觀測(cè)性的更多思考。在傳統(tǒng) APM 中,我們通常使用 Span 之間的關(guān)聯(lián)關(guān)系以火焰圖的方式展現(xiàn)一次分布式調(diào)用(Trace),也會(huì)講所有的 Span 聚合為指標(biāo)來展現(xiàn)所有服務(wù)之間的應(yīng)用拓?fù)?。我們發(fā)現(xiàn)拓?fù)湔宫F(xiàn)了所有調(diào)用的數(shù)據(jù),而追蹤展現(xiàn)了一個(gè)調(diào)用的數(shù)據(jù),它們二者恰恰是取了兩個(gè)極端。DeepFlow 目前正在探索,中間是否有一個(gè)折中的點(diǎn),他們展示一組聚合的火焰圖或拓?fù)鋱D,有點(diǎn)類似于單個(gè)進(jìn)程的 Profile,但是用于分布式應(yīng)用的場(chǎng)景,我們稱它為 Distributed Profile。我們相信這樣的折中會(huì)帶來效率的提升,它不香所有調(diào)用聚合而成的拓?fù)洌瑫?huì)有太多噪聲;也不像單一請(qǐng)求展示出來的 Trace,問題排查需要一個(gè)一個(gè) Trace 的看。
而 eBPF,正是絕佳的實(shí)現(xiàn) Distributed Profile 的技術(shù)手段,請(qǐng)期待后續(xù)我們進(jìn)一步的分享。
05|什么是 DeepFlow
DeepFlow[3] 是一款開源的高度自動(dòng)化的可觀測(cè)性平臺(tái),是為云原生應(yīng)用開發(fā)者建設(shè)可觀測(cè)性能力而量身打造的全棧、全鏈路、高性能數(shù)據(jù)引擎。DeepFlow 使用 eBPF、WASM、OpenTelemetry 等新技術(shù),創(chuàng)新的實(shí)現(xiàn)了 AutoTracing、AutoMetrics、AutoTagging、SmartEncoding 等核心機(jī)制,幫助開發(fā)者提升埋點(diǎn)插碼的自動(dòng)化水平,降低可觀測(cè)性平臺(tái)的運(yùn)維復(fù)雜度。利用 DeepFlow 的可編程能力和開放接口,開發(fā)者可以快速將其融入到自己的可觀測(cè)性技術(shù)棧中。
GitHub 地址:https://github.com/deepflowio/deepflow
訪問 DeepFlow Demo[4],體驗(yàn)高度自動(dòng)化的可觀測(cè)性新時(shí)代。
參考資料
[1]回看鏈接: https://www.bilibili.com/video/BV1B14y1f7UB/
[2]PPT下載: http://yunshan-guangzhou.oss-cn-beijing.aliyuncs.com/yunshan-ticket/pdf/1a1847b242ae7c16d53f0af31d9c6aa4_20230509140119.pdf
[3]DeepFlow: https://github.com/deepflowio/deepflow
[4]DeepFlow Demo: https://deepflow.yunshan.net/docs/zh/install/overview/
相關(guān)新聞
本文為 DeepFlow 在首屆云原生社區(qū)可觀測(cè)性峰會(huì)上的演講實(shí)錄。回看鏈接[1],PPT下載[2]。
很高興有機(jī)會(huì)在第一屆可觀測(cè)性峰會(huì)上向大家介紹我們的產(chǎn)品 DeepFlow,我相信它會(huì)是今天 eBPF 含量最高的一個(gè)分享。DeepFlow 的能力很多,而我今天的分享會(huì)聚焦于一個(gè)點(diǎn)上說透,希望大家由此感知到 eBPF 帶給可觀測(cè)性的變革。那么我今天要分享的內(nèi)容就是,DeepFlow 如何利用 eBPF 技術(shù),在不改代碼、不改配置、不重啟進(jìn)程的前提下,自動(dòng)繪制云原生應(yīng)用的全景拓?fù)?/span>。全景應(yīng)用拓?fù)淠芙鉀Q我們很多問題:觀測(cè)任意服務(wù)的全景依賴關(guān)系、觀測(cè)整個(gè)應(yīng)用的瓶頸路徑、定位整個(gè)應(yīng)用中的故障位置。
在開始之前,我大概也介紹一下自己的背景:從清華畢業(yè)以來,我一直在云杉網(wǎng)絡(luò)工作,我們從 2016 年開始開發(fā) DeepFlow,我們基于 eBPF 等創(chuàng)新技術(shù)打通云基礎(chǔ)設(shè)施和云原生應(yīng)用的全棧環(huán)節(jié),零侵?jǐn)_的實(shí)現(xiàn)應(yīng)用的可觀測(cè)性。今天的分享分為四個(gè)部分:第一部分介紹傳統(tǒng)的解決方案如何繪制應(yīng)用拓?fù)?;第二部分介紹如何用 eBPF 完全零侵?jǐn)_的實(shí)現(xiàn)應(yīng)用拓?fù)涞挠?jì)算;第三部分介紹如何利用 eBPF 實(shí)現(xiàn)進(jìn)程、資源、服務(wù)等標(biāo)簽的注入,使得開發(fā)、運(yùn)維、運(yùn)營(yíng)都能從自己熟悉的視角查看這個(gè)拓?fù)?;最后第四部分介紹一個(gè)全鏈路壓測(cè)的 Demo 和我們?cè)诳蛻籼幍膸讉€(gè)實(shí)戰(zhàn)案例。
01|傳統(tǒng)解決方案的問題
首先我們知道,在應(yīng)用拓?fù)渲?,?jié)點(diǎn)可以展示為不同的粒度,例如按服務(wù)、按實(shí)例、按應(yīng)用等不同粒度展現(xiàn)。例如對(duì)于服務(wù)粒度,一個(gè)節(jié)點(diǎn)代表一個(gè)服務(wù),它由多個(gè)實(shí)例組成。節(jié)點(diǎn)之間的連線代表調(diào)用關(guān)系,通常也對(duì)應(yīng)了一系列的指標(biāo),表示服務(wù)之間調(diào)用的吞吐、異常、時(shí)延等性能。在云原生時(shí)代,云基礎(chǔ)設(shè)施、微服務(wù)拆分等因素會(huì)帶來很多挑戰(zhàn),導(dǎo)致繪制一個(gè)全景的應(yīng)用拓?fù)渥兊梅浅FD難。
傳統(tǒng)的解決方案我們很熟悉,通過埋點(diǎn)、插碼的方式,修改代碼來輸出類似于 Span 的調(diào)用信息,通過聚合 Span 得到應(yīng)用拓?fù)?。即使是采用自?dòng)化的 Java 字節(jié)碼注入等技術(shù),雖然看起來不用改代碼,但還是要修改啟動(dòng)參數(shù),意味著服務(wù)要重新發(fā)布,至少要重啟服務(wù)進(jìn)程。
插碼方案的困難 – 難以升級(jí)
在云原生場(chǎng)景下,使用這樣的方法來獲取應(yīng)用拓?fù)渥兊酶永щy。隨著服務(wù)拆得越來越微小,每個(gè)服務(wù)的開發(fā)人員的自由度變得越來越大,因此可能會(huì)出現(xiàn)各種新奇的語言、框架。相對(duì)于 Java agent 而言,其他語言基本都會(huì)涉及到代碼修改、重編譯重發(fā)布。當(dāng)然一般我們都會(huì)將這部分邏輯實(shí)現(xiàn)在一個(gè) SDK 中,然而 SDK 的升級(jí)也是一個(gè)痛苦甚至絕望的過程,業(yè)務(wù)方都不愿意升級(jí),升級(jí)意味著發(fā)版,發(fā)版可能就意味著故障。
那有方法能避免修改代碼嗎?云原生時(shí)代我們還有一個(gè)選項(xiàng) —— 使用服務(wù)網(wǎng)格(Service Mesh)。例如 Istio 在 K8s 及非容器環(huán)境下可構(gòu)建一個(gè)服務(wù)網(wǎng)絡(luò)。
網(wǎng)格方案的困難 – 難以全覆蓋
但服務(wù)網(wǎng)格的問題在于并不能覆蓋所有協(xié)議,例如 Istio 主要覆蓋 HTTP/gRPC,而其他大量各種各樣的 Protobuf/Thrift RPC,以及 MySQL/Redis/Kafka/MQTT 等中間件訪問都無法覆蓋到。另外從因果關(guān)系來講,我們可以因?yàn)檫x擇了服務(wù)網(wǎng)格而順帶實(shí)現(xiàn)一部分可觀測(cè)性,但肯定不會(huì)因?yàn)橐?shí)現(xiàn)可觀測(cè)性而引入服務(wù)網(wǎng)格這樣一個(gè)帶有侵入性的技術(shù)。
除此之外,在云原生時(shí)代,即使我們?nèi)ジ臉I(yè)務(wù)代碼、上服務(wù)網(wǎng)格,仍然無法獲取到一個(gè)完整的應(yīng)用拓?fù)洹1热缭苹A(chǔ)設(shè)施中的 Open vSwitch,K8s 中的 IPVS,Ingress 位置的 Nginx 網(wǎng)關(guān),這些都是看不到的。
下面出場(chǎng)的就是我們今天要講的主角,eBPF,它是一個(gè)非?;馃岬男录夹g(shù)。我們來看看它是不是能解決我們今天聚焦的問題,能否將應(yīng)用拓?fù)洚嬋?、畫?zhǔn),能為開發(fā)、運(yùn)維等不同團(tuán)隊(duì)的人提供一個(gè)統(tǒng)一的視圖,消除他們的 Gap。我們知道 eBPF 有很多很好的特性,它不需要業(yè)務(wù)改代碼、不需要服務(wù)修改啟動(dòng)參數(shù)、不需要重啟服務(wù)進(jìn)程,它是編程語言無關(guān)的,而且能覆蓋到云基礎(chǔ)設(shè)施和云原生應(yīng)用的整個(gè)技術(shù)棧。DeepFlow 基于 eBPF 技術(shù)也享受到了很多這方面的紅利,我們的客戶做 POC、社區(qū)的用戶試用都非常絲滑,不用去考慮運(yùn)維窗口、實(shí)施周期,DeepFlow 的社區(qū)版只需一條命令,五分鐘即可開啟全景、全棧的可觀測(cè)性。
DeepFlow 軟件架構(gòu)
這里也用一頁(yè) PPT 來簡(jiǎn)單介紹一下 DeepFlow 社區(qū)版的軟件架構(gòu)。我們開源至今還不到一年,目前在社區(qū)受到了不少關(guān)注。上圖中間藍(lán)色的部分是 DeepFlow 的兩個(gè)主要組件:Agent 負(fù)責(zé)采集數(shù)據(jù),利用 eBPF 的零侵?jǐn)_特性,覆蓋各種各樣的云原生技術(shù)棧;Server 負(fù)責(zé)富化數(shù)據(jù),將標(biāo)簽與數(shù)據(jù)關(guān)聯(lián),并存儲(chǔ)至實(shí)時(shí)數(shù)倉(cāng)中。在北向,DeepFlow 提供 SQL、PromQL、OTLP 等接口,通過 DeepFlow 自己的 GUI 展現(xiàn)所有可觀測(cè)性數(shù)據(jù),也兼容 Grafana、Prometheus、OpenTelemetry Collector 等生態(tài)。在南向,DeepFlow 可集成 Prometheus 等指標(biāo)數(shù)據(jù)、SkyWalking 等追蹤數(shù)據(jù)、Pyrosope 等 Profile 數(shù)據(jù)。
AutoTracing AutoTagging
今天我們要聚焦的一點(diǎn),就是 DeepFlow 如何使用 eBPF 生成全景應(yīng)用拓?fù)?。DeepFlow 的兩個(gè)核心能力 AutoTracing 和 AutoTagging 為應(yīng)用拓?fù)涞纳傻於藞?jiān)實(shí)的基礎(chǔ)。在沒有任何代碼修改、不重啟任何業(yè)務(wù)進(jìn)程的情況下,我們實(shí)現(xiàn)了全景應(yīng)用拓?fù)涞睦L制。首先,我們可通過 eBPF 從網(wǎng)絡(luò)包、內(nèi)核 Socket Data 中獲取原始比特流;接下來我們分析 Raw Data 中的 IP、端口等信息,使用 eBPF 將原始數(shù)據(jù)與進(jìn)程、資源、服務(wù)等信息關(guān)聯(lián),以便繪制不同團(tuán)隊(duì)不同視角的應(yīng)用拓?fù)?,這也是今天分享的第三部分;然后我們會(huì)從原始數(shù)據(jù)中提取應(yīng)用調(diào)用協(xié)議,聚合調(diào)用中的請(qǐng)求和響應(yīng),計(jì)算調(diào)用的吞吐、時(shí)延和異常,以及其他系統(tǒng)和網(wǎng)絡(luò)性能指標(biāo);最后,基于這類 Request Scope 的 Span 數(shù)據(jù),我們可以聚合生成全景應(yīng)用拓?fù)?,也可以通過關(guān)聯(lián)關(guān)系的計(jì)算生成分布式追蹤火焰圖。
今天我們主要講的是應(yīng)用拓?fù)涞睦L制,對(duì)于使用 eBPF 實(shí)現(xiàn)全自動(dòng)的分布式追蹤這個(gè)話題,DeepFlow 社區(qū)的博客 https://deepflow.io/blog 上有很多資料。
02|eBPF 零侵?jǐn)_計(jì)算全景應(yīng)用拓?fù)?/h1>
Universal Application Topology
首先讓我們看一個(gè)效果圖,這是 DeepFlow 展示的一個(gè)全景應(yīng)用拓?fù)?。這個(gè)拓?fù)淇赡艽蠹冶容^熟悉,它是 OpenTelemetry 的一個(gè) Demo,它 Fork 自 Google Cloud Platform 的一個(gè)項(xiàng)目,它是一個(gè)小型的電商應(yīng)用。從圖中可以看到,DeepFlow 可以獲取所有服務(wù)之間的調(diào)用關(guān)系以及相應(yīng)的性能指標(biāo),這都是通過 eBPF 實(shí)現(xiàn)的,沒有做任何的代碼修改和進(jìn)程重啟,展現(xiàn)這些結(jié)果之前我們關(guān)閉了所有 OTel Instrumentation。
在這一節(jié),我們將聚焦四個(gè)問題:第一個(gè)問題是如何采集原始數(shù)據(jù);第二個(gè)問題是如何解析應(yīng)用協(xié)議,eBPF做了什么;第三個(gè)問題是如何計(jì)算全棧性能指標(biāo);第四個(gè)問題是如何適配低版本內(nèi)核,許多用戶的內(nèi)核版本可能是3.10。
數(shù)據(jù)采集:DeepFlow 同時(shí)用到了 eBPF 和它的前身,有 30 年歷史的 Classic BPF(cBPF)。在云原生環(huán)境中,應(yīng)用進(jìn)程運(yùn)行在容器 Pod 內(nèi),容器可能存在于多個(gè)節(jié)點(diǎn)上,并通過網(wǎng)關(guān)連接到數(shù)據(jù)庫(kù)等其他服務(wù)。我們可以使用 eBPF 技術(shù)來覆蓋所有的端節(jié)點(diǎn),并使用 cBPF 覆蓋所有的中間轉(zhuǎn)發(fā)節(jié)點(diǎn)。從應(yīng)用進(jìn)程(端節(jié)點(diǎn))層面,我們使用 kprobe 和 tracepoint 覆蓋所有的 TCP/UDP socket 讀寫操作;并使用 uprobe 來掛載到應(yīng)用程序內(nèi)的核心函數(shù)上,比如 OpenSSL/Golang 的 TLS/SSL 函數(shù),來獲取加密或壓縮之前的數(shù)據(jù)。除此之外,在兩個(gè)服務(wù)之間還有許多中間轉(zhuǎn)發(fā)路徑,例如 IPVS、iptables、OvS 等,此時(shí)需要使用 cBPF 來獲取網(wǎng)絡(luò)包數(shù)據(jù),因?yàn)檫@部分流量不會(huì)有用戶態(tài)應(yīng)用程序去讀寫,會(huì)直接在內(nèi)核里查表轉(zhuǎn)發(fā)。
eBPF Probes
這里列舉了 DeepFlow 中主要使用到的 eBPF Probe,包括最左邊的 kprobe,以及中間最高性能的 tracepoint,以及最右邊解決加密和壓縮數(shù)據(jù)的 uprobe。其中 tracepoint 滿足了 DeepFlow 中的絕大多數(shù)需求。
協(xié)議解析:在獲取原始數(shù)據(jù)方面,我們使用 eBPF 和 cBPF 來捕獲字節(jié)流,但此時(shí)我們無法看到任何可理解的信息。接下來,我們要做的就是從這些字節(jié)流中提取出我們關(guān)心的應(yīng)用協(xié)議。大多數(shù)協(xié)議都是明文的,例如 HTTP、RPC、SQL 等,我們可以直接遵照協(xié)議規(guī)范來解析它們的內(nèi)容。對(duì)于一些特殊的協(xié)議,例如 HTTP2 之類的壓縮協(xié)議,我們需要進(jìn)行更復(fù)雜的處理。我們可以使用 tracepoint 或 kprobe 來提取未壓縮的字段,但此時(shí)對(duì)于已經(jīng)壓縮的頭部字段,還原它們是一項(xiàng)困難的工作,因此我們會(huì)選擇使用 uprobe 作為補(bǔ)充來采集壓縮協(xié)議的數(shù)據(jù)。另外對(duì)于加密流量也無法從內(nèi)核函數(shù)中獲取明文,我們通過 uprobe 直接獲取加密之前的數(shù)據(jù)。最后,對(duì)于私有協(xié)議,它們沒有可遵循的通用規(guī)范,或者雖然遵循了 Protobuf/Thrift 等標(biāo)準(zhǔn)但無法提供協(xié)議定義文件,此時(shí)我們提供了基于 WASM 的插件化的可編程接口,在未來也有計(jì)劃提供基于 LUA 的插件機(jī)制。
在協(xié)議解析層面還需要考慮的一個(gè)事情是流重組。例如一個(gè) HTTP2/gRPC 請(qǐng)求的頭部字段可能會(huì)分為多次系統(tǒng)調(diào)用讀寫,或者分拆為多個(gè)網(wǎng)絡(luò)包收發(fā),此時(shí)我們需要還原完整請(qǐng)求或響應(yīng)頭,一遍重組一遍嘗試解析。
eBPF WASM
特別提一下我們對(duì)私有協(xié)議的識(shí)別,通過結(jié)合 eBPF 和 WebAssembly 的能力,提供一個(gè)靈活的插件機(jī)制。插件在 DeepFlow 中解決兩個(gè)問題:提供對(duì)私有協(xié)議的解析能力、提供對(duì)任意協(xié)議的業(yè)務(wù)字段解析能力。商用軟件供應(yīng)商或業(yè)務(wù)開發(fā)人員可以輕松添加自定義插件來解析協(xié)議,將可觀測(cè)性從 IT 層面提升到業(yè)務(wù)層面。這樣的方式使得我們可以自定義提取出一些業(yè)務(wù)指標(biāo)或業(yè)務(wù)標(biāo)簽,例如訂單量、用戶 ID、交易 ID、車機(jī) ID 等信息。這些業(yè)務(wù)信息對(duì)于不同的業(yè)務(wù)場(chǎng)景都是獨(dú)特的,我們將這個(gè)靈活性給到了業(yè)務(wù)方,使得業(yè)務(wù)方可以快速實(shí)現(xiàn)零侵?jǐn)_的可觀測(cè)性。
RED Metrics
性能指標(biāo):我們可以通過數(shù)請(qǐng)求和響應(yīng)的個(gè)數(shù)來獲取吞吐量,同時(shí)還可以根據(jù)響應(yīng)狀態(tài)碼、耗時(shí)等信息計(jì)算 Error 和 Delay 指標(biāo)。吞吐和異常的計(jì)算相對(duì)簡(jiǎn)單,只需要基于單個(gè)請(qǐng)求或單個(gè)響應(yīng)進(jìn)行處理即可。最困難的是對(duì)時(shí)延的計(jì)算,我們需要使用 eBPF 技術(shù)來關(guān)聯(lián)請(qǐng)求和響應(yīng)這兩個(gè)動(dòng)作。這個(gè)過程又分為兩步:從 Socket/Packet Data 中基于 <sip, dip, sport, dport, protocol> 五元組聚合出 TCP/UDP Flow;然后在 Flow 上下文中匹配應(yīng)用協(xié)議的每一個(gè) Request 和 Response。而對(duì)于第二步,一個(gè) Flow 中一般會(huì)有多個(gè)請(qǐng)求和響應(yīng),匹配邏輯我們以 HTTP 協(xié)議為例解釋:
- 對(duì)于串行協(xié)議如 HTTP 1.0,直接匹配臨近的請(qǐng)求和響應(yīng)即可
- 對(duì)于并發(fā)協(xié)議如 HTTP 2.0,需要提取協(xié)議頭中的 StreamID 進(jìn)行請(qǐng)求和響應(yīng)的配對(duì)
- 還有一種特殊情況即 HTTP 1.1 中的管道機(jī)制,他是一種偽并發(fā)協(xié)議,我們可以利用它的 FIFO 特點(diǎn)完成請(qǐng)求和響應(yīng)的配對(duì)
Network Metrics
但是,在云原生環(huán)境下,僅僅只統(tǒng)計(jì)應(yīng)用層 RED 往往不夠。例如我們會(huì)發(fā)現(xiàn)客戶端側(cè)看到的時(shí)延是 3 秒,而服務(wù)端側(cè)看到的時(shí)延是 3 毫秒;或者客戶端和服務(wù)端側(cè)都看不到有請(qǐng)求,但下游服務(wù)卻報(bào)錯(cuò)了。針對(duì)這些場(chǎng)景,我們基于 Flow 數(shù)據(jù)計(jì)算得到了網(wǎng)絡(luò)層面的吞吐、異常、時(shí)延。業(yè)務(wù)開發(fā)發(fā)現(xiàn)時(shí)延高時(shí),可以快速查看網(wǎng)絡(luò)層時(shí)延來判斷到底是業(yè)務(wù)自身的問題還是基礎(chǔ)設(shè)施問題;另外在發(fā)現(xiàn)請(qǐng)求報(bào)錯(cuò)時(shí)可以快速查看是否建連或者傳輸異常了。
針對(duì)時(shí)延我們分的更細(xì)致,通過從 Packet Data 中重建 TCP 狀態(tài)機(jī)來更加精細(xì)的展現(xiàn)各個(gè)層面引入的時(shí)延。在生產(chǎn)環(huán)境中我們會(huì)發(fā)現(xiàn)高時(shí)延的原因一般分為幾個(gè)方面:
- 建連慢:由于防火墻、負(fù)載均衡、網(wǎng)關(guān)的影響,導(dǎo)致 TCP 建連過程慢,這一過程又進(jìn)一步拆分為了到底是客戶側(cè)建連慢還是服務(wù)側(cè)建連慢
- 框架慢:由于業(yè)務(wù)代碼在建連后的慢處理,客戶端在建連后等待了一段時(shí)間才發(fā)送請(qǐng)求,這段等待時(shí)間我們會(huì)刻畫為客戶端等待時(shí)延,它能夠方面定位到底是框架/庫(kù)層面的問題,還是業(yè)務(wù)代碼的問題
- 系統(tǒng)慢:由于操作系統(tǒng)處理不及時(shí),例如系統(tǒng)負(fù)載高等,對(duì)請(qǐng)求的 TCP ACK 回復(fù)慢,從而導(dǎo)致 TCP 無法高效增大窗口大小
- 傳輸慢:網(wǎng)絡(luò)重傳、零窗等也是導(dǎo)致高時(shí)延的一些可能原因
在云原生環(huán)境下,還有一個(gè)特點(diǎn)是網(wǎng)絡(luò)路徑非常長(zhǎng),例如兩個(gè)服務(wù)之間的通信可能依次經(jīng)過容器 Pod 網(wǎng)卡、容器 Node 網(wǎng)卡、KVM 主機(jī)網(wǎng)卡等。路徑的復(fù)雜性也是引發(fā)傳輸慢的主要原因。由于 DeepFlow 同時(shí)使用 cBPF,因此可以從所有中間轉(zhuǎn)發(fā)路徑中觀測(cè)到應(yīng)用層和網(wǎng)絡(luò)層的時(shí)延,然后通過對(duì)比逐跳時(shí)延來定位到底是哪一跳開始出現(xiàn)了問題。
System Metrics
除了網(wǎng)絡(luò)層面以外,造成慢調(diào)用的還有一個(gè)重要原因是 IO 慢。例如 Client 訪問 DB 時(shí)可能出現(xiàn)時(shí)延抖動(dòng),而這些抖動(dòng)通常是由 DB 的文件讀寫慢導(dǎo)致。再例如大數(shù)據(jù)場(chǎng)景下一批 Worker 中可能總有那么一兩個(gè) Worker 完成的慢,排查后會(huì)發(fā)現(xiàn)通常是由于文件讀寫慢導(dǎo)致。這里 DeepFlow 的做法是觀測(cè)所有與應(yīng)用調(diào)用相關(guān)的 IO 時(shí)延,并記錄所有的慢 IO 事件。實(shí)現(xiàn)層面,文件 IO 與 Socket IO 事件可通過 tracepoint/kprobe hook 同樣一組函數(shù)獲取到,通過 FD 的類型可以進(jìn)行區(qū)分。依靠這樣的能力,我們能快速的定位到 deepflow-server 寫入 clickhouse-server 偶發(fā)性慢是由于文件 IO 時(shí)延抖動(dòng)造成,且能定位到具體的文件路徑。
低版內(nèi)核:最后我們總結(jié)一下本節(jié)。我們希望在低版本內(nèi)核中 DeepFlow 也能展現(xiàn)更多的能力。以 Kernel 4.14 為界,DeepFlow 在 4.14 的環(huán)境下可以基于 eBPF 實(shí)現(xiàn)全功能,包括加密數(shù)據(jù)的觀測(cè)、應(yīng)用進(jìn)程的性能指標(biāo)、系統(tǒng)層面文件 IO 性能指標(biāo),以及全自動(dòng)的分布式追蹤能力。而在 4.14 以下的內(nèi)核環(huán)境中,我們基于 cBPF 也實(shí)現(xiàn)了大部分的能力,包括對(duì)于明文協(xié)議、私有協(xié)議的觀測(cè),對(duì)于壓縮協(xié)議部分頭部字段的觀測(cè),以及對(duì)于應(yīng)用性能、網(wǎng)絡(luò)性能指標(biāo)的采集,對(duì)于性能指標(biāo)的全棧路徑追蹤。
03|eBPF 自動(dòng)關(guān)聯(lián)服務(wù)和資源標(biāo)簽
講到這里實(shí)際上我們整個(gè)目標(biāo)只完成了一半。我們從原始數(shù)據(jù)中提取出來了指標(biāo)、調(diào)用關(guān)系、調(diào)用鏈,但還需要以開發(fā)者熟悉的方式展現(xiàn)出來。Packet 和 Socket Data 中只有 IP 和端口號(hào)信息,這是開發(fā)和運(yùn)維都無法理解的。我們希望所有的數(shù)據(jù)都能從實(shí)例、服務(wù)、業(yè)務(wù)等維度按需展示。
在這個(gè)問題上我們也遇到了一些挑戰(zhàn):我會(huì)先介紹從哪里采集標(biāo)簽,以及采集什么樣的標(biāo)簽;然后討論輪詢采集的方式會(huì)帶來哪些問題;接下來我會(huì)介紹一下基于 eBPF 的事件觸發(fā)的方案,來避免輪詢的缺陷;最后同樣的也會(huì)介紹一下我們?cè)诘桶姹緝?nèi)核環(huán)境下的一些能力。
標(biāo)簽數(shù)據(jù):首先僅通過 IP 地址是不能完整關(guān)聯(lián)客戶端和服務(wù)端的服務(wù)信息的,這是因?yàn)閷?shí)際環(huán)境中普遍存在 NAT,包括 SNAT、DNAT、FULLNAT 等。實(shí)際上通過單側(cè)的 IP Port 也難以準(zhǔn)確關(guān)聯(lián),這是因?yàn)?Client Port Reuse 也是一個(gè)普遍存在的現(xiàn)象。因此,在 DeepFlow 中使用五元組來將通信端點(diǎn)關(guān)聯(lián)至服務(wù)。具體來講,我們會(huì)通過 K8s apiserver 獲取 IP 對(duì)應(yīng)的容器資源標(biāo)簽;通過 CMDB 及腳本化的插件獲取 PID 對(duì)應(yīng)的業(yè)務(wù)標(biāo)簽信息;然后再依靠下面要講的一些機(jī)制將 IP Port 的五元組與 PID 關(guān)聯(lián)起來,最終實(shí)現(xiàn)資源、服務(wù)、業(yè)務(wù)標(biāo)簽的自動(dòng)注入。
輪詢方案:我們首先能想到的是通過輪詢 /proc/pid/net/ 文件夾來獲取 PID 與 Socket 五元組的關(guān)聯(lián)。每個(gè) agent 獲取本機(jī)的關(guān)聯(lián)信息,并通過 server 交換得到全局的關(guān)聯(lián)信息,從而使得每個(gè) agent 能獨(dú)立的為客戶端和服務(wù)端標(biāo)注雙端的 PID 信息。
輪詢方案也會(huì)碰到一些挑戰(zhàn),例如 LVS 場(chǎng)景下,/proc 下的 Socket 信息指向的是 LVS,但從業(yè)務(wù)上來講我們希望獲取 LVS 背后的 RS 的進(jìn)程信息。DeepFlow 通過同步 LVS 轉(zhuǎn)發(fā)規(guī)則解決這個(gè)問題。具體來講,基于 LVS 規(guī)則和包頭中嵌入的 TOA(TCP Option Address)字段,可以快速的在 RS 上定位客戶端的 PID,并通過 LVS 轉(zhuǎn)發(fā)規(guī)則快速的在客戶端側(cè)定位服務(wù)端的 PID。
觸發(fā)方案:當(dāng)時(shí)輪詢總會(huì)存在時(shí)間間隔,因此永遠(yuǎn)無法解決短連接的監(jiān)控問題。在這方面 DeepFlow 也做了一些工作,這就是下面我們要介紹的觸發(fā)式方案。
受 TOA 的啟發(fā),我們基于 eBPF 實(shí)現(xiàn)了一個(gè) TOT(TCP Option Tracing)的機(jī)制,即在 TCP 包頭 Option 字段中嵌入額外的信息,表示發(fā)送方的 IP 和 PID。這樣的話我們就能將源發(fā)的進(jìn)程信息告知對(duì)端了。為了實(shí)現(xiàn)這樣的機(jī)制,我們使用 eBPF sockops 和 tracepoint,一共 Hook 了五個(gè)函數(shù)。我們會(huì)在每個(gè) TCP SYN、SYN-ACK 包中注入 TOT 信息,使得連接新建時(shí)即能標(biāo)記進(jìn)程信息。同時(shí)也會(huì)概率性的抽取 TCP PSH 包注入 TOT,使得對(duì)于長(zhǎng)連接,即使 agent 的啟動(dòng)時(shí)間滯后于業(yè)務(wù)進(jìn)程,也能及時(shí)獲取到進(jìn)程信息。
低版內(nèi)核:同樣這里也分享一下我們?cè)诘桶姹緝?nèi)核上做的一些工作。首先輪詢的方案依靠掃描 /proc 文件夾實(shí)現(xiàn),因此是能適配所有 2.6 的內(nèi)核環(huán)境的。在 3.10 及以上的內(nèi)核中,我們也提供了一個(gè)精巧的 ko 模塊來實(shí)現(xiàn) TOT 的注入,這個(gè)內(nèi)核模塊僅有 227 行代碼,我們也進(jìn)行了開源,歡迎大家使用。
04|Demo – 持續(xù)觀測(cè)全鏈路壓測(cè)性能瓶頸
OTel Demo
最后通過一個(gè) Demo 介紹一下這些工作的效果。我們?nèi)匀皇且?OTel 的電商 Demo 為例,仍然是關(guān)閉了其中的所有 OTel Instrumentation。選擇這個(gè) Demo 的理由在于,他是一個(gè)典型的微服務(wù)架構(gòu)的應(yīng)用,且盡力模擬了比較真實(shí)的電商場(chǎng)景,微服務(wù)的實(shí)現(xiàn)語言涵蓋了十二種之多,也包含了 PostgreSQL、Redis、Kafka、Envoy 等中間件。
首先我們可以看到,不修改代碼、不修改啟動(dòng)參數(shù)、不重啟進(jìn)程,我們已經(jīng)能自動(dòng)繪制微服務(wù)粒度的全景應(yīng)用。當(dāng)我們注入一個(gè) 1.5K QPS 的壓力時(shí),能清晰的在拓?fù)渲锌吹狡款i鏈路。沿著 frontend 服務(wù)一路往下,也能快速的定位瓶頸服務(wù) ProductCatalog。接下來我們分為三次,分別將 ProductCatalog 擴(kuò)容至 2x、4x、8x 副本數(shù),擴(kuò)容的過程中也可清晰的看到瓶頸鏈路在逐漸消失,知道最終所有服務(wù)的時(shí)延恢復(fù)正常。
除了這個(gè) Demo 意外,這里也分享幾個(gè)我們?cè)诳蛻籼幍膶?shí)戰(zhàn)案例:
- 某造車新勢(shì)力客戶,使用 DeepFlow 從數(shù)萬 Pod 中在 5 分鐘內(nèi)定位 RDS 訪問量最大的 Pod、所屬服務(wù)、負(fù)責(zé)該服務(wù)的團(tuán)隊(duì)。
- 某股份制銀行客戶,信用卡核心業(yè)務(wù)上線受阻,壓測(cè)性能上不去,使用 DeepFlow 在 5 分鐘內(nèi)發(fā)現(xiàn)兩個(gè)服務(wù)之間 API 網(wǎng)關(guān)是性能瓶頸,進(jìn)而發(fā)現(xiàn)緩存設(shè)置不合理。
- 某互聯(lián)網(wǎng)客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位服務(wù)間 K8s CNI 性能瓶頸,識(shí)別由于環(huán)路造成的某個(gè)服務(wù)上下云訪問時(shí)延周期性飆升的問題,云廠商兩周無解。
- 某證券客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位 ARP 故障導(dǎo)致 Pod 無法上線的問題,「瞬間」結(jié)束業(yè)務(wù)、系統(tǒng)、網(wǎng)絡(luò)多部門「會(huì)商」。
- 某基礎(chǔ)設(shè)施軟件客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位 Rust 客戶端使用 Tokio 不合理導(dǎo)致的 gRPC 偶發(fā)性超大時(shí)延,終結(jié)了 QA 及多個(gè)開發(fā)團(tuán)隊(duì)之間踢來踢去的 Bug。
- 某四大行客戶,使用 DeepFlow 在 5 分鐘內(nèi)定位某個(gè) NFVGW 實(shí)例對(duì)特定服務(wù)流量不轉(zhuǎn)發(fā)導(dǎo)致的客戶端頻繁重試。
從這些案例中我們能發(fā)現(xiàn),依靠 eBPF 技術(shù)的零侵?jǐn)_特性,我們能完整的覆蓋應(yīng)用的全景調(diào)用拓?fù)洌夷苷宫F(xiàn)任意調(diào)用路徑中的全棧性能指標(biāo)。得益于這些可觀測(cè)性能力,我們能快速的定位 RDS、API 網(wǎng)關(guān)、K8s CNI、ARP 故障、Rust Tokio 庫(kù)、NFVGW 造成的故障。
Distributed Profile
這是最后一張PPT,我想和大家分享一下 DeepFlow 對(duì)可觀測(cè)性的更多思考。在傳統(tǒng) APM 中,我們通常使用 Span 之間的關(guān)聯(lián)關(guān)系以火焰圖的方式展現(xiàn)一次分布式調(diào)用(Trace),也會(huì)講所有的 Span 聚合為指標(biāo)來展現(xiàn)所有服務(wù)之間的應(yīng)用拓?fù)?。我們發(fā)現(xiàn)拓?fù)湔宫F(xiàn)了所有調(diào)用的數(shù)據(jù),而追蹤展現(xiàn)了一個(gè)調(diào)用的數(shù)據(jù),它們二者恰恰是取了兩個(gè)極端。DeepFlow 目前正在探索,中間是否有一個(gè)折中的點(diǎn),他們展示一組聚合的火焰圖或拓?fù)鋱D,有點(diǎn)類似于單個(gè)進(jìn)程的 Profile,但是用于分布式應(yīng)用的場(chǎng)景,我們稱它為 Distributed Profile。我們相信這樣的折中會(huì)帶來效率的提升,它不香所有調(diào)用聚合而成的拓?fù)洌瑫?huì)有太多噪聲;也不像單一請(qǐng)求展示出來的 Trace,問題排查需要一個(gè)一個(gè) Trace 的看。
而 eBPF,正是絕佳的實(shí)現(xiàn) Distributed Profile 的技術(shù)手段,請(qǐng)期待后續(xù)我們進(jìn)一步的分享。
05|什么是 DeepFlow
DeepFlow[3] 是一款開源的高度自動(dòng)化的可觀測(cè)性平臺(tái),是為云原生應(yīng)用開發(fā)者建設(shè)可觀測(cè)性能力而量身打造的全棧、全鏈路、高性能數(shù)據(jù)引擎。DeepFlow 使用 eBPF、WASM、OpenTelemetry 等新技術(shù),創(chuàng)新的實(shí)現(xiàn)了 AutoTracing、AutoMetrics、AutoTagging、SmartEncoding 等核心機(jī)制,幫助開發(fā)者提升埋點(diǎn)插碼的自動(dòng)化水平,降低可觀測(cè)性平臺(tái)的運(yùn)維復(fù)雜度。利用 DeepFlow 的可編程能力和開放接口,開發(fā)者可以快速將其融入到自己的可觀測(cè)性技術(shù)棧中。
GitHub 地址:https://github.com/deepflowio/deepflow
訪問 DeepFlow Demo[4],體驗(yàn)高度自動(dòng)化的可觀測(cè)性新時(shí)代。
參考資料
[1]回看鏈接: https://www.bilibili.com/video/BV1B14y1f7UB/
[2]PPT下載: http://yunshan-guangzhou.oss-cn-beijing.aliyuncs.com/yunshan-ticket/pdf/1a1847b242ae7c16d53f0af31d9c6aa4_20230509140119.pdf
[3]DeepFlow: https://github.com/deepflowio/deepflow
[4]DeepFlow Demo: https://deepflow.yunshan.net/docs/zh/install/overview/