博客 / 詳情

返回

opentelemetry全鏈路初探--jaeger架構拆分

前言

jaeger的架構演變

在之前的描述中,一直使用jaeger:all-in-one來做數據存儲與展示,jaeger:all-in-one就是將collector、query、ui、storage等等功能的大雜燴,在調試與測試環境中,非常方便,但是在生產環境肯定是不能這樣用,本節就來 將其拆分成對應的子模塊

jaeger架構改造

  • 數據上報:可以是sdk、api、定時腳本等一切上報trace、metrics數據的工具
  • collector:用於接收trace數據上報
  • storage:將數據發送到對應的地方存儲起來,以便UI查詢使用。trace常見的storage:es、kafka等
  • UI:trace常見的展示工具:jaeger

watermarked-collector_1.png

下面我們來詳細描述一下整個過程

採集程序

import tornado.httpserver as httpserver
import tornado.web
from tornado.ioloop import IOLoop
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.trace import get_tracer


trace.set_tracer_provider(
    TracerProvider(resource=Resource.create({SERVICE_NAME: "hello-otlp"}))
)
span_processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://127.0.0.1:14318/v1/traces"))
trace.get_tracer_provider().add_span_processor(span_processor)

def traced(name):
    def decorator(func):
        def wrapper(*args, **kwargs):
            tracer = get_tracer(__name__)
            with tracer.start_as_current_span(name):
                return func(*args, **kwargs)
        return wrapper
    return decorator

class TestFlow(tornado.web.RequestHandler):
    def get(self):
        views()
        self.finish('hello world')

@traced("phase-1")
def views():
    views_sub_2()
    views_sub_3()

@traced("phase-2")
def views_sub_2():
    pass

@traced("phase-3")
def views_sub_3():
    pass

def applications():
    urls = []
    urls.append([r'/', TestFlow])
    return tornado.web.Application(urls)

def main():
    app = applications()
    server = httpserver.HTTPServer(app)
    server.bind(10000, '0.0.0.0')
    server.start(1)
    IOLoop.current().start()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt as e:
        IOLoop.current().stop()
    finally:
        IOLoop.current().close()

jaeger-collector

docker run -d --name jaeger-collector \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14317:4317 \
  -p 14318:4318 \
  -e SPAN_STORAGE_TYPE=elasticsearch \
  -e ES_SERVER_URLS=http://10.22.12.178:9200 \
  -e ES_USERNAME=elastic \
  -e LOG_LEVEL=debug \
  jaegertracing/jaeger-collector:1.72.0

storage

這裏使用es來充當storage

docker run -d --name jaeger-es \
    -e bootstrap.memory_lock=true \
    -e discovery.type=single-node \
    -p 9200:9200 \
    -p 9300:9300 \
    -e xpack.security.enabled=false \
    -e xpack.security.http.ssl.enabled=false \
    elastic/elasticsearch:9.1.2

jaeger-ui

docker run -d --name jaeger-query \
  -p 16686:16686 \
  -p 16687:16687 \
  -e SPAN_STORAGE_TYPE=elasticsearch \
  -e ES_SERVER_URLS=http://10.22.12.178:9200 \
  -e ES_USERNAME=elastic \
  -e LOG_LEVEL=debug \
  jaegertracing/jaeger-query:1.72.0

來看下效果:

  • 首先讓上報程序上報trace數據: curl 127.0.0.1:10000
  • 登錄http://127.0.0.1:16686/查看
    watermarked-collector_2.png

很好,數據已經正常上報了

小結

  • jaeger從1.35版本開始支持原生otlp協議,所以可以直接在sdk中使用otlp協議上報。如果是低版本的jaeger,需要使用jaeger grpc、jaeger thrift等協議
  • 這裏要非常小心版本對應的問題,文中jaeger的版本是1.72,而es的版本是9.1.2,如果版本不匹配,很容易報錯。在低版本中,Jaeger Collector 在創建 ES 索引模板時,模板的格式不符合 Elasticsearch 8.x 新的要求 造成的。ES 7.x 還接受某些字段是字符串,但 8.x 對 index_template 結構要求更嚴格,會直接拒絕了導致無法啓動

新增數據處理層otel-collector

在jaeger-collector上做一層otel-collector做數據採集

watermarked-collector_3.png

對應的修改:

數據採集

...
span_processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://127.0.0.1:4318/v1/traces"))
...

otel-collector

docker run -d --name=otel-collector \
    -v ./otel-collector-config.yaml:/etc/otelcol/config.yaml \
    -p 4317:4317 \
    -p 4318:4318 \
    otel/opentelemetry-collector:latest

配置完成,有位老哥説了,為啥要這麼配置,本來我直接發到jaeger-collector就行了,現在多加一層otel-collector,多做了一層無用功,完全沒必要啊

這位老哥的思路非常清晰,現在來仔細觀察下otel-collector與jaeger-collector的區別

