當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]題外話微服務(wù)架構(gòu)作為云原生核心技術(shù)之一,提倡將單一應(yīng)用程序劃分成一組小的服務(wù)(微服務(wù)),服務(wù)之間互相協(xié)調(diào)、互相配合,為用戶提供最終價(jià)值。但數(shù)量龐大的微服務(wù)實(shí)例治理起來(lái)給我們帶來(lái)了很多問題,通常的做法都是引入相應(yīng)組件完成,如API網(wǎng)關(guān)(apisix,kong,traefik)負(fù)責(zé)認(rèn)...

題外話

微服務(wù)架構(gòu) 作為云原生核心技術(shù)之一,提倡將單一應(yīng)用程序劃分成一組小的服務(wù)(微服務(wù)),服務(wù)之間互相協(xié)調(diào)、互相配合,為用戶提供最終價(jià)值。

但數(shù)量龐大的微服務(wù)實(shí)例治理起來(lái)給我們帶來(lái)了很多問題,通常的做法都是引入相應(yīng)組件完成,如 API 網(wǎng)關(guān) ( apisix, kong, traefik ) 負(fù)責(zé)認(rèn)證鑒權(quán)、負(fù)載均衡、限流和靜態(tài)響應(yīng)處理;服務(wù)注冊(cè)與發(fā)現(xiàn)中心 ( Consul, Etcd, ZooKeeper ) 負(fù)責(zé)管理維護(hù)微服務(wù)實(shí)例,記錄服務(wù)實(shí)例元數(shù)據(jù);可觀察性方面包括 Metrics 監(jiān)控 ( Prometheus ) 負(fù)責(zé)性能指標(biāo)統(tǒng)計(jì)告警,Logging 日志 ( Loki, ELK ) 負(fù)責(zé)日志的收集查看,Tracing 鏈路追蹤 ( OpenTracing, Jaeger ) 負(fù)責(zé)追蹤具體的請(qǐng)求和繪制調(diào)用的拓?fù)潢P(guān)系。對(duì)于這種需要自行引入各種組件完成微服務(wù)治理的稱為 侵入式架構(gòu) ,與之相對(duì)應(yīng)的另外一種做法就是未來(lái)微服務(wù)架構(gòu) —— 服務(wù)網(wǎng)格 ( Service Mesh ) 。

正文

本文主要介紹可觀察性的鏈路追蹤模塊,我將按以下幾個(gè)大綱逐步演進(jìn):

  • OpenTracing 介紹
  • Jaeger 介紹
  • Jaeger 部署
  • Jaeger 使用

OpenTracing 介紹

起源

實(shí)現(xiàn)分布式追蹤的方式一般是在程序代碼中進(jìn)行埋點(diǎn),采集調(diào)用的相關(guān)信息后發(fā)送到后端的一個(gè)追蹤服務(wù)器進(jìn)行分析處理。在這種實(shí)現(xiàn)方式中,應(yīng)用代碼需要依賴于追蹤服務(wù)器的 API,導(dǎo)致業(yè)務(wù)邏輯和追蹤的邏輯耦合。為了解決該問題,CNCF (云原生計(jì)算基金會(huì))下的 OpenTracing 項(xiàng)目定義了一套分布式追蹤的標(biāo)準(zhǔn),以統(tǒng)一各種分布式追蹤系統(tǒng)的實(shí)現(xiàn)。OpenTracing 中包含了一套分布式追蹤的標(biāo)準(zhǔn)規(guī)范,各種語(yǔ)言的 API,以及實(shí)現(xiàn)了該標(biāo)準(zhǔn)的編程框架和函數(shù)庫(kù)。參考[1]

OpenTracing 提供了平臺(tái)無(wú)關(guān)、廠商無(wú)關(guān)的 API,因此開發(fā)者只需要對(duì)接 OpenTracing API,無(wú)需關(guān)心后端采用的到底是什么分布式追蹤系統(tǒng),Jager、Skywalking、LightStep 等都可以無(wú)縫切換。

