Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

利用Jaeger打造云原生架构下分布式追踪系统

01

为什么选择Jaeger

优点


缺点

  • 接入过程有一定的侵入性;

  • 相比与上篇介绍的 Apache SkyWalking 、CAT,Jaeger更专注于链路追踪(Tracing),日志和指标功能支持比较有限;

  • 本身缺少监控和报警机制,需要结合第三方工具来实现,比如配合Grafana 和 Prometheus实现。文章后面会给出简单的示例。


02

实现原理

1.Jaeger架构图解

图片来源: Jaeger Architecture


2.Jaeger组件

  • 客户端库实现了OpenTarcing API。可以手动也可以通过已经集成OpenTracing 的框架工具实现应用的分布式追踪,像Flask、Dropwizard、gRPC等都已经有现成的集成工具库;

  • 每当接受到新的请求,就会创建 span 并关联上下文信息(trace id、span id 和 baggage)。只有 id 和 baggage 会随请求向下传递,而所有组成 span 的其他信息,如操作名称、日志等,不会在同一个trace 的span间传递。通过采样得到的 span 会在后台异步发送到 Jaeger Agents 上;

  • 需要注意的是虽然所有的traces都会创建,但是只有少部分会被采样,采样到的trace会被标记并用于后续的处理和存储。默认情况下,Jaeger client 的采样率是 0.1%,也就是千分之一,并且可以从 Agent上取回采样率设置;

  • Agent 是一个网络守护进程,监听通过 UDP 发送过来的 spans,并将其批量发送给 Collector。按设计 Agent 要作为基础设施被部署到所有主机节点。Agent 将 Collector 和客户端之间的路由与发现机制抽象了出来。后面会详细介绍Agent的部署模式;

  • Collector 从 Agents 接收 traces,并通过一个pipeline对其进行处理。目前的pipeline会检验traces、建立索引、执行转换并最终进行存储。Jaeger的存储系统是一个可插入的组件,当前支持 Cassandra、Elasticsearch 和 Kafka(测试环境可以支持纯内存存储);

  • Query 从存储中检索 traces 并通过 一个漂亮的 UI 界面进行展现,目前支持搜索、过滤、traces 对比、查看依赖调用关系图等功能。


3.关于采样率

分布式追踪系统本身也会造成一定的性能低损耗,如果完整记录每次请求,对于生产环境可能会有极大的性能损耗,一般需要进行采样设置。

当前支持四种采样率设置:

  • 固定采样(sampler.type=const)sampler.param=1 全采样, sampler.param=0 不采样;

  • 按百分比采样(sampler.type=probabilistic)sampler.param=0.1 则随机采十分之一的样本;

  • 采样速度限制(sampler.type=ratelimiting)sampler.param=2.0 每秒采样两个traces;

  • 动态获取采样率 (sampler.type=remote) 这个是默认配置,可以通过配置从 Agent 中获取采样率的动态设置。

自适应采样(Adaptive Sampling)也已经在开发计划中。


03

部署实践

1.在Kubernetes集群上部署Jaeger

Jaeger是为云原生环境下的分布式追踪而打造,Kubernetes 又是当前云原生服务编排事实上的标准,下面以示例的方式介绍如何在 Kubernetes集群上部署 Jaeger:

   1# 克隆示例到本地   
2git clone https://github.com/maguowei/distributed-tracing-system.git
3cd distributed-tracing-system
4
5# 这里我们选择Elasticsearch作为存储, 简单创建测试用的 Elasticsearch 服务
6kubectl create -f deployment/kubernetes/elasticsearch
7
8# 部署Jaeger全家桶(Agent, Collector, Query)
9kubectl create -f deployment/kubernetes/jaeger
10
11# 以NodePort 方式暴露 Query UI
12kubectl expose service jaeger-query --port 16686 --type NodePort --name jaeger-query-node-port
13
14# 找到暴露的端口号
15kubectl get service jaeger-query-node-port
16
17# 访问 http://127.0.0.1:${port}



当前Query 中可以看到是空的,我们运行 官方的 HotROD 微服务示例,生成一些数据:

  1kubectl create -f deployment/kubernetes/example   
2kubectl expose service jaeger-example-hotrod --port 8080 --type NodePort --name jaeger-example-hotrod-node-port


打开HotROD页面, 任意点击页面上的按钮,生成一些调用数据:

刷新Jaeger Query UI 页面,然后我们就可以看到生成的调用信息:

