Distributed Tracing
Table of Contents
1. 分布式链路追踪
“链路追踪”源于 2010 年 Google 发表的论文 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure 。
1.1. 为什么需要链路追踪
采用微服务架构开发时,微服务之间的调用链可能会很复杂,通过追踪调用链,我们能直观地观察出各微服务间的调用关系,快速定位出问题的具体服务,同时调用链还可以帮助我们:进行调用耗时分析、可视化错误、进行链路优化等等。
1.2. 核心概念:Span
在微服务环境中,如果想知道一个接口在哪个环节出现了问题,就必须清楚该接口调用了哪些服务,以及调用的顺序,如果把这些服务串起来,看起来就像链条一样,我们称其为调用链。想要实现调用链,就要为每次调用做个标识,然后将服务按标识大小排列,可以更清晰地看出调用顺序,我们将这个调用标识就记作 Span,每个 Span 都有一个唯一的 ID,记为 Spanid,如图 1 所示。
Span 是一个追踪的记录单元,它可以是一个 http 请求,也可以是一个本地函数调用,也可以是一个 rpc 调用或者数据库访问。
Figure 1: Spanid
实际场景中,我们需要知道某次请求调用的情况,所以只有 spanid 还不够,得为每次请求做个唯一标识,这样才能根据标识查出本次请求调用的所有服务,而这个标识我们命名为 traceid,如图 2 所示。
Figure 2: Traceid
现在根据 spanid 可以轻易地知道被调用服务的先后顺序,但无法体现调用的层级关系,正如图 3 所示,多个服务可能是逐级调用的链条,也可能是同时被同一个服务调用。
Figure 3: No parentid
所以应该每次都记录下是谁调用的,我们用 parentid 作为这个标识的名字。
Figure 4: Parentid
到现在,已经知道调用顺序和层级关系了,但是接口出现问题后,还是不能找到出问题的环节,如果某个服务有问题,那个被调用执行的服务一定耗时很长,要想计算出耗时,上述的三个标识还不够,还需要加上时间戳,如图 5 所示。
Figure 5: Timestamp
综上所述,Span 可以由下面 Golang 代码表示:
type Span struct { TraceID int64 // 用于标识一次完整的请求id Name string ID int64 // 当前这次调用span_id ParentID int64 // 上层服务的调用span_id,最上层服务parent_id为null Annotation []Annotation // 用于标记的时间戳 }
注:本节图片和文字整理自博文 技术分析:搞懂链路追踪
2. 侵入式 VS. 非侵入式
我们可以在应用程序代码中增加分布式追踪的相关代码,这称为埋点。这种方式是侵入式的。
除了侵入式的方式外,还有非侵入式的方式来实现链路追踪,如:
- 字节码注入(如 pinpoint、skywalking),适用于 Java 等动态语言。
- Service Mesh。Service Mesh 一般会被翻译成“服务啮合层”,它是在网络层面做文章,通过 Sidecar 的方式为 Pod 增加一层代理,通过这层网络代理来实现一些服务治理的功能,因为是工作在网络层面,可以做到跨语言、非侵入。Istio 则是目前最成熟的 Service Mash 工具,支持启用分布式追踪服务。Istio 会修改微服务之间发送的网络请求,在请求中注入 Trace 和 Span 标记,再将采集到的数据发送到支持 OpenTracing 的分布式追踪服务中,从而拿到请求在微服务中的调用链。 当然非侵入式也有缺点,它无法追踪某个微服务内部的调用过程,并且目前阶段 Istio 只能追踪 HTTP 请求,能够覆盖的范围比较有限。 如果想追踪更详细的数据,还是需要在中间件和代码中埋点(侵入式),不过好在埋点的过程并不复杂,不会成为一个额外的负担。
参考:https://pjw.io/articles/2018/05/08/opentracing-explanations/
3. Tracing 相关标准
Tracing 的标准主要有三个:
- OpenTracing,这个标准 2016 年就开始启动了,为了解决 tracing 系统各自为政,搞了个统一的标准,并提供了各种语言的 api。目前的主要参与者为 Uber,LightStep(Dapper 第一作者的公司),Redhat(Jaeger 开源后,直接放弃自家研发的类似产品,转到了 Jaeger)等公司。
- OpenCensus。这里 Google 2018 年初提出的一个集 metrics 和 tracing 的新标准,估计一方面是看到 OpenTracing 想竞争一把,就像之前的容器编排一样,另外也可以推广自己家的 Stackdriver,目前微软也加入了对该标准的支持,准备搞到自己的 Auze 里面。
- w3c/distributed-tracing,这里 2017 年提出的一个为了统一各种 tracer 系统中 Context 格式标准,目前还是 Draft 阶段,主要参与者为 Google 和微软。
其中,OpenTracing 的实现最为广泛,如:Jaeger、Appdash、LightStep、Instana、SkyWalking、inspectIT、stagemonitor、Datadog 等等。
注: OpenTracing 和 OpenCensus 目前已经合并成为了一个新标准:OpenTelemetry (informally called OTEL or OTel)。
参考:http://billowqiu.github.io/2018/10/21/distrituted-tracing-history/
4. Jaeger
Jaeger 是 Uber 推出的一款开源分布式追踪系统,兼容 OpenTelemetry API,其架构如图 6 所示,其论文参见:Evolving Distributed Tracing at Uber Engineering
Figure 6: Jaeger 架构图
Jaeger 中的 jaeger-agent 组件,部署在宿主机或容器中,专门负责向 collector 异步上报 Span 数据。
Jaeger 有个很有用的特性:支持设置采样率。我们可以设置仅对指定比率(如 50%)的请求开启 Trace,这可以减轻 Trace 功能对系统带来的压力。
在 Docker 中部署 Jaeger 测试环境很简单:
docker run -d --name jaeger \ -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:1.12