Jaeger-分布式调用链跟踪系统理论与实战

栏目: 服务器 · 发布时间: 6年前

内容简介:调用链跟踪系统,又称为tracing,是微服务设计架构中,从系统层面对整体的monitoring和profiling的一种技术手段,而Jaeger则是Uber开发的新一代tracing系统。由于我们的项目是微服务方向,中后台服务现在已经有10多类左右服务模块,且各个服务/模块之间的调用关系复杂,部分服务与服务之间还存在一些proxy服务(实现服务的多活部署)。这些现象就导致在开发调试、问题跟踪上都会逐步出现问题。因此,前段时间对当前微服务中较流行的两款开源分布式tracing系统:Zipkin和Jaege

导语

调用链跟踪系统,又称为tracing,是微服务设计架构中,从系统层面对整体的monitoring和profiling的一种技术手段,而Jaeger则是Uber开发的新一代tracing系统。

1. 背景说明

由于我们的项目是微服务方向,中后台服务现在已经有10多类左右服务模块,且各个服务/模块之间的调用关系复杂,部分服务与服务之间还存在一些proxy服务(实现服务的多活部署)。这些现象就导致在开发调试、问题跟踪上都会逐步出现问题。因此,前段时间对当前微服务中较流行的两款开源分布式tracing系统:Zipkin和Jaeger分别进行了调研。

Zipkin( githubhomepage ),是一款由 java 开发的分布式追踪系统。在微服务架构下,它用于帮助收集排查潜在问题的时序数据,同时管理数据收集和数据查询。

Jaeger( githubhomepage ),则是受Dapper和OpenZipkin启发,由Uber使用golang开发的分布式跟踪系统。由于我们项目的技术栈为golang,所以重点调研了Jaeger并在此列出。

2. 跟踪链

首先,简要说下什么是跟踪链,如果有相关基础的同学可以略过,或者也可以快速过一下该段。基本上,任何说到跟踪链相关的文章,都会贴出下面这张图:

图 1. 一个简单的tracing调用过程

图1是Google的Dapper论文里的一个例子:图示中,A~E五个节点表示五个服务。用户发起一次请求RequestX到A,同时由于该请求依赖服务B与C,因此A分别发送RPC请求到B和C,B处理完请求后会直接返回到A,但是服务C还依赖服务D和E,因此还要发起两个RPC请求分别到D和E,D和E处理完毕后回到C,C才继续回复到A,最终A会回复用户ReplyX。对于这样一个请求,简单实用的分布式跟踪的实现,就是为服务器上每一次发送和接收动作来收集跟踪标识符和时间戳。

在Dapper这篇论文中,Trace和Span是两个很重要的名词。我们使用Trace表示对一次请求完整调用链的跟踪,而将两个服务例如上面的服务A和服务B的请求/响应过程叫做一次Span,trace是通过span来体现的, 通过一句话总结,我们可以将一次trace,看成是span的有向图,而这个有向图的边即为span。为了可以更好的理解这两个名词,我们可以再来看一下面的调用图。