点开具体的一条Trace 可以看到详细的调用过程:

还可以看到图形化的调用关系链:


2.选择 DaemonSet 还是 Sidecar

Agent 官方目前有两种部署方案,一种是 Daemonset 方式,一种是 Sidecar 方式。

按照官方的说法,Jaeger 中的 Agent 组件是作为 tracer 和 Collector 之间的 buffer, 所以 Agent 应该离 tracer 越近越好,通常应该是 tracer 的 localhost, 基于这样的假定,tracer 能够直接通过UDP发送span 到 Agent,达到最好的性能和可靠性之间的平衡。

这样的假定在裸机服务器上部署非常棒,但在当前流行的云环境和容器中,对于 Kubernetes 来说究竟什么是本地(localhost)呢?是服务运行所在的节点(node)还是 pod 本身呢?

DaemonSet 的 pod 运行在节点(node)级别,这样的pod如同每个节点上的守护进程,Kubernetes 确保每个节点有且只有一个 Agent pod运行, 如果以 DaemonSet 方式部署,则意味着这个 Agent 会接受节点上所有应用pods发送的数据,对于 Agent 来说所有的 pods 都是同等对待的。这样确实能够节省一些内存,但是一个 Agent 可能要服务同一个节点上的数百个 pods。

Sidecar 是在应用 pod 中增加其他服务,在Kubernetes 中服务是以 pod 为基本单位的,但是一个 pod 中可以包含多个容器, 这通常可以用来实现嵌入一些基础设施服务, 在 Sidecar 方式部署下,对于 Jaeger Agent 会作为 pod 中的一个容器和 tarcer 并存,由于运行在应用级别,不需要额外的权限,每一个应用都可以将数据发送到不同的 Collector 后端,这样能保证更好的服务扩展性。

总结来说,基于你的部署架构,如果是私有云环境,且信任 Kubernetes 集群上运行的应用,可能占用更少内存的 DaemonSet 会适合你。如果是公有云环境,或者希望获得多租户能力,Sidecar 可能更好一些,由于 Agent 服务当前没有任何安全认证手段,这种方式不需要在 pod 外暴露Agent服务,相比之下更加安全一些,尽管内存占用会稍多一些(每个 Agent 内存占用在20M以内)。


1)Agent 以 DaemonSet 模式部署

DaemonSet 方式部署会有一个问题,如何保证应用能够和自己所在节点的Agent通讯?

为解决通讯问题,Agent需要使用主机网络(hostNetwork), 应用中需要借用 Kubernetes Downward Api 获取节点IP信息。

DaemonSet 模式部署 Agent:

   1apiVersion: apps/v1   
2kind: DaemonSet
3metadata:
4  name: jaeger-agent
5  labels:
6    app: jaeger-agent
7spec:
8  selector:
9    matchLabels:
10      app: jaeger-agent
11  template:
12    metadata:
13      labels:
14        app: jaeger-agent
15    spec:
16      containers:
17        - name: jaeger-agent
18          image: jaegertracing/jaeger-agent:1.12.0
19          env:
20            - name: REPORTER_GRPC_HOST_PORT
21              value: "jaeger-collector:14250"
22          resources: {}
23      hostNetwork: true
24      dnsPolicy: ClusterFirstWithHostNet
25      restartPolicy: Always


通过 Kubernetes Downward API 将节点的IP信息(status.hostIP) 以环境变量的形式注入到应用容器中:

   1apiVersion: apps/v1   
2kind: Deployment
3metadata:
4  name: myapp
5spec:
6  selector:
7    matchLabels:
8      app: myapp
9  template:
10    metadata:
11      labels:
12        app: myapp
13    spec:
14      containers:
15      - name: myapp
16        image: example/myapp:version
17        env:
18        - name: JAEGER_AGENT_HOST
19          valueFrom:
20            fieldRef:
21              fieldPath: status.hostIP


2)Agent以Sidecar模式部署

下面是以Sidecar模式运行的应用示例,官方也提供了自动注入Sidecar的机制,详细使用可以参考[12]:

   1apiVersion: apps/v1   
2kind: Deployment
3metadata:
4  name: myapp
5  labels:
6    app: myapp
7spec:
8  replicas: 1
9  selector:
10    matchLabels:
11      app: myapp
12  template:
13    metadata:
14      labels:
15        app: myapp
16    spec:
17      containers:
18        - name: myapp
19          image: example/myapp:version
20        - name: jaeger-agent
21          image: jaegertracing/jaeger-agent:1.12.0
22          env:
23            - name: REPORTER_GRPC_HOST_PORT
24              value: "jaeger-collector:14250"