數(shù)據(jù)模型


分布式鏈路追蹤

OpenTracing 定義了以下數(shù)據(jù)模型:

  • Trace (調(diào)用鏈):一個(gè) Trace 代表一個(gè)事務(wù)或者流程在(分布式)系統(tǒng)中的執(zhí)行過(guò)程。例如來(lái)自客戶端的一個(gè)請(qǐng)求從接收到處理完成的過(guò)程就是一個(gè) Trace。
  • Span(跨度):Span 是分布式追蹤的最小跟蹤單位,一個(gè) Trace 由多段 Span 組成??梢员焕斫鉃橐淮畏椒ㄕ{(diào)用, 一個(gè)程序塊的調(diào)用, 或者一次 RPC/數(shù)據(jù)庫(kù)訪問。只要是一個(gè)具有完整時(shí)間周期的程序訪問,都可以被認(rèn)為是一個(gè) Span。
  • SpanContext(跨度上下文):分布式追蹤的上下文信息,包括 Trace id,Span id 以及其它需要傳遞到下游服務(wù)的內(nèi)容。一個(gè) OpenTracing 的實(shí)現(xiàn)需要將 SpanContext 通過(guò)某種序列化協(xié)議 (Wire Protocol) 在進(jìn)程邊界上進(jìn)行傳遞,以將不同進(jìn)程中的 Span 關(guān)聯(lián)到同一個(gè) Trace 上。對(duì)于 HTTP 請(qǐng)求來(lái)說(shuō),SpanContext 一般是采用 HTTP header 進(jìn)行傳遞的。
總結(jié):多個(gè) Span 共同組成一個(gè)有向無(wú)環(huán)圖(DAG)形成了 Trace ,SpanContext 則用于將一個(gè) Span 的上下文傳遞到其下游的 Span 中,以將這些 Span 關(guān)聯(lián)起來(lái)。

例如:下面的示例 Trace 就是由 8 個(gè) Span 組成的:參考[2]

以樹的結(jié)構(gòu)展示 Trace 調(diào)用鏈:

單個(gè)Trace中,span間的因果關(guān)系


????????[Span?A]??←←←(the?root?span)
????????????|
????? ------ ------
?????|?????????????|
?[Span B]??????[Span C]?←←←(Span C 是 Span A 的孩子節(jié)點(diǎn), ChildOf)
?????|?????????????|
?[Span?D]?????? --- -------
???????????????|???????????|
???????????[Span?E]????[Span?F]?>>>?[Span?G]?>>>?[Span?H]
???????????????????????????????????????↑
???????????????????????????????????????↑
???????????????????????????????????????↑
?????????????????????????(Span?G?在?Span?F?后被調(diào)用,?FollowsFrom)


基于時(shí)間軸的時(shí)序圖展示 Trace 調(diào)用鏈:

單個(gè)Trace中,span間的時(shí)間關(guān)系


––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–>?time

?[Span?A···················································]
???[Span?B··············································]
??????[Span?D··········································]
????[Span?C········································]
?????????[Span?E·······]????????[Span?F··]?[Span?G··]?[Span?H··]

OpenTracing API for Go

以官方博客例子為例[3]

安裝

go?get?github.com/opentracing/opentracing-go
創(chuàng)建 main.go ,實(shí)現(xiàn)一個(gè) Web 服務(wù),并在請(qǐng)求流程中使用 OpenTracing API 進(jìn)行埋點(diǎn)處理。

Show me the code !

package?main

import?(
?"fmt"
?"log"
?"math/rand"
?"net/http"
?"time"

?"github.com/opentracing/opentracing-go"
)

func?main()?{
?port?:=?8080
?addr?:=?fmt.Sprintf(":%d",?port)
?mux?:=?http.NewServeMux()
?mux.HandleFunc("/",?indexHandler)
?mux.HandleFunc("/home",?homeHandler)
?mux.HandleFunc("/async",?serviceHandler)
?mux.HandleFunc("/service",?serviceHandler)
?mux.HandleFunc("/db",?dbHandler)
?fmt.Printf("http://localhost:%d\n",?port)
?log.Fatal(http.ListenAndServe(addr,?mux))
}

//?主頁(yè)?Html
func?indexHandler(w?http.ResponseWriter,?r?*http.Request)?{
?w.Write([]byte(`?點(diǎn)擊開始發(fā)起請(qǐng)求?`))
}

func?homeHandler(w?http.ResponseWriter,?r?*http.Request)?{
?w.Write([]byte("開始請(qǐng)求...\n"))

?//?在入口處設(shè)置一個(gè)根節(jié)點(diǎn)?span
?span :=?opentracing.StartSpan("請(qǐng)求?/home")
?defer?span.Finish()

?//?發(fā)起異步請(qǐng)求
?asyncReq,?_?:=?http.NewRequest("GET",?"http://localhost:8080/async",?nil)
?//?傳遞span的上下文信息
?//?將關(guān)于本地追蹤調(diào)用的span context,設(shè)置到http?header上,并傳遞出去
?err?:=?span.Tracer().Inject(span.Context(),
??opentracing.TextMap,
??opentracing.HTTPHeadersCarrier(asyncReq.Header))
?if?err?!=?nil?{
??log.Fatalf("[asyncReq]無(wú)法添加span context到http?header:?%v",?err)
?}
?go?func()?{
??if?_,?err?:=?http.DefaultClient.Do(asyncReq);?err?!=?nil?{
???//?請(qǐng)求失敗,為span設(shè)置tags和logs
???span.SetTag("error",?true)
???span.LogKV(fmt.Sprintf("請(qǐng)求?/async?error:?%v",?err))
??}
?}()

?time.Sleep(time.Duration(rand.Intn(200))?*?time.Millisecond)

?//?發(fā)起同步請(qǐng)求
?syncReq,?_?:=?http.NewRequest("GET",?"http://localhost:8080/service",?nil)
?err?=?span.Tracer().Inject(span.Context(),
??opentracing.TextMap,
??opentracing.HTTPHeadersCarrier(syncReq.Header))
?if?err?!=?nil?{
??log.Fatalf("[syncReq]無(wú)法添加span context到http?header:?%v",?err)
?}
?if?_,?err?=?http.DefaultClient.Do(syncReq);?err?!=?nil?{
??span.SetTag("error",?true)
??span.LogKV(fmt.Sprintf("請(qǐng)求?/service?error:?%v",?err))
?}
?w.Write([]byte("請(qǐng)求結(jié)束!"))
}

//?模擬業(yè)務(wù)請(qǐng)求
func?serviceHandler(w?http.ResponseWriter,?r?*http.Request)?{
?//?通過(guò)http?header,提取span元數(shù)據(jù)信息
?var?sp?opentracing.Span
?opName?:=?r.URL.Path
?wireContext,?err?:=?opentracing.GlobalTracer().Extract(
??opentracing.TextMap,
??opentracing.HTTPHeadersCarrier(r.Header))
?if?err?!=?nil?{
??//?獲取失敗,則直接新建一個(gè)根節(jié)點(diǎn)?span
??sp?=?opentracing.StartSpan(opName)
?}?else?{
??sp?=?opentracing.StartSpan(opName,?opentracing.ChildOf(wireContext))
?}
?defer?sp.Finish()

?dbReq,?_?:=?http.NewRequest("GET",?"http://localhost:8080/db",?nil)
?err?=?sp.Tracer().Inject(sp.Context(),
??opentracing.TextMap,
??opentracing.HTTPHeadersCarrier(dbReq.Header))
?if?err?!=?nil?{
??log.Fatalf("[dbReq]無(wú)法添加span context到http?header:?%v",?err)
?}
?if?_,?err?=?http.DefaultClient.Do(dbReq);?err?!=?nil?{
??sp.SetTag("error",?true)
??sp.LogKV("請(qǐng)求?/db?error",?err)
?}

?time.Sleep(time.Duration(rand.Intn(200))?*?time.Millisecond)
}

//?模擬DB調(diào)用
func?dbHandler(w?http.ResponseWriter,?r?*http.Request)?{
?//?通過(guò)http?header,提取span元數(shù)據(jù)信息
?var?sp?opentracing.Span
?opName?:=?r.URL.Path
?wireContext,?err?:=?opentracing.GlobalTracer().Extract(
??opentracing.TextMap,
??opentracing.HTTPHeadersCarrier(r.Header))
?if?err?!=?nil?{
??//?獲取失敗,則直接新建一個(gè)根節(jié)點(diǎn)?span
??sp?=?opentracing.StartSpan(opName)
?}?else?{
??sp?=?opentracing.StartSpan(opName,?opentracing.ChildOf(wireContext))
?}
?defer?sp.Finish()

?time.Sleep(time.Duration(rand.Intn(200))?*?time.Millisecond)
}
最后,只需要在應(yīng)用程序啟動(dòng)時(shí)連接到任意實(shí)現(xiàn)了 OpenTracing 標(biāo)準(zhǔn)的鏈路追蹤系統(tǒng)即可。詳見下文的 Jaeger 使用。

Jaeger 介紹

Jaeger 受 Dapper 和 OpenZipkin 的啟發(fā),是 Uber Technologies 開源的分布式跟蹤系統(tǒng),遵循 OpenTracing 標(biāo)準(zhǔn),功能包括:

  • 分布式上下文傳播
  • 監(jiān)控分布式事務(wù)
  • 執(zhí)行根原因分析
  • 服務(wù)依賴分析
  • 優(yōu)化性能和延遲時(shí)間

架構(gòu)

Jaeger 既可以部署為一體式二進(jìn)制文件 (ALL IN ONE),其中所有 Jaeger 后端組件都運(yùn)行在單個(gè)進(jìn)程中,也可以部署為可擴(kuò)展的分布式系統(tǒng) (高可用架構(gòu))


分布式鏈路追蹤

主要有以下幾個(gè)組件:

  • Jaeger Client : OpenTracing API 的具體語(yǔ)言實(shí)現(xiàn)。它們可以用來(lái)為各種現(xiàn)有開源框架提供分布式追蹤工具。
  • Jaeger Agent : Jaeger 代理是一個(gè)網(wǎng)絡(luò)守護(hù)進(jìn)程,它會(huì)監(jiān)聽通過(guò) UDP 發(fā)送的 span,并發(fā)送到收集程序。這個(gè)代理應(yīng)被放置在要管理的應(yīng)用程序的同一主機(jī)上。這通常是通過(guò)如 Kubernetes 等容器環(huán)境中的 sidecar 來(lái)實(shí)現(xiàn)的。
  • Jaeger Collector : 與代理類似,該收集器可以接收 span,并將其放入內(nèi)部隊(duì)列以便進(jìn)行處理。這允許收集器立即返回到客戶端/代理,而不需要等待 span 進(jìn)入存儲(chǔ)。
  • Storage : 收集器需要一個(gè)持久的存儲(chǔ)后端。Jaeger 帶有一個(gè)可插入的機(jī)制用于 span 存儲(chǔ)。
  • Query : Query 是一個(gè)從存儲(chǔ)中檢索 trace 的服務(wù)。
  • Ingester : 可選組件。Jaeger 可以使用 Apache Kafka 作為收集器和實(shí)際后備存儲(chǔ)之間的緩沖。Ingester 是一個(gè)從 Kafka 讀取數(shù)據(jù)并寫入另一個(gè)存儲(chǔ)后端的服務(wù)。
  • Jaeger Console : Jaeger 提供了一個(gè)用戶界面,可讓您可視覺地查看所分發(fā)的追蹤數(shù)據(jù)。在搜索頁(yè)面中,您可以查找 trace,并查看組成一個(gè)獨(dú)立 trace 的 span 詳情。

Jaeger 部署

Jaeger 部署方案主要圍繞以下幾個(gè)方面:

  • ALL IN ONE 還是分布式
  • 后端存儲(chǔ)的選擇(Elasticsearch、Cassandra 甚至 memory)
  • 是否引入 Kafka 作為中間緩沖器
  • Jaeger Agent 代理安裝方式:sidecar 還是 DaemonSet
  • 安裝工具的選擇:Operator 還是 Helm chart
仁者見仁智者見智,結(jié)合自身業(yè)務(wù)場(chǎng)景選擇適合自己的即可。

本文為了簡(jiǎn)化操作,就以 Operator Jaeger Agent sidecar memory ALL IN ONE 為例。

  1. 在 Kubernetes 上安裝 Jaeger Operator
#?創(chuàng)建?observability?命名空間
kubectl?create?namespace?observability
#?創(chuàng)建?crd?資源
kubectl?create?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml
#?聲明用戶權(quán)限
kubectl?create?-n?observability?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml
kubectl?create?-n?observability?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml
kubectl?create?-n?observability?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml
#?部署?Jaeger?Operator
kubectl?create?-n?observability?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml
  1. 獲得集群范圍的權(quán)限,可選
kubectl?create?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role.yaml
kubectl?create?-f?https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/cluster_role_binding.yaml
  1. 查看 Jaeger Operator 是否部署成功
$?kubectl?get?deployment?jaeger-operator?-n?observability
NAME??????????????READY???UP-TO-DATE???AVAILABLE???AGE
jaeger-operator???1/1?????1????????????1???????????10s
  1. 使用 Jaeger Operator 部署 Jaeger ,創(chuàng)建 Jaeger 定制資源 參考[4]
apiVersion:?jaegertracing.io/v1
kind:?Jaeger
metadata:
??name:?my-jaeger
spec:
??strategy:?allInOne?#?部署策略
??allInOne:
????image:?jaegertracing/all-in-one:latest
????options:
??????log-level:?debug?#?日志等級(jí)
??storage:
????type:?memory?#?可選?Cassandra、Elasticsearch
????options:
??????memory:
????????max-traces:?100000
??ingress:
????enabled:?false
??agent:
????strategy:?sidecar?#?代理部署策略可選?DaemonSet
??query:
????serviceType:?NodePort?#?用戶界面使用?NodePort
$?kubectl?apply?-f?my-jaeger.yaml?-n?observability
jaeger.jaegertracing.io/my-jaeger?created

$
?kubectl?get?jaeger?-n?observability
NAME????????STATUS???VERSION???STRATEGY???STORAGE???AGE
my-jaeger??????????????????????allinone???memory????10s

$
?kubectl?get?svc?-n?observability
NAME???????????????????????????TYPE????????CLUSTER-IP???????EXTERNAL-IP???PORT(S)??????????????????????????????????AGE
jaeger-operator-metrics????????ClusterIP???10.103.46.73?????????????8383/TCP,8686/TCP????????????????????????3m33s
my-jaeger-agent????????????????ClusterIP???None?????????????????????5775/UDP,5778/TCP,6831/UDP,6832/UDP??????15s
my-jaeger-collector????????????ClusterIP???10.111.136.244???????????9411/TCP,14250/TCP,14267/TCP,14268/TCP???15s
my-jaeger-collector-headless???ClusterIP???None?????????????????????9411/TCP,14250/TCP,14267/TCP,14268/TCP???15s
my-jaeger-query????????????????NodePort????10.105.255.201???????????16686:32710/TCP,16685:32493/TCP??????????15s
訪問 jaeger 用戶界面 http://集群域名:32710

分布式鏈路追蹤
恭喜成功看到土撥鼠。

Jaeger 使用

繼續(xù)回到上文的 OpenTracing API for Go 示例,現(xiàn)在就可以將我們的應(yīng)用程序連接到 Jaeger 了。

安裝 Jaeger Client Go

go?get?-u?github.com/uber/jaeger-client-go
main.go 添加 init 初始化函數(shù)

func?init()?{
?cfg?:=?jaegercfg.Configuration{
??Sampler:?
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