分佈式鏈路追蹤在字節跳動的實踐
綜述
字節跳動在發展過程中,逐漸形成了十分複雜的超大規模微服務體系,對後端整體的可觀測性解決方案提出了極高的要求。爲了解決這個問題,基礎架構智能運維團隊自研鏈路追蹤系統,將海量 Metrics/Trace/Log 數據進行整合與統一,並在此基礎上實現了新一代的一站式全鏈路觀測診斷平臺,幫助業務解決監控排障、鏈路梳理、性能分析等問題。本文將會介紹字節跳動鏈路追蹤系統的整體功能和技術架構,以及實踐過程中我們的思考與總結。
什麼是分佈式鏈路追蹤 (Trace) ?
M T L 的關係
可觀測性的三大基礎數據是 Metrics / Log / Trace。說到這三大件,可能大家會想到當需要監控變化趨勢和配置告警時就去用 Metrics;當需要細查問題時去查 log;對於微服務數量較多的系統,還得有 Trace,Trace 也可以看做一種標準結構化的 log,記錄了很多固定字段,例如上下游調用關係和耗時,查看上下游調用關係或者請求耗時在鏈路各節點上的分佈可以查看 Trace。
但是如果帶着孤立的觀點去用這些數據的話,數據的價值會大大降低。舉例一個常見的場景,通過 Metrics 得知某服務 SLA 降低,錯誤率上升,怎麼去排查根因呢?先去找錯誤日誌吧,可是我看到的錯誤日誌是不是真的和這個錯誤率上升有關係呢?得翻翻代碼看看這些錯誤日誌都是哪裏打出來的,代表什麼意思。再去找找有沒有錯誤 Trace?找出來的 Trace 也不太確定是不是和這個錯誤率上升有關係,還是得看代碼確認下。終於通過一行行的代碼和數據比對,確認到這個錯誤是下一層服務返回給我的,把那個服務的負責人拉進來一起排查吧,然後這個羣越拉越大,更多的人被拖進來一層一層地查下去,最終定位到是某個底層服務上線了一個變更導致 Panic,錯誤層層向上傳播導致服務 SLA 降低。
這個過程很不美好,需要工程師理解每一項數據的底層邏輯,才能充分利用它們去解決具體問題。而在複雜的大規模微服務系統中,沒有單個工程師能夠做到熟悉每一個微服務的底層邏輯,因此複雜微服務系統的排障和觀測往往是一項有挑戰的困難工作。
Trace 是數據的鏈接紐帶
如果所有微服務的監控數據都是遵循統一模型和語義規範並且天生高度關聯的呢?
在軟件系統中,每秒鐘有無數的 Context 在流動。這些 Context 可能是一個實時在線請求,也可能是一個異步處理任務。每個 Context 都會在多個微服務節點中持續傳播才能最終完成。所有的監控數據(包括 Metric, Log 等)都源自於某一個 Context。Trace 就是這個 Context 的數據載體,通過標準化的數據模型,記錄 Context 在多個微服務中的全部執行過程,並沿途關聯上此 Context 上發生的所有事件(包括 Metric, Log 等)。
再回到剛纔那個 Case,當我們對某個 Metric 波動發生興趣時,可以直接將造成此波動的 Trace 關聯檢索出來,然後查看這些 Trace 在各個微服務中的所有執行細節,發現是底層某個微服務在執行請求過程中發生了 Panic,這個錯誤不斷向上傳播導致了服務對外 SLA 下降。如果可觀測平臺做得更完善一些,將微服務的變更事件數據也呈現出來,那麼一個工程師就可以快速完成整個排障和根因定位的過程,甚至不需要人,通過機器就可以自動完成整個排障和根因定位過程。
Trace 不僅僅是用來查看耗時分佈甘特圖的工具,也是海量監控數據的 Context 鏈接紐帶。基於可靠關聯的 Metric / Trace / Log 數據,也構建出強大的可觀測性能力,回答監控排障、SLO 調優、架構梳理、流量估算、智能化故障歸因等衆多複雜問題。
Trace 的採集以及跨服務進程的 Context 傳遞一般是由微服務框架等基礎設施自動完成的,但是要實現最佳效果也需要所有研發工程師的理解和配合。研發工程師在編碼的過程中應當有意識地在所有代碼執行過程中持續傳遞 Context。比如在 Golang 中,context.Context 需要在所有函數調用中作爲參數持續傳遞;在 Java 中,一般默認用 Threadlocal 作爲 Context 的存儲載體,但是如果有多線程或者異步的場景,則需要開發者自行對 Context 進行顯式的傳遞,否則上下文就斷了,難以實現有效的追蹤和監控。
字節鏈路追蹤系統的挑戰與機遇
字節跳動在發展過程中,逐漸形成了十分複雜的超大規模微服務體系,對後端整體的可觀測性解決方案提出了極高的要求。
我們面臨的挑戰包括:
-
線上流量巨大
-
微服務數量巨大,調用關係複雜,迭代變化快
-
研發團隊龐大,分工複雜
目前字節跳動有巨大的流量,衆多的活躍微服務、容器實例數,以及龐大的研發團隊。一個複雜業務鏈路動輒涉及數百個微服務,有一線業務,有中臺,也有基礎設施,不同微服務由不同的研發團隊開發,同時還有各類橫向團隊負責整體架構的質量、穩定性、安全和效率等相關工作。不同團隊對鏈路追蹤系統都會有不一樣的訴求。
同時我們也有着難得的機遇:
-
微服務框架高度統一
-
微服務高度容器化,環境統一
-
存儲 / 中間件等基礎設施高度統一
得益於長期的統一基建工作,字節全公司範圍內的所有微服務使用的底層技術方案統一度較高。絕大部分微服務都部署在公司統一的容器平臺上,採用統一的公司微服務框架和網格方案,使用公司統一提供的存儲組件及相應 SDK。高度的一致性對於基礎架構團隊建設公司級別的統一鏈路追蹤系統提供了有利的基礎。
字節鏈路追蹤系統的目標
面對這樣的現狀,字節鏈路追蹤系統圍繞着一些目標展開建設。我們的功能性目標主要包括這幾個方面:
-
統一數據模型與語義:統一數據模型和語義規範,對所有主流框架 / 組件進行默認埋點中間件的替換升級,建立 Metrics / Trace / Log 可靠關聯關係。
-
開放自定義:統一模型的基礎上,充分開放自定義能力,滿足不同業務場景的監控追蹤需求。
-
中心化配置管控:中心化動態管理採樣、染色、熔斷限流、索引、脫敏保密等各類策略。
-
一站式觀測平臺:提供從 SDK 到採集、計算、存儲、查詢和產品化交互的完整解決方案,基於高質量基礎數據,構建一站式觀測平臺,提升監控排障、SLO 調優、架構梳理、容量管理等場景的能效。
在功能性目標的背後,我們追求的技術目標主要圍繞這幾個方面:
-
業務集成開銷最小化:集成開銷包括業務接入的改造成本和接入後帶來的 Overhead 開銷。大範圍的鏈路追蹤能夠成功覆蓋推廣,必須保證將集成開銷降到最低。
-
平衡存儲效率與檢索需求:需要以有限的機器預算完成較大數據量的處理和存儲,保證數據從產生到可被檢索的延遲在分鐘級以內,檢索響應速度在秒級以內。
-
多機房容災完備性:需要優先考慮當發生斷網或擁塞、機房宕機等災難場景,業務急需觀測線上狀況時,保持可用。
-
最小化架構與依賴複雜度:字節在海內外有衆多機房,需儘可能最小化整體架構的複雜度和第三方依賴的複雜度,否則多機房的部署運維包括容災完備性保障會非常困難。
字節鏈路追蹤系統的實現
數據採集
數據模型
統一的數據模型是 Trace 的基礎,字節鏈路追蹤系統的數據模型設計借鑑了 opentracing 和 CAT 等優秀的開源解決方案,結合字節內部實際生態和使用習慣,使用如下數據模型:
-
Span: 一個有時間跨度的事件,例如一次 RPC 調用,一個函數執行。
-
Event: 一個沒有時間跨度的事件,例如一條 log,一次 panic。
-
Metric: 一個帶多維 tag 的數值,例如一個消息體的大小,一個訂單的價格。
-
Trace: 一個請求上下文在多個分佈式微服務節點的完整執行鏈路。
-
Transaction: 一條 Trace 在單個服務節點上的所有 Span / Event / Metric 對象構成的樹形結構消息體。Transaction 是 Trace 數據的處理和存儲的最小單位,緊湊的數據結構有利於節約成本和提高檢索性能。
下圖展示了使用字節鏈路追蹤系統 SDK 埋 Trace 的代碼示例。注意其中 Context 貫穿整個請求生命週期,在進程內和跨進程間持續傳遞,將數據串聯起來。
繼續這個示例,我們結合下圖闡述一下如何基於這套模型將 Metric / Trace / Log 進行可靠關聯的。
Metric 關聯 Trace:
-
每個 Span 會有內置的頻次 / 耗時 / 失敗率 Metric 統計時序,Rpc/Mq 場景的 Span 還會有 SendSize/RecvSize/MqLag 等內置統計時序。Span Tag 和 Metric Tag 一一對應,以此爲依據可以將 Span 時序指標與 Trace 中的 Span 可靠關聯。
-
每個 Event 不僅會掛載在 Span 上,也會有內置的頻次 Metric 統計時序。Event Tag 與 Metric Tag 一一對應,以此爲依據可以將 Event 時序指標與 Trace 中的 Event 可靠關聯。
-
每個 Metric 不僅會掛載在 Span 上,也會按 Metric 類型輸出 rate/timer/store 等各類統計時序,兩邊 Tag 一一對應,以此爲依據可以將 Metric 時序指標與 Trace 中的 Metric 對象可靠關聯。
Trace 關聯 Log:
- Log SDK 會將 Context 中的 TraceID 和 SpanID 寫入日誌頭中,通過 TraceID 和 SpanID 與 Trace 建立可靠關聯。
語義規範
僅有統一的抽象數據模型還不夠。如果每個服務都五花八門的隨意打 tag 沒有統一標準,那麼即使有統一抽象模型也很難建設高質量的觀測平臺。必須對 HTTP Server, RPC Server, RPC Client, MySQL Client, Redis Client, MQ Consumer, MQ Producer 等各類主流場景都進行統一的語義規範,確保不同語言不同框架在相同場景下上報的數據遵循統一語義規範,才能夠真正獲取高質量的可觀測性數據。
語義規範沒有唯一標準,下面給出字節內部目前使用的部分語義規範作爲參考示例。
通用基礎字段
場景化語義規範示例:RPC Client 場景
採樣策略
由於字節整體線上流量非常大,微服務數目衆多,不同微服務的性能敏感度、成本敏感度和數據需求各有不同,例如有些服務涉及敏感數據,必須有非常完整的追蹤數據;有些服務性能高度敏感,需要優先控制採樣數最小化 Overhead;測試泳道、小流量灰度或者線上問題追查等場景會需要不同的採樣策略;常規流量和發生異常的流量也需要不同的採樣策略。因此靈活的採樣策略以及調控手段非常必要。字節鏈路追蹤系統主要提供瞭如下幾種採樣模式:
-
固定概率採樣 + 低流量接口兜底採樣:默認以 Logid 作爲採樣種子,按固定概率進行採樣。對於流量較低的接口,按固定概率採樣難以命中的,SDK 會自動按一定的時間間隔進行兜底採樣,確保低流量接口也有一定數目的請求被採集。
-
自適應概率採樣:按單位時間對每個接口採集一定數目的 Transaction 爲目標,例如 100 條 / min,SDK 自動根據當前 QPS 動態計算採樣率進行採樣。流量較低或不穩定的服務建議採取這種模式。
-
染色採樣:對特定的請求添加染色標記,SDK 檢測到染色標對該請求進行強制採樣。
-
PostTrace 後置採樣: 當一個 Trace 一開始未命中採樣,但在執行過程中發生了一些令人感興趣的事(例如出錯或時延毛刺)時,可以在 Trace 中間狀態發起採樣。相較於先全採再後置採樣,此方案開銷極低。PostTrace 是前置概率採樣的一個重要補充,可以針對性地採集到異常鏈路,相比於先全採後 tail-based sampling 方案其開銷是極小的。但 PostTrace 的缺點只能採集到 PostTrace 時刻尚未結束的 Span,因此數據完整性相較前置採樣有一定損失。
我們結合一個示例來更好的理解什麼是 PostTrace。左圖是一個請求,按照阿拉伯數字標識的順序在微服務間發生了調用,本來這條 trace 沒有采樣,但是在階段 5 時發生了異常,觸發了 posttrace,這個 posttrace 信息可以從 5 回傳到 4,並傳播給後續發生的 6 和 7,最後再回傳到 1,最終可以採集到 1,4,5,6,7 這幾個環節的數據,但是之前已經結束了的 2、3 環節則採集不到。右圖是我們線上的一個實際的 posttrace 展示效果,錯誤層層向上傳播最終採集到的鏈路的樣子。PostTrace 對於錯誤鏈傳播分析、強弱依賴分析等場景有很好的應用。
這些採樣策略可以同時組合使用。需注意,採樣不影響 Metrics 和 Log。Metrics 是全量數據的聚合計算結果,不受採樣影響。業務日誌也是全量採集,不受採樣影響。
中心化配置管控
爲了提高效率,方便不同團隊高效工作,字節鏈路追蹤系統提供了豐富的中心化配置管控能力,核心能力包括以下幾個方面:
-
採樣策略:支持業務按照不同的集羣、接口、機房、部署階段設置不同的概率採樣策略;也可以動態設置染色、PostTrace 觸發條件。
-
自定義索引:不同的框架和場景會有不同的默認索引字段,也支持業務按需在默認索引的基礎上爲自定義字段創建索引。
-
熔斷保護:SDK 默認配備多種熔斷保護機制確保 Trace 採集不會佔用過多資源影響主線功能,同時允許業務根據實際情況對相關的熔斷參數進行動態調整。
-
脫敏保密:業務可以按需對 Trace 數據進行脫敏和保密。
整體架構
整體架構
字節鏈路追蹤系統從數據接入側、消費存儲到查詢整體模塊架構如上圖所示。這裏說一些技術細節:
-
私有協議數據流,性能更極致:從 SDK 到最終寫入存儲,整體數據流採用私有協議,數據流中各環節僅需解碼部分 header 即可完成處理,無需解碼所有內容,可以節約大量的中間環節資源,降低時延。
-
底層本高吞吐的字節自研日誌存儲:以較低的存儲成本實現較高的寫入速度和查詢性能,用於支持各類 Trace 在線檢索場景。
-
單元化架構保障多機房容災完備性:整體採用單元化架構,節約機房間網絡帶寬,在部分機房間網絡故障或單機房宕機時保持高可用。
-
精細靈活的中心化調控能力:統一的配置中心向整個數據流各階段下發各類動態配置發並實時生效。
-
兼顧在線實時查詢與計算分析:如架構圖所示,數據流主要分兩條,一條負責數據的在線存儲和實時查詢,要求鏈路儘可能短,追求性能極致,壓低時延,保證數據從產生到可檢索要儘可能快 + 高可用;另一條是計算分析流,對延遲的要求相對較低,但是需要滿足各類場景化的計算分析需求,與公司數倉平臺有較好的集成。
-
元數據採集與安全過期:從 Trace 數據流中可以採集到準確度和時效性很高的元數據,例如每個微服務有哪些活躍接口,使用了哪些框架組件等信息,爲多個第三方系統例如監控告警和服務治理等平臺提供支持。
多機房容災完備性
前面講目標時提到,鏈路追蹤系統作爲一個可觀測性基礎設施,需要優先考慮當發生斷網或擁塞、機房宕機等災難場景,業務急需觀測線上狀況時,保持可用。
字節鏈路追蹤系統採用單元化部署機制,寫入數據流上各機房間無通信,頂層查詢模塊部署在匯聚機房(同時在主機房部署備用查詢節點),查詢時向各機房發起檢索併合並結果。
-
寫入流主機房間無數據通信,主機房間發生斷網時,功能不受損。
-
當發生單機房宕機或孤島時,可執行預案在查詢側屏蔽掉故障機房,保證其他機房數據可用。
-
當頂層查詢模塊所在的機房宕機或斷網時,可執行預案將查詢切到備用機房查詢節點繼續提供服務。
分析計算
除了基礎的實時檢索能力以外,場景化的數據聚合分析計算也是鏈路追蹤系統的一個重要需求,目前字節鏈路追蹤系統支持的分析計算場景主要包括:
-
拓撲計算:爲每個微服務(精確到接口 / 集羣 / 機房粒度)計算上下游依賴鏈路拓撲骨架,滿足業務架構梳理,全鏈路監控等場景需求。
-
流量估算:聚合計算所有 Trace 並結合每條 Trace 的採樣率估算出鏈路的原始流量及調用比例,滿足活動擴容評估,流量來源分析,成本分攤計算等場景需求。
-
錯誤鏈分析:聚合計算含有錯誤的 Trace 的錯誤傳播路徑,滿足故障根因定位,錯誤影響面分析,易故障點優化等場景需求。
-
鏈路性能分析:聚合計算滿足特定條件的 Trace 並分析各環節的耗時及調用比例等數據,滿足全鏈路性能優化,耗時增加根因定位等場景需求。
不同的需求場景可以選擇不同的計算模式,目前字節鏈路追蹤系統採用的計算模式主要有三種:
-
近實時流式計算:從消息隊列中消費數據按照時間窗口進行流式聚合計算,近實時地不斷更新計算結果。例如拓撲計算主要採取此模式,以獲取近實時的拓撲骨架。
-
即興抽樣計算:即興從在線存儲中按照特定條件抽樣檢索出有限數目(例如數百條)的 Trace 進行聚合計算,快速獲得計算結果。
-
離線批計算:定時對離線數倉中的 Trace 進行 MapReduce 批計算,輸出分析結果。
大部分場景的的 Trace 分析計算實質都是批量 Trace 的 MapReduce 計算,基礎邏輯算子在不同的計算模式中可以複用。例如錯誤傳播鏈分析計算,既可以在故障時刻進行即興抽樣計算快速得到分析結果,也可以通過離線批計算的方式進行長期訂閱用於 SLO 持續優化。
現階段實施效果
-
吞吐量:Transaction 數 10 Million / 秒(默認採樣率 0.1%),索引數 300 Million / 秒
-
查詢性能:TraceID 檢索性能 P50 < 100 毫秒, P99 < 500 毫秒
-
數據產生到可檢索整體時延: AVG ≈ 1 分鐘,P99 < 2 分鐘
-
存儲資源:5 PB (2 副本 TTL 15 天)
實踐應用案例
P99 慢請求根因追查
基於 Metrics, Trace, 底層調用分析和容器資源監控進行毛刺慢請求根因的快速定位。
全鏈路實時監控
支持從任一微服務節點發起拓撲查詢,實時觀測各節點的流量 / 延遲 / 錯誤率 / 資源使用率 / 告警 / 變更等,快速從全鏈路視角獲取整體狀態信息,用於日常巡檢、故障排查或壓測觀測等場景。
活動大促全鏈路容量預估
各業務線會經常搞些活動來促進用戶增長或留存,在準備這些活動時,容量估算是一個必備階段,過程一般如下:
字節鏈路追蹤系統可以根據入口在歷史時段上的 QPS,各節點調用比例,資源使用率等指標自動完成全鏈路各環節 QPS 增量與資源增量需求的一鍵估算。
故障來源與影響面分析
當發生異常時,可以從在線存儲中快速批量檢索到異常 Trace 進行聚合計算,分析錯誤根源來自哪裏,傳播到了哪裏,影響到了哪些服務,並和昨日同時段錯誤率進行對比,幫助工程師在遇到故障時快速定位根因。錯誤傳播鏈計算也支持通過離線訂閱的方式,對全時段所有異常 Trace 進行長期計算,以助力於 SLO 長期優化。
小結
Trace 是軟件系統監控數據的鏈接紐帶,建立 Metrics/Trace/Log 的可靠關聯關係,是構建強大的可觀測性能力的基礎,基於優質的監控數據,可以回答監控排障,SLO 調優,架構梳理,流量估算,智能化故障歸因等衆多複雜問題。
字節跳動在發展過程中,逐漸形成了十分複雜的超大規模微服務體系,我們面臨着很多挑戰,包括線上流量巨大,微服務數量巨大,迭代變化快,研發團隊龐大,分工複雜等,但同時也有着難得的機遇,即全公司層面的微服務基礎設施十分統一。
面對這樣的現狀,字節鏈路追蹤系統圍繞着一些目標展開建設,這些目標有一些是項目建設之初就明確的,也有一些是在實踐過程中反思總結的,主要包括統一數據模型與語義,開放自定義,中心化配置管控,一站式觀測平臺;業務集成開銷最小化,平衡存儲效率與檢索需求,多機房容災完備性和最小化架構與依賴複雜度。
接下來分享了字節鏈路追蹤系統的整體實現。數據採集側的建設主要關注數據模型,語義統一,採樣策略以及熔斷保護等,並實現中心化配置管控。服務端的建設主要關注平衡成本和延遲,兼顧在線檢索和分析計算,保障多機房的容災完備性。
最後分享了字節鏈路追蹤系統的一些實踐應用,例如 P99 慢請求根因追查,全鏈路實時監控,活動大促全鏈路容量預估和錯誤傳播分析等。
關於智能運維團隊
字節跳動基礎架構智能運維團隊(可觀測性基礎設施 Observability Infrastructure)負責(1)自研海量時序數據庫、調用鏈路、日誌系統;(2)一站式可觀測性平臺,包括自定義大盤、智能報警和智能監控產品、歸因自愈、鏈路排障分析。由此推動線上穩定性保障、故障診斷、容量管理和服務治理等能力構建。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/a0Pm26-8toNKz0brrRVG4Q