这样Jaeger Agent将会监听 localhost:5775/localhost:6831/localhost:6832/localhost:5778 这些本地端口,通常你不需要再在client配置中指定连接的主机名或者端口信息,应为这都是默认值。


3.生成依赖调用关系图

Jaeger Query UI服务中的 dependencies 选项默认点开为空,需要运行 spark-dependencies 来生成依赖关系图。

spark-dependencies 是一个Spark job 可以通过聚合和分析存储中的 trace 数据,生成服务间的依赖关系图,并将依赖链接信息持久化到存储中。之后 Jaeger Query Dependencies 页面就可以显示服务之间的依赖关系。

  1# 可以手动只执行一次   
2kubectl run -it --rm jaeger-spark-dependencies --env=STORAGE=elasticsearch --env ES_NODES=http://jaeger-elasticsearch:9200 --env ES_NODES_WAN_ONLY=true --restart=Never --image=jaegertracing/spark-dependencies
3
4# 也可以创建 CronJob, 每天定点生成新的依赖图
5kubectl create -f deployment/kubernetes/spark-dependencies/jaeger-spark-dependencies-cronjob.yaml


04

应用示例

下面以Python Django项目为例在服务中集成 Jaeger。

安装必要的依赖:

  1pip install jaeger-client   
2pip install django_opentracing


Jaeger tracer 配置和初始化:

   1from jaeger_client import Config   
2from django.conf import settings
3
4
5def init_jaeger_tracer(service_name='your-app-name'):
6    config = Config(
7        config={
8            'sampler': {
9                'type': 'const',
10                'param': 1,
11            },
12            'local_agent': {
13                'reporting_host': settings.JAEGER_REPORTING_HOST,
14                'reporting_port': settings.JAEGER_REPORTING_PORT,
15            },
16            'logging': True,
17        },
18        service_name='django-example',
19        validate=True,
20    )
21    return config.initialize_tracer()
22
23
24# this call also sets opentracing.tracer
25jaeger_tracer = init_jaeger_tracer(service_name='example')


Django_opentracing配置, 在Django settings文件中增加以下配置:

   1import django_opentracing   
2
3...
4
5# 添加 django_opentracing.OpenTracingMiddleware
6MIDDLEWARE = [
7    'django_opentracing.OpenTracingMiddleware',
8    ... # other middleware classes
9]
10
11# OpenTracing settings
12
13OPENTRACING_SET_GLOBAL_TRACER = True
14
15# if not included, defaults to True.
16# has to come before OPENTRACING_TRACING setting because python...
17OPENTRACING_TRACE_ALL = True
18
19# defaults to []
20# only valid if OPENTRACING_TRACE_ALL == True
21OPENTRACING_TRACED_ATTRIBUTES = ['path', 'method']
22
23from example.service.jaeger import jaeger_tracer
24
25OPENTRACING_TRACER = django_opentracing.DjangoTracing(jaeger_tracer)


这样Django接收的每个请求都会生成一条单独的Trace,当前请求的path和method会以Span Tag的形式记录下来。

手动创建Span和记录调用信息等更详尽的使用方法,请参考官方使用文档。


05

监控和报警

当前Jaeger缺少自带的报警机制,但是由于存储可以使用Elasticsearch,配合Grafana就可以实现简单的报警监控。

Jaeger本身暴露了Prometheus 格式的metrics 信息, 配合 Grafana可以方便的监控 Jaeger本身的运行状态。


06

资源清理

演示完毕,最后做一下资源的清理和释放:

  1kubectl delete -f deployment/kubernetes/spark-dependencies   
2kubectl delete -f deployment/kubernetes/example
3kubectl delete -f deployment/kubernetes/jaeger
4kubectl delete -f deployment/kubernetes/elasticsearch
5kubectl delete service jaeger-example-hotrod-node-port
6kubectl delete service jaeger-query-node-port


This post first appeared on IT瘾 | IT社区推荐资讯, please read the originial post: here

Share the post

利用Jaeger打造云原生架构下分布式追踪系统

×

Subscribe to It瘾 | It社区推荐资讯

Get updates delivered right to your inbox!

Thank you for your subscription

×