[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信息

图 2. 某次tracing过程的spans关系图

––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
 [Span A···················································]
   [Span B··············································]
      [Span D··········································]
    [Span C········································]
         [Span E·······]        [Span F··] [Span G··] [Span H··]

图 3. Spans的时间轴关系图

而分布式跟踪系统要做的,就是记录每次发送和接受动作的标识符和时间戳,将一次请求涉及到的所有服务串联起来,只有这样才能搞清楚一次请求的完整调用链。

3. OpenTracing

在具体介绍Jaeger之前,还需要赘述一下另一个关键词:OpenTracing。

一句话总结, OpenTracing 是一套标准,它通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现(我们在测试使用中是基本上通过两行代码的更改就可以在Zipkin和Jaeger之间切换)。OpenTracing提供了用于运营支撑系统的和针对特定平台的辅助程序库。程序库的具体信息请参考详细的规范。OpenTracing 已进入 CNCF (云原生计算基金会,著名的Kubernetes、gRPC和Prometheus等均孵化于此),正在为全球的分布式追踪,提供统一的概念和数据标准。

当然,本文之前提到的Zipkin和Jaeger,都是支持OpenTracing标准的。

3.1 引用关系

OpenTracing标准中目前定义了两种类型的引用:

  1. ChildOf; 
  2. FollowsFrom;  两种参考类型都具体模拟了child span与parent span之间的直接因果关系。  未来,OpenTracing还可能支持带有非因果关系的Spans的引用类型(例如,一起批量化的Spans,停留在同一队列中的Spans等)。

3.1.1 ChildOf引用

一个span A可以是ChildOf另一个span B。以下所有内容均构成ChildOf关系:

  1. 代表RPC服务器端的Span,可以是ChildOf代表该RPC客户端的span 
  2. 代表 SQL 插入的Span,可以是ChildOf代表ORM save方法的Span  通过时序图来看,如下所示:
[-Parent Span---------]
         [-Child Span----]
    [-Parent Span--------------]
         [-Child Span A----]
          [-Child Span B----]
        [-Child Span C----]
         [-Child Span D---------------]
         [-Child Span E----]

图 4. Span的ChildOf引用

3.1.2 FollowsFrom引用

一些parent Spans不以任何方式依赖他们的child Spans的结果。 在这些情况下,我们只说child Span从因果意义上来自parent Span。

4. Jaeger介绍

如果细心的话,通过上面CNCF的地址,我们可以发现,Jaeger现在也成为了CNCF的孵化项目。

为了深入了解下jaeger的工作原理,首先,我们来看一下Jaeger的架构设计图:

图 5. Jaeger架构设计图

在上图,我们看到Jaeger系统中:黄色部分是我们的应用程序代码;红色部门表示instrument操作,即把我们应用程序与jaeger-client装载起来,从而开始了应用程序到Jaeger的的数据交互操作。将该部分放大来看,我们可以参考下图,了解详细的数据交互方式:

图 6. Jaeger的instrumentation过程

在图5中,我们可以观察到Jaeger的完整的概览设计。从中我们会发现Jaeger有5个模块元素,如下所列出,接下来我们来分别解释这五个模块的作用:

1.Jaeger-client
2.Agent
3.Collector
4.Data Store
5.UI

Jaeger-client(客户端库)

client是客户端lib,便于不同语言的项目来介入到Jaeger中,当我们的应用程序装载上之后,client会负责收集并发送数据到Agent。当前Jaeger的SDK支持有如下:

--官方 
1.Go 
2.Java 
3.Node 
4.Python 
5.C++ 
--非官方 
1.PHP
3.Others

client是支持OpenTracing标准的,如上文提到,Zipkin也是支持OpenTracing标准的,这也就意味着,我们的应用程序在嵌入Jaeger-client的地方,可以随时替换成Zipkin,对业务是完全透明的。

Agent(客户端代理)

jaeger的agent,是一个监听在 UDP 端口上接收 span 数据的网络守护进程。 如同大多数分布式系统都拥有一个Agent一样,Jaeger的Agent有以下几类特点:

agent收集并汇聚这些span信息到collector;

agent的被设计成一个基础组件,旨在 作为基础架构组件部署到所有宿主机

agent将client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节;

Collector(数据收集处理)

collector,顾名思义,从agent收集traces信息,并通过处理管道处理他们,再写入后端存储(backends)。

当前的collector工作主要是管理trace,建立索引,执行相关转换,并最终存储它们。

Collector中运行着sampling逻辑,根据我们设定的sampling方式对数据进行收集和处理。

Data Store(数据存储)

jaeger的data store是组件的方式。

当前可以支持 Cassandra和ElasticSearch(当然也支持纯内存方式,但是不适用于生产环境).

Query & UI(数据查询与前端界面展示)

Query查询是一种从存储中检索trace,并提供UI以显示它们的服务。上图中就展示了一次Trace的数据流向,作为一次系统作用的数据传播/执行图,即可以在Jaeger UI上展示出来。

5. 部署方式

Jaeger的部署由于方案的不同,会依赖不同的服务,这些第三方基础服务的部署安装不再该文范围内,如 docker 、Elasticsearch、Cassandra等

5.1 All in one

为了方便大家快速使用,Jaeger直接提供一个All in one的docker镜像,通过All in one的image,我们可以通过以下命令直接启动一套完整的Jaeger tracing系统:

$ docker run -d -e \
  COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 9411:9411 \
  jaegertracing/all-in-one:latest

一旦启动成功后,就可以去http://localhost:16686看到Jaeger UI了,如下所示。

图 7. Jaeger UI的首页

注:在All in one模式下,Data Store使用的是内存,因此若重启dockre容器后就看不到之前的数据了。所以,该模式仅可用于前期demo或者验证,不可在生产环境中这样部署。

5.2 独立部署

当然我们更推荐的方式是独立部署,独立部署也可以分为docker镜像方式和binary 方式,官网有详细的 docker镜像方式启动命令介绍 ,在这儿就不再粘贴复制了。

关于binary方式部署,可以参看github上的 Jaeger的二进制包 。地址提供了mac、 linux 和windows的三大操作系统的binary包。以linux为例,解压后我们可以发现有以下几个bin包,分别清晰地对应了我们之前说的几个模块:

drwxrwxr-x 3 2000 2000 4.0K May 28 23:29 jaeger-ui-build
-rwxrwxr-x 1 2000 2000  27M May 28 23:29 jaeger-standalone
-rwxrwxr-x 1 2000 2000  22M May 28 23:29 jaeger-query
-rwxrwxr-x 1 2000 2000  25M May 28 23:29 jaeger-collector
-rwxrwxr-x 1 2000 2000  16M May 28 23:29 jaeger-agent

注:Jaeger同时也提供可Kubernetes and OpenShift的模板。可参考github地址有详细介绍

5.3 端口说明

通过上述All in one启动方式,我们直接发现了Jaeger启动时占据了很多端口,当然,并不是所有的端口都是必需的,这儿简单列出这些端口的说明如下:

端口	协议	所属模块	     功能
5775	UDP	agent	     通过兼容性Thrift协议,接收Zipkin thrift类型数据
6831	UDP	agent	     通过兼容性Thrift协议,接收Jaeger thrift类型数据
6832	UDP	agent	     通过二进制Thrift协议,接收Jaeger thrift类型数据
5778	HTTP	agent	     配置控制服务接口
16686	HTTP	query	     客户端前端界面展示端口
14268	HTTP	collector    接收客户端Zipkin thrift类型数据
14267	HTTP	collector    接收客户端Jaeger thrift类型数据
9411	HTTP	collector    Zipkin兼容endpoint

6. Sampling

关于Jaeger系统中的采样方式,我们可以通过一个例子来解释。

假设有三个服务A,B,C,且存在一个简单的调用方式:A->B->C, 当服务A收到请求时,Jaeger检查该请求有没有trace信息,如果没有,将为其生成新的trace(TraceId为随机生成的),并基于当前的取样策略进行sampling。该取样策略会随着请求一路广播到服务B和C,因此这些服务将必须再做采样的策略选择。这种采样方式确保了当一个trace被采用后,它的所有后续spans都会被存储起来(若每层服务都再做一次采样策略选择的话,我们就会很难获取到完整的一个trace了)。

7. 代码侵入性

关于装载的方法,不同语言在网上的代码示例都很多。对于tracing这种监控类系统,一定不能对我们的业务代码侵入太高,因为我们的项目代码为golang,下面以golang代码中grpc下,jaeger的代码侵入性比较(为了提高可读性,直接展示出diff信息):

图 8. tracing的代码侵入展示

我们看到,利用grpc的interceptor功能,jager只需要在拦截器中插入一个interceptor即可,整体代码的影响之有5行左右。(注:在业务代码中,grpc使用context来携带信息)

8. Jaeger使用

当我们正是使用jager后,可以通过两种方式来进行查看:

根据TraceId搜索

通过Web UI左上方,可以直接键入TraceId进行某次trace的搜索

根据服务节点查看

通过Web UI左边栏Find Traces,可以详细地进行高级搜索功能,支持服务名,操作,Tag信息(Jaeger中的tag功能,可以在context中加入tag,进行更过的标识)等。当我们确定搜索条件后,就可以查出符合条件的trace信息了,下图为我们一个腾讯云cos代理业务程序的简单请求示例:

图 9. Jaeger的使用展示

上面就是本次文章的所有内容,有感兴趣或有疑问的小伙伴欢迎讨论和留言


以上所述就是小编给大家介绍的《Jaeger-分布式调用链跟踪系统理论与实战》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Kubernetes权威指南

Kubernetes权威指南

龚正、吴治辉、王伟、崔秀龙、闫健勇、崔晓宁、刘晓红 / 电子工业出版社 / 2016-10 / 99

Kubernetes是由谷歌开源的Docker容器集群管理系统,为容器化的应用提供了资源调度、部署运行、服务发现、扩容及缩容等一整套功能。《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第2版)》从一个开发者的角度去理解、分析和解决问题,囊括了Kubernetes入门、核心原理、实践指南、开发指导、高级案例、运维指南及源码分析等方面的内容,图文并茂、内容丰富、由浅入......一起来看看 《Kubernetes权威指南》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器