分佈式鏈路追蹤在字節跳動的實踐

綜述

字節跳動在發展過程中,逐漸形成了十分複雜的超大規模微服務體系,對後端整體的可觀測性解決方案提出了極高的要求。爲了解決這個問題,基礎架構智能運維團隊自研鏈路追蹤系統,將海量 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。高度的一致性對於基礎架構團隊建設公司級別的統一鏈路追蹤系統提供了有利的基礎。

字節鏈路追蹤系統的目標

面對這樣的現狀,字節鏈路追蹤系統圍繞着一些目標展開建設。我們的功能性目標主要包括這幾個方面:

在功能性目標的背後,我們追求的技術目標主要圍繞這幾個方面:

字節鏈路追蹤系統的實現

數據採集

數據模型

統一的數據模型是 Trace 的基礎,字節鏈路追蹤系統的數據模型設計借鑑了 opentracing 和 CAT 等優秀的開源解決方案,結合字節內部實際生態和使用習慣,使用如下數據模型:

下圖展示了使用字節鏈路追蹤系統 SDK 埋 Trace 的代碼示例。注意其中 Context 貫穿整個請求生命週期,在進程內和跨進程間持續傳遞,將數據串聯起來。

繼續這個示例,我們結合下圖闡述一下如何基於這套模型將 Metric / Trace / Log 進行可靠關聯的。

Metric 關聯 Trace:

Trace 關聯 Log:

語義規範

僅有統一的抽象數據模型還不夠。如果每個服務都五花八門的隨意打 tag 沒有統一標準,那麼即使有統一抽象模型也很難建設高質量的觀測平臺。必須對 HTTP Server, RPC Server, RPC Client, MySQL Client, Redis Client, MQ Consumer, MQ Producer 等各類主流場景都進行統一的語義規範,確保不同語言不同框架在相同場景下上報的數據遵循統一語義規範,才能夠真正獲取高質量的可觀測性數據。

語義規範沒有唯一標準,下面給出字節內部目前使用的部分語義規範作爲參考示例。

通用基礎字段

e3Tgpy

場景化語義規範示例:RPC Client 場景

SAbA9u

採樣策略

由於字節整體線上流量非常大,微服務數目衆多,不同微服務的性能敏感度、成本敏感度和數據需求各有不同,例如有些服務涉及敏感數據,必須有非常完整的追蹤數據;有些服務性能高度敏感,需要優先控制採樣數最小化 Overhead;測試泳道、小流量灰度或者線上問題追查等場景會需要不同的採樣策略;常規流量和發生異常的流量也需要不同的採樣策略。因此靈活的採樣策略以及調控手段非常必要。字節鏈路追蹤系統主要提供瞭如下幾種採樣模式:

我們結合一個示例來更好的理解什麼是 PostTrace。左圖是一個請求,按照阿拉伯數字標識的順序在微服務間發生了調用,本來這條 trace 沒有采樣,但是在階段 5 時發生了異常,觸發了 posttrace,這個 posttrace 信息可以從 5 回傳到 4,並傳播給後續發生的 6 和 7,最後再回傳到 1,最終可以採集到 1,4,5,6,7 這幾個環節的數據,但是之前已經結束了的 2、3 環節則採集不到。右圖是我們線上的一個實際的 posttrace 展示效果,錯誤層層向上傳播最終採集到的鏈路的樣子。PostTrace 對於錯誤鏈傳播分析、強弱依賴分析等場景有很好的應用。

這些採樣策略可以同時組合使用。需注意,採樣不影響 Metrics 和 Log。Metrics 是全量數據的聚合計算結果,不受採樣影響。業務日誌也是全量採集,不受採樣影響。

中心化配置管控

爲了提高效率,方便不同團隊高效工作,字節鏈路追蹤系統提供了豐富的中心化配置管控能力,核心能力包括以下幾個方面:

整體架構

整體架構

字節鏈路追蹤系統從數據接入側、消費存儲到查詢整體模塊架構如上圖所示。這裏說一些技術細節:

多機房容災完備性

前面講目標時提到,鏈路追蹤系統作爲一個可觀測性基礎設施,需要優先考慮當發生斷網或擁塞、機房宕機等災難場景,業務急需觀測線上狀況時,保持可用。

字節鏈路追蹤系統採用單元化部署機制,寫入數據流上各機房間無通信,頂層查詢模塊部署在匯聚機房(同時在主機房部署備用查詢節點),查詢時向各機房發起檢索併合並結果。

分析計算

除了基礎的實時檢索能力以外,場景化的數據聚合分析計算也是鏈路追蹤系統的一個重要需求,目前字節鏈路追蹤系統支持的分析計算場景主要包括:

不同的需求場景可以選擇不同的計算模式,目前字節鏈路追蹤系統採用的計算模式主要有三種:

大部分場景的的 Trace 分析計算實質都是批量 Trace 的 MapReduce 計算,基礎邏輯算子在不同的計算模式中可以複用。例如錯誤傳播鏈分析計算,既可以在故障時刻進行即興抽樣計算快速得到分析結果,也可以通過離線批計算的方式進行長期訂閱用於 SLO 持續優化。

現階段實施效果

實踐應用案例

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