otel-collector與jaeger-collector

otel-collector jaeger-collector
作用範圍 traces、metrics 只支持traces
協議 OTLP、prometheus、zipkin等多種協議 jaeger thrift、jaeger grpc,新版本也支持OTLP
後端支持 可以發到支持otel的後端,比如tempo、prometheus、logging,甚至是jaeger-collector 只能發到 Jaeger Collector
  • jaeger的出現早於opentelemetry,所以有部分系統是通過jaeger這一套邏輯構建的,但是後面慢慢發展,不但traces數據需要分析,還有metrics、logs等重要的數據也需要分析了,那opentelemetry的出現解決了這個問題,能 採集需要的數據,並且otel-collector作為一個數據逐漸成為了數據中轉中心。比如jaeger擅長分析traces,那就發到jaeger這一套生態中,包括jaeger-collector、jaeger-UI等;prometheus擅長分析metrics數據,那就將metrics發到prometheus裏面去
  • 所以在jaeger-collector之前新增otel-collector,並沒有增加系統冗餘,而是解耦了jaeger與數據採集,並且豐富了系統功能性

otel-collector採集metrics

watermarked-collector_4.png

otel-collector

修改otel-collector-config.yaml

receivers:
...
  hostmetrics:
    collection_interval: 10s
    scrapers:
      cpu: {}
      memory: {}
      disk: {}
      filesystem: {}
      network: {}

exporters:
...
  prometheus:
    endpoint: "0.0.0.0:9464"
    namespace: otelcol

service:
...
    metrics:
      receivers: [hostmetrics]
      exporters: [prometheus]

暴露9464端口,等prometheus來拉取

docker run -d --name=otel-collector \
  -v ./otel-collector-config.yaml:/etc/otelcol/config.yaml \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 9464:9464 \
  otel/otel-collector:latest

prometheus

prometheus.yml

global:
  scrape_interval: 5s

scrape_configs:
  - job_name: "otel-collector"
    static_configs:
      - targets: ["10.22.12.178:9464"]
docker run -d --name prometheus \
  -p 9090:9090 \
  -v ./prometheus.yml:/etc/prometheus/prometheus.yml \
  prom/prometheus:v3.5.0

檢查prometheus,metrics數據已經獲取

watermarked-collector_5.png

traces轉換為metrics

提取traces耗時

將traces數據轉換為metrics,比如文中有3段span phase-1 phase-2 phase-3,分別將它們的耗時時間轉換成metrics存入prometheus,便於分析

1)修改otel-collector-config.yaml

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

  hostmetrics:
    collection_interval: 10s
    scrapers:
      cpu: {}
      memory: {}
      disk: {}
      filesystem: {}
      network: {}

connectors:
  spanmetrics:
    dimensions:
      - name: operation

exporters:
  otlp:
    endpoint: 10.22.12.178:14317
    tls:
      insecure: true
  prometheus:
    endpoint: "0.0.0.0:9464"
    namespace: otelcol

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp, spanmetrics]
    metrics:
      receivers: [hostmetrics, spanmetrics]
      exporters: [prometheus]

2)重新運行鏡像

docker run -d --name=otel-collector \
  -v ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 9464:9464 \
  otel/opentelemetry-collector-contrib:0.132.3

這裏需要非常小心了,由於需要對數據處理,使用了spanmetrics插件,而該插件只能在opentelemetry-collector-contrib才有,如果用opentelemetry-collector是沒有的

3)上報trace數據: curl 127.0.0.1:10000,查看prometheus

watermarked-collector_6.png

watermarked-collector_7.png

耗時也是能夠對應起來的

提取traces的attribute

1)先修改下采集程序,注入attribute

...
def traced(name):
    def decorator(func):
        def wrapper(*args, **kwargs):
            tracer = get_tracer(__name__)
            with tracer.start_as_current_span(name) as span:
                span.set_attribute("addr", "cd") # 注入屬性
                return func(*args, **kwargs)
        return wrapper
    return decorator
...

2)修改otel-collector配置

otel-collector-config.yaml

...
connectors:
  spanmetrics:
    dimensions:
      - name: operation
      - name: addr # 提取屬性
...

3)上報trace數據: curl 127.0.0.1:10000,查看prometheus

watermarked-collector_8.png

小結

  • 先從jaeger出發,從all-in-one的測試架構改造成可在生產環境使用的架構jaeger-collector --> es storage --> jaeger-UI
  • 新增數據處理層otel-collector,使得整個數據採集更靈活,不但可以採集traces、也可以採集metrics
  • otel-collector不但做數據轉發,也可以做數據修改

聯繫我

  • 聯繫我,做深入的交流

至此,本文結束
在下才疏學淺,有撒湯漏水的,請各位不吝賜教...

user avatar reqingderiguangdeng_emmgay 頭像 changqingdeyema_cy7lds 頭像 websong16 頭像
3 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.