APM

一秒开始OpenTracing

Posted by 聪少 on 2018-05-02

背景

随着应用架构的演变,从单体系统渐渐的转变为微服务架构,其中业务调用关系也转变为服务与服务之间的调用与请求。分布式监控系统也应运而起,随着Google大爷的Dapper论文的发布,市面上出现了一大批优秀的监控系统,当然它们各有优缺点。

  • Dapper(Google) : 各 tracer 的基础
  • StackDriver Trace (Google)
  • Zipkin(twitter)
  • Appdash(golang)
  • 鹰眼(taobao)
  • 谛听(盘古,阿里云云产品使用的Trace系统)
  • 云图(蚂蚁Trace系统)
  • sTrace(神马)
  • X-ray(aws)
  • jaeger(Uber)

当然本文的重点还是在opentracing。

OpenTracing

opentracing是一套分布式追踪协议,与平台,语言无关,统一接口,方便开发接入不同的分布式追踪系统。
为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。
OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。

opentrace_location

OpenTracing的优势

  • OpenTracing 已进入 CNCF,正在为全球的分布式追踪,提供统一的概念和数据标准。
  • OpenTracing 通过提供平台无关、厂商无关的 API,使得开发人员能够方便的添加(或更换)追踪系统的实现。

OpenTracing 数据模型

OpenTracing 中的 Trace(调用链)通过归属于此调用链的 Span 来隐性的定义。
特别说明,一条 Trace(调用链)可以被认为是一个由多个 Span 组成的有向无环图(DAG图),Span 与 Span 的关系被命名为 References。

例如:下面的示例 Trace 就是由8个 Span 组成:

span_1

有些时候,使用下面这种,基于时间轴的时序图可以更好的展现 Trace(调用链):
span_2
每个 Span 包含以下的状态:(译者注:由于这些状态会反映在 OpenTracing API 中,所以会保留部分英文说明)

  • An operation name,操作名称
  • A start timestamp,起始时间
  • A finish timestamp,结束时间
  • Span Tag,一组键值对构成的 Span 标签集合。键值对中,键必须为 string,值可以是字符串,布尔,或者数字 类型。
  • Span Log,一组 span 的日志集合。

    每次 log 操作包含一个键值对,以及一个时间戳。

键值对中,键必须为 string,值可以是任意类型。
但是需要注意,不是所有的支持 OpenTracing 的 Tracer,都需要支持所有的值类型。

  • SpanContext,Span 上下文对象 (下面会详细说明)
  • References(Span间关系),相关的零个或者多个 Span(Span 间通过 SpanContext 建立这种关系)

每一个 SpanContext 包含以下状态:

  • 任何一个 OpenTracing 的实现,都需要将当前调用链的状态(例如:trace 和 span 的 id),依赖一个独特的 Span 去跨进程边界传输
  • Baggage Items,Trace 的随行数据,是一个键值对集合,它存在于 trace 中,也需要跨进程边界传输。

一个完整的opentracing调用链包含 Trace + span + 无限极分类

  • Trace:追踪对象,一个Trace代表了一个服务或者流程在系统中的执行过程,如:test.com,redis,mysql等执行过程。一个Trace由多个span组成
  • span:记录Trace在执行过程中的信息,如:查询的sql,请求的HTTP地址,RPC调用,开始、结束、间隔时间等。
  • 无限极分类:服务与服务之间使用无限极分类的方式,通过HTTP头部或者请求地址传输到最低层,从而把整个调用链串起来。

当然opentracing只是一套标准,要玩转还需要配合Jaeger。

Jaeger

Jaeger 是 Uber 推出的一款开源分布式追踪系统,兼容 OpenTracing API。

Jaeger架构

Jaeger服务之间关系

  • Jaeger Client - 为不同语言实现了符合 OpenTracing 标准的 SDK。应用程序通过 API 写入数据,client library 把 trace 信息按照应用程序指定的采样策略传递给 jaeger-agent。
  • Agent - 它是一个监听在 UDP 端口上接收 span 数据的网络守护进程,它会将数据批量发送给 collector。它被设计成一个基础组件,部署到所有的宿主机上。Agent 将 client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节。
  • Collector - 接收 jaeger-agent 发送来的数据,然后将数据写入后端存储。Collector 被设计成无状态的组件,因此您可以同时运行任意数量的 jaeger-collector。
  • Data Store - 后端存储被设计成一个可插拔的组件,支持将数据写入 cassandra、elastic search。
  • Query - 接收查询请求,然后从后端存储系统中检索 trace 并通过 UI 进行展示。Query 是无状态的,您可以启动多个实例,把它们部署在 nginx 这样的负载均衡器后面。

Jaeger 存在的问题

  • 需要架设并维护存储。
  • UI比较薄弱,有一些个性化的分析需求无法快速满足(例如对比,统计延迟分布等)。

Jaeger Docker部署

用docker起一个示例Jaeger服务。

docker run -d -p 5775:5775/udp -p 16686:16686 jaegertracing/all-in-one:latest

访问 http://127.0.0.1:16686

如下图成功安装示例

Jaeger服务之间关系

示例代码

好了废话说了一堆直接上代码如何使用Opentracing并上报Jaeger。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
"log"
"time"

"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
)

func main() {
cfg := config.Configuration{
Sampler: &config.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &config.ReporterConfig{
LogSpans: true,
BufferFlushInterval: 1 * time.Second,
LocalAgentHostPort: "127.0.0.1:5775", // 数据上报地址
},
}
tracer, closer, err := cfg.New(
"Hello Jaeger",
config.Logger(jaeger.StdLogger),
)
if err != nil {
log.Panic(err)
}
opentracing.SetGlobalTracer(tracer)
defer closer.Close()

someFunction()

}

func someFunction() {
parent := opentracing.GlobalTracer().StartSpan("hello")
defer parent.Finish()

child := opentracing.GlobalTracer().StartSpan("world", opentracing.ChildOf(parent.Context()))
defer child.Finish()
}
1
2
3
4
5
go run main.go

2018/05/04 13:08:11 Initializing logging reporter
2018/05/04 13:08:11 Reporting span 3e27061246d7ffe6:3f98ef24d2fb1144:3e27061246d7ffe6:1
2018/05/04 13:08:11 Reporting span 3e27061246d7ffe6:3e27061246d7ffe6:0:1

然后到Jaeger去查询刚刚的调用,如下图:
span_hello_2
span_hello_4

OK ! 简单的示例就到这里结束了,这里Jaeger是通过docker部署的,官方在对于非docker部署方式的文档基本是无的,后面我会写一篇如果在Linux下通过二进制部署Jaeger,See you! (本文部分内容摘自阿里云以及官方文档)

OK! See you!