為什麼需要Trace
在現代IT系統,尤其是雲原生、微服務系統中,一次外部請求往往需要內部多個服務、多箇中間件、多臺機器的相互調用才能完成。在這一系列的調用中,任意階段出現的問題都可能導致外部服務失敗或延遲升高,最終影響用戶體驗。在這種情況下,若想要精確分析並定位具體服務、模塊、機器,你需要知道服務調用的關係是怎樣的,錯誤是怎麼傳播過來的,具體是哪塊的延遲比較大...這些Trace全部都可以告訴你。
Trace是什麼
分佈式鏈路追蹤(Distributed Tracing,簡稱Trace)又名全鏈路數據追蹤,為業務系統提供了整個服務調用鏈路的調用關係、延遲、結果等信息。相比訪問日誌這種記錄順時請求結果的數據,Trace則把內部的各種請求連接起來,組成一個服務調用的關係圖,讓你能輕鬆知道服務是怎麼發起的、怎麼流轉處理的。
冰山一角:一個典型的Trace表示
上述是一個非常簡單的程序,用戶訪問網站的時候自動給他們展示用戶所在地的天氣情況,這就需要調用兩個外部的服務。整個交互過程如上午的1、2所示。:首先獲取遠端的IP地址,調用IP服務獲取IP對應的地域信息;獲取到地域信息後,調用天氣服務查詢對應地點的天氣,最後返回給用戶。
這個典型的調用也可以理解為一個Transaction,也就是端到端的請求->返回的過程,對於用戶來說,就是一個簡單的頁面/接口請求,內部經歷的每個請求->返回的記錄就是Trace。圖3是一個典型的Trace調用展示,從這個圖中,可以看到請求整體的耗時、調用的接口、每個接口的耗時等信息。通過這種圖形化的方式,就可以很容易的找到是哪個請求耗費了太多的時間,或者那個請求出現了錯誤。而圖4是通過Trace信息還原出的DAG圖,能夠反映出系統中服務的依賴路徑,能夠更加清晰的瞭解系統。
上述就是一個最簡單的Trace表示,也是Trace最開始的狀態,能夠表示一個請求具體的調用路徑以及調用的詳細信息,具備一定的可視化能力來輔助查看。當然Trace的能力還不僅僅於此,下面我們會詳細展開真正友好的Trace方案所需具備的各項能力。
1. Trace標準化-OpenTelemetry
而無論圖3還是4都是依賴Trace的“父子關係”來構造出來,而這個關係主要有Trace的組成Span而來(Trace Span的概念最早來自於Google的Dapper)。
- Trace記錄了整個請求的生命週期,本身由一組Span組成,Span代表其中的一條調用鏈
- Span具有“父子關係”,這個父子關係由SpanID和ParentSpanID組成
- 當調用傳播到下一層時,原來的SpanID就變成了ParentSpanID,隨後會生成一個新的SpanID
- Span的傳播可能會跨進程、跨主機,因此需要有一個傳遞TraceID、SpanID的途徑,這個途徑叫做Trace Propagation,Trace Propagation需要保證上下游的服務都能夠支持一樣的協議才行,否則傳播到下一層時,因為服務無法識別,Trace會斷掉。
- 由於系統中可能具有多個服務,還有隊列、數據庫、ServiceMesh等中間件,因此Trace Propagation需要遵循某個國際化標準,這個標準需要儘可能的通用。
- 最早在出現的國際化標準是OpenTracing,隨後還有Google發起的OpenCensus項目
- 而目前OpenTracing項目和OpenCensus項目已經合併成為OpenTelemetry,OpenTelemetry已經成為Trace領域的唯一國際化標準。
而OpenTelemetry標準帶來的好處不僅僅是解決各個系統之間的Trace互通問題,還有統一的SDK、自動化埋點方案、數據採集、Traces/Metrics/Logs互通等等好處。感興趣的同學可以異步:OpenTelemetry介紹。
2. 埋點:手動 or 自動
對於開發者來講,使用Trace最大的工作量是接入數據,我們需要在請求發起的時候創建一個Span,並且這個Span需要繼承上一個Span傳過來的TraceID、ParentSpanID等信息。下述代碼就是一個簡單的手動構造Trace的例子:
/* api-service */ app.use('/api/v1/whereami', async (req, res, next) => { const parentSpan = createContinuationSpan(tracer, req, 'whereami-request') const childSpan = tracer.startSpan('city-from-ip', {childOf: parentSpan}) const _ = await axios.get(`http://ip-api.com/json/${req.ip}`) childSpan.finish() // Do more work and create more children spans. parentSpan.finish() }) function createContinuationSpan(tracer, req, spanName) { const incomingSpanContext = tracer.extract(opentelemetry.FORMAT_HTTP_HEADERS, req.headers) if (incomingSpanContext == null) { return tracer.startSpan(spanName) } return tracer.startSpan(spanName, {childOf: incomingSpanContext}) }
對於一個龐大的系統而言,業務中涉及的請求非常多,每個請求都手動埋點的工作量太大,一般這種方式也很難在公司進行推廣,因此各類語言的自動埋點的方式開始蓬勃發展。自動埋點在Trace領域又叫做Trace Instrumentation,主要利用一些代理、裝飾器、代碼注入、AOP等技術,在請求處理之前產生Span,在請求處理完成後保存Span並自動發送到Trace服務端。
下述是一個常見的服務架構,Service A和Service B都是HTTP的服務實現,網絡通信使用ServiceMesh進行流量管理,HTTP請求都會經過HTTP框架進行處理,然後提交到Dispatcher模塊,該模塊會根據不同的URL轉發到對應的Hander進行處理。這一架構中,可以自動埋點的地方有3個:
- ServiceMesh層,所有的HTTP請求在處理的時候都進行埋點
- HTTP Framework,在HTTP請求接收、處理的過程中埋點
- Dispatcher,在轉發的時候進行埋點
一般而言,自動埋點也可以在多層同時進行,這樣可以獲取到每層對於性能的消耗是多少,對整體架構的性能開銷有個更準確的理解。在生產上絕大部分情況都是使用自動埋點,對於有些自動埋點不能覆蓋的場景,會用手動埋點的方式進行補全。例如某些語言不支持自動埋點、某個請求內部處理邏輯複雜需要手動創建子Span分成多個階段...
自動埋點 |
手動埋點 |
|
工作量 |
低 |
高 |
支持的場景 |
少 |
全面 |
Trace完整度 |
低 |
高 |
性能開銷 |
較高 |
低 |
3. 基於Trace的監控
我們知道Trace由Span組成,Span表示了當前某個調用的結果以及“父子”關係,而當某個Service的某個調用積累一定數量的時候,我們就可以分析出這個Service、調用的一些關鍵指標:訪問次數(QPS)、延遲、錯誤率等。而這些通常也代表著我們的服務是否正常,因此通常情況下Trace不僅僅用來去查看某個請求是否有問題,更多的時候會用來監控服務是否正常。
4. PXX延遲-業務真實體驗
基於Trace的監控不是從聚合指標而來,而是從每個調用來去計算出相應的訪問次數、延遲、錯誤率,從原始數據計算的一個優點是可以較為準確的統計出PXX延遲,而PXX延遲也是反應用戶對於我們業務真實體驗的一個重要指標。因為通常情況下,平均延遲只能說明服務的情況,而不代表所有的客戶訪問都是如此的延遲,如果有一小部分的客戶訪問延遲很高,整體的平均延遲不會增加很多,但是這部分客戶對於產品的體驗會下降很多。
因此我們會更加關心P90、P95、P99之類的延遲指標,也就是按延遲正序排列,位於90%、95%、99%分位數的延遲大小。這部分延遲如果比平均延遲大非常多,那說明我們的服務在某些場景下對部分用戶的體驗非常不好,而這種情況下就需要去看這部分高延遲的指標具體耗時在哪個階段,並進行針對性解決。
5. 高級Dependency分析
Dependency作為Trace的核心功能,是分析系統架構的利器,通過大數據工程(一般使用流計算、批量計算)分析所有上報的Span信息,將其中公共的關係提取、聚合,然後保存成一張DAG圖。通常Dependency都只是從服務、調用維度進行計算,主要是因為這種計算的資源消耗比較少,畢竟服務、調用的組合比較少。
而實際場景中,我們希望知道在某個時間段、某一個進程、某一組容器的依賴關係,這就需要針對Span的Resource信息進行提取計算,組合關係會提升1-2個數量級,而計算複雜度和資源消耗也會隨之提升。但帶來的好處非常明顯,我們能夠知道更加細粒度的依賴關係,也更容易找出問題所在的機器、進程。
同時,在Dependency分析中,不僅僅只是計算DAG圖的關係,而且還可以計算每個父子關係(也就是DAG圖的邊)的統計信息,包括調用次數、錯誤率、延遲等。因此在查看Dependency的時候,還可以看到某個節點調用其他服務的統計信息,更加有利於問題排查。
6. Trace自定義過濾
通常為了更好的分析Trace數據,我們都會在Span中附加一定的Tag信息,如果更精確的描述,Tag分位兩類,分別Resource和Attribute。在OpenTelemetry協議中,這兩種數據雖然都是同一數據類型,但含義卻有著很大的不同:
- Resource代表Span的宿主信息描述,例如進程名、主機名、IP地址、設備類型、版本號、服務名、DB類型、K8s Pod名、環境信息、雲相關信息,相對都是靜止的信息。
- Attribute標識Span產生時和調用相關的屬性信息,例如HTTP的方法、返回碼、InFlow、OutFlow、URL、DB請求的SQL、數據隊列請求的Topic信息等。
通常我們都需要按照不同的Resource、Attribute來過濾出我們關心的數據,用來定位問題主要發生在哪些類型的調用中。當然除了對這些Tag信息支持過濾外,還需要對Trace的其他屬性,例如延遲、狀態碼、Span附加的Event信息等支持過濾。
7. Trace自定義分析
如果把Trace的自定義過濾功能比作是千里眼,則Trace的自定義分析功能就是顯微鏡。Trace自定義過濾從茫茫的數據中發現感興趣的一部分Trace數據,而自定義分析對這部分Trace數據進行詳細的排查,找出其中有問題的那一部分。這裡的分析主要利用統計學相關的技術,按照不同的維度組合去分析,而這些維度包括上述的任一Resource和Attribute信息,從中去發現異常的維度。
8. Trace自動化分析
上述的自定義分析主要是靠人工+統計學,這種方式在數據維度不多的情況下還可以勝任,但如果要分析的數據維度超過3個時,純粹的手工分析已經很難去把每個組合都嘗試一遍。例如某個支付類型的調用成功率出現了下降,這個調用有地域、設備版本號、運營商、會員類型、支付類型5個維度信息,手動來做的話需要按照5個不同的維度去一一嘗試,找到其中某個有明顯分界的維度,再保存這個維度的信息,繼續向其他4個維度進行嘗試,最壞的情況下需要5*4*3*2*1次的手動分析才能找到問題的組合。而這個還是在問題只是由一個組合影響,如果有多個組合綜合產生的結果,手動分析會收到多個結果的影響而更加複雜。
因此這時候我們就需要用算力和算法去做自動化的分析,利用算力從海量的組合中去一一分析,結合算法進行有效的剪枝,最終快速的得到造成調用錯誤率提升、延遲升高的維度組合。
9. 可觀察性數據關聯
Trace可做的事情很多,但並不是萬能的,Trace更多的是應用的關係數據以及從這些數據提取出的應用健康度(延遲、錯誤率、QPS等)數據。對於問題的根因分析還缺少一些關鍵的信息,包括與之關聯的基礎設施健康信息(例如Pod/主機核心指標、異常事件、內核報錯等)以及應用的詳細錯誤日誌。這些通常情況下叫做Metrics、Logs數據。
為了能夠讓Trace更好的關聯上述的其他數據,Trace中的Span必須包含對應的主機名、容器名、服務名等信息,也就是OpenTelemetry中的Resource信息。而且Trace方案需要提供快速跳轉並查詢對應數據的能力。
10. 自動根因定位
當我們能夠將Trace關聯到Logs和Metrics時,就能夠“順藤摸瓜”找到問題的根本原因,無論是某臺機器硬件故障(機器內核日誌定位)、資源飽和(指標數據定位)、應用錯誤(應用報錯日誌)...
這些問題的排查相比純粹的Trace分析會更加複雜,需要與更多的系統交互,需要去查看更多的數據,因此所花費的時間也更久。而故障發生時,1分鐘的損失也是難以接受的,因此我們需要更快的去發現這些問題,這時AIOps中的自動根因定位就派上了用場:利用Trace內部的“父子”關聯關係以及Resource與Logs、Metrics的關聯關係自動“順藤摸瓜”去排查問題的根因所在,大大節省問題排查的時間,能夠讓問題的根因定位從小時/分鐘級降低到秒級。
總結
隨著雲原生、微服務技術的普及,Trace在運維領域的重要性也越發的凸顯,構造一個簡單的Trace系統並不難,但如何構建一個能充分發揮Trace優勢的系統難度很大。上述介紹的這些Trace的特性,在生產中都極其適用,有這些特性的加持,問題排查將變得更加簡單,尤其是後面6-10的特性價值更高,實現難度也更大:
- Trace自定義過濾和自定義分析能夠幫助我們更好的去定位Trace相關的問題,這個能力要求Trace系統具備足夠的強的存儲、索引和算力。
- 可觀察性數據關聯能夠實現問題的根因定位,前提是需要所有的數據都能遵循一個統一的標準,而OpenTelemetry正是首選方案。
- 自動化的根因定位能夠縮短問題的排查時間,減少故障帶來的損失,核心是AIOps算法需要具備超強的算力和更好的檢測效果。
目前SLS已經發布了第一個版本的Trace服務,支持OpenTelemetry格式的Trace接入,並提供了上述1-7功能,後續功能的支持也在緊鑼密鼓的研發中,歡迎大家試用。
傳送門:https://sls.console.aliyun.com/lognext/trace
視頻介紹:https://www.loom.com/share/8f13f0af46164b52a5e78e5b455ec622
參考
- https://opentelemetry.io/
- https://developer.aliyun.com/article/766070
- https://aiopsworkshop.github.io/
- https://landscape.cncf.io/
- https://github.com/apache/skywalking
- https://www.instana.com/blog/observability-vs-monitoring/
- https://www.katacoda.com/lokoms/scenarios/log-workshop-4
- https://wiprodigital.com/2020/04/30/how-aiops-impacts-business-performance/
- https://docs.lightstep.com/docs/view-traces
- https://docs.lightstep.com/docs/investigate-a-latency-regression
- https://www.jaegertracing.io/
- https://medium.com/jaegertracing/using-jaeger-to-trace-an-apache-camel-application-2b8118efbb4d
- https://github.com/apache/camel/blob/master/components/camel-opentracing/src/main/docs/opentracing.adoc
- https://www.sumologic.com/blog/logs-metrics-overview/
- https://www.novatec-gmbh.de/en/blog/5-reasons-why-opentelemetry-will-boost-observability-and-monitoring/