爲了實現系統組件的水平擴展、敏捷開發、CD(持續集成)、解耦等各類訴求,現代的大型軟件架構每每都是微服務架構。 微服務架構自己會面臨一系列諸如:後臺錯誤緣由分析、各個微服務組件的調用狀況診斷等。因此須要有一個 Tracing 系統解決這些問題。javascript
除了微服務之外 Serverless 應用也面臨一樣的問題。一個完整的業務實現想要基於 Serverless 模型來開發的話可能會分解成多個 Serverless 模塊,每個模塊單獨經過 Knative 的 Serving 部署,那麼這些不一樣的 Serving 之間就須要調用鏈進行事務的串聯。php
而 Knative 自己沒有 Tracing 的設計,須要藉助 ServiceMesh 層來實現 tracing。對於開源的 Knative 來講 ServiceMesh 層其實就是 Istio。說到 Istio 的 Tracing 就要提到 OpenTracing 了。OpenTracing 如今已經成了雲原生的 Tracing 標準。因此本文咱們從 OpenTracing 到 Istio 再到 Knative 來層層分析看看 Knative 應用應該如何使用 Tracing。前端
目前流行的分佈式跟蹤系統 Zipkin、Jeager 等。若是這些 Tracing 系統使用不兼容的 API 或者他們對開發語言支持不友好那麼就會給微服務開發帶來困難。java
OpenTracing 經過提供平臺無關、廠商無關的 API,使得開發人員可以方便的添加(或更換)追蹤系統的實現。 OpenTracing 還提供了流行開發語言的 SDK,這樣就很是方便非服務的 tracing 實現。經常使用的開發語言 SDK 以下:python
首先咱們要搞清楚什麼是 Trace,在前面的介紹中咱們提到 Trace 是爲了解決微服務調用的一些鏈路追蹤等問題,因此 Trace 應該是可以表達一個事務在各個微服務中的執行過程。git
因此一個 Trace 表明了一個事務或者流程在系統中的執行過程。在 OpenTracing 標準中,Trace 是由多個 Span 組成的一個有向無環圖,每個 Span 表明事務的一個執行節點或者片斷,每個 Span 都是有名字而且有起止時間的。github
分佈式追蹤中的每一個組件都一個或者多個 Span。例如,在一個常規的 RPC 調用過程當中,OpenTracing 推薦在 RPC 的客戶端和服務端,至少各有一個 Span,用於記錄 RPC 調用的客戶端和服務端信息。apache
Causal relationships between Spans in a single Trace [Span A] ←←←(the root span) | +------+------+ | | [Span B] [Span C] ←←←(Span C is a `ChildOf` Span A) | | [Span D] +---+-------+ | | [Span E] [Span F] >>> [Span G] >>> [Span H] ↑ ↑ ↑ (Span G `FollowsFrom` Span F)
如上所示是一個由 8 個 Span 組成的完整的 Trace 調用流程。swift
關於 Span 有兩個主要的關係須要解釋一下:ChildOf 和 FollowsFrom,從上面的示意圖中看起來都是前面的 Span 觸發後面的 Span,從圖上出不出區別。但實際區別仍是很大的。ruby
表示父子關係。意思是父 Span 須要等待子 Span 結束才能結束。因此子 Span 會 block 父 Span 的執行。如上所示的 [Span A] 必須等待 B 和 C 都結束本身才算結束,同理 C 也要等待 E 和 F 都結束才行。
表示兄弟關係。兄弟關係和父子關係的根本區別就是不須要等待兄弟的結束。好比上面的 F 和 G 的關係就是 F 結束以後觸發的 G,因此 F 要先於 G 結束,不須要等待 G。
ChildOf 有點兒像是函數調用,父函數須要等待子函數結束。FollowsFrom 有點兒像是 Goroutine 父 goroutine 並不須要等待子 goroutine 的結束,二者各自的生命週期是獨立的。
上圖能夠很好的表示各組件的組合關係,但沒法表示組件的調用時間、是串行調用仍是並行調用。因此通常的 Trace 實現都會用以下所示的方式展現 Trace 的調用過程
Temporal relationships between Spans in a single Trace ––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time [Span A···················································] [Span B··············································] [Span D··········································] [Span C········································] [Span E·······] [Span F··] [Span G··] [Span H··]
Istio 自己是基於 OpenTracing 調用鏈追蹤,而且 ZipKin 和 Jeager 兩種實現均可以用於 Istio。
上圖是 Istio 官方例子中的 Bookinfo 應用的例子。Bookinfo 應用由以下四個模塊組成
Python 語言開發,用來展現書本信息
Java 語言開發,接收 Product page 的請求,返回評論信息,同時也調用 Ratings 微服務。Reviews 有三個版本:v一、v二、v3,這個三個版本在響應 Product page 的請求時各有不一樣:v1 的響應沒有評分等級、v2 的響應包含黑色的評分等級、v3 的響應包含紅色的評分等級
Nodejs 語言開發,接受 Reviews 的請求,返回評分信息
Ruby 語言開發,接受 Product page 的請求,返回書本詳細屬性
從前端入口 Gateway 那個 Envoy 上進行一次調用,到四個不一樣語言開發的服務間完成調用,一次調用輸出的 Tracing 多是這樣:
如上圖所示有四個 Span:
並且這四個 Span 構成了一個調用鏈,也就是說 Ratings Span 知道本身的父 Span 是 reviews,reviews Span 也知道本身的父 Span 是 product page。那麼問題來了 reviews Span 和 ratings Span 是怎麼知道本身的父 Span 的呢?
咱們知道 Istio 是經過 Sidecar 的方式接管流量的,對於 Sidecar 裏面的 Envoy 來講是沒有辦法判斷進來的哪一個請求和出去的哪一個是一個事務。
咱們看一下 Istio bookInfo 的代碼 https://github.com/istio/istio/blob/master/samples/bookinfo/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java#L146 就能發現其實這個 tracing 的完整流程是須要應用程序參與的。
對於上圖所示對於 HTTP 請求需求在 Header 中把 Tracing 的信息獲取出來而且在調用下游服務的時候還須要繼續傳遞下去。也就是說 Istio 只是負責接管流量而且建立 Span,可是微服務 Span 之間的銜接是須要應用程序來配合的。
瞭解了 Istio Tracing 的原理咱們就比較清楚 Knative 應該的玩法了。Knative Service 部署的應用若是有多個微服務模塊,那麼微服務模塊之間的完整調用鏈的銜接就須要應用程序來完成。
對於 HTTP 協議而言就須要應用程序提取接受到的請求的 Tracing Header 中的信息而且傳遞到款下一個微服務的調用中。
另外雖然 OpenTracing 是一個通用的標準,可是 OpenTracing 並無規定 HTTP Header 應該如何設置。也就是說雖然 Istio 支持 Zipkin 和 Jeager 兩種 Tracing 實現,可是這兩種是不能混用的,由於這兩種實現的 Header 設置是不同的。不過 Jeager 設置一下也是能夠支持 ZipKin 標準的(參考這裏),因此咱們的應用程序開發的時候也要想好是基於 Zipkin 的規範實現仍是基於 Jeager 的規範實現。
原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。