<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          分布式鏈路追蹤

          共 18006字,需瀏覽 37分鐘

           ·

          2021-09-14 21:39

          題外話

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

          但數(shù)量龐大的微服務(wù)實(shí)例治理起來給我們帶來了很多問題,通常的做法都是引入相應(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)的另外一種做法就是未來微服務(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)無關(guān)、廠商無關(guān)的 API,因此開發(fā)者只需要對(duì)接 OpenTracing API,無需關(guān)心后端采用的到底是什么分布式追蹤系統(tǒng),Jager、Skywalking、LightStep 等都可以無縫切換。

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



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

          • Trace (調(diào)用鏈):一個(gè) Trace 代表一個(gè)事務(wù)或者流程在(分布式)系統(tǒng)中的執(zhí)行過程。例如來自客戶端的一個(gè)請(qǐng)求從接收到處理完成的過程就是一個(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 通過某種序列化協(xié)議 (Wire Protocol) 在進(jìn)程邊界上進(jìn)行傳遞,以將不同進(jìn)程中的 Span 關(guān)聯(lián)到同一個(gè) Trace 上。對(duì)于 HTTP 請(qǐng)求來說,SpanContext 一般是采用 HTTP header 進(jìn)行傳遞的。

          總結(jié):多個(gè) Span 共同組成一個(gè)有向無環(huán)圖(DAG)形成了 Trace ,SpanContext 則用于將一個(gè) Span 的上下文傳遞到其下游的 Span 中,以將這些 Span 關(guān)聯(lián)起來。

          例如:下面的示例 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(`<a href="/home"> 點(diǎn)擊開始發(fā)起請(qǐng)求 </a>`))
          }

          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]無法添加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]無法添加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) {
           // 通過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]無法添加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) {
           // 通過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)。它們可以用來為各種現(xiàn)有開源框架提供分布式追蹤工具。
          • Jaeger Agent : Jaeger 代理是一個(gè)網(wǎng)絡(luò)守護(hù)進(jìn)程,它會(huì)監(jiān)聽通過 UDP 發(fā)送的 span,并發(fā)送到收集程序。這個(gè)代理應(yīng)被放置在要管理的應(yīng)用程序的同一主機(jī)上。這通常是通過如 Kubernetes 等容器環(huán)境中的 sidecar 來實(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     <none>        8383/TCP,8686/TCP                        3m33s
          my-jaeger-agent                ClusterIP   None             <none>        5775/UDP,5778/TCP,6831/UDP,6832/UDP      15s
          my-jaeger-collector            ClusterIP   10.111.136.244   <none>        9411/TCP,14250/TCP,14267/TCP,14268/TCP   15s
          my-jaeger-collector-headless   ClusterIP   None             <none>        9411/TCP,14250/TCP,14267/TCP,14268/TCP   15s
          my-jaeger-query                NodePort    10.105.255.201   <none>        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: &jaegercfg.SamplerConfig{
             Type:  jaeger.SamplerTypeConst,
             Param: 1,
            },
            Reporter: &jaegercfg.ReporterConfig{
             LogSpans: true,
            },
           }
           _, err := cfg.InitGlobalTracer(
            "jaeger-example"// 服務(wù)名
            jaegercfg.Logger(jaegerlog.StdLogger),
            jaegercfg.Metrics(metrics.NullFactory),
           )
           if err != nil {
            panic(err)
           }
          }

          將應(yīng)用部署到 k8s 集群

          $ kubectl apply -f https://raw.githubusercontent.com/togettoyou/jaeger-example/master/jaeger-example.yaml -n observability
          deployment.apps/jaeger-example created
          service/jaeger-example-service created

          $
           kubectl get svc jaeger-example-service -n observability
          NAME                     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
          jaeger-example-service   NodePort   10.106.2.139   <none>        8080:32668/TCP   11s

          提示:要使 jaeger 能夠自動(dòng)為我們的應(yīng)用注入邊車代理,只需要在部署的 Deployment 資源中添加 "sidecar.jaegertracing.io/inject": "true" 的注釋

          訪問 http://集群域名:32668

          訪問 jaeger 用戶界面

          查看剛才的調(diào)用鏈:

          總結(jié)

          本文主要介紹了 OpenTracing 以及 jaeger 之間的關(guān)系和使用方法,OpenTracing 是一個(gè)鏈路追蹤的規(guī)范,我們可以使用 OpenTracing API 完成代碼的監(jiān)控埋點(diǎn),最后可以自由選擇連接遵循 OpenTracing 標(biāo)準(zhǔn)的鏈路追蹤系統(tǒng),比如 jaeger 。

          本文所有代碼均托管在 github.com/togettoyou/jaeger-example[5]

          參考資料

          [1]

          istio-handbook/practice/opentracing: https://www.servicemesher.com/istio-handbook/practice/opentracing.html

          [2]

          opentracing-specification-zh: https://github.com/opentracing-contrib/opentracing-specification-zh/blob/master/specification.md

          [3]

          opentracing-io/quick-start: https://wu-sheng.gitbooks.io/opentracing-io/content/pages/quick-start.html

          [4]

          jaeger-operator: https://github.com/jaegertracing/jaeger-operator/tree/master/examples

          [5]

          github.com/togettoyou/jaeger-example: https://github.com/togettoyou/jaeger-example


          瀏覽 69
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚州成人 | 国产精品久久77777免费影视 | 日韩99视频 | 99热网址 | 黄色一级视频在线观看 |