分布式链路追踪系统

一个链路追踪系统最重要的部分就是数据收集系统,然后还搭配着数据存储和数据展示的功能,当然你可以狭义的认为链路追踪系统就是数据收集系统也可以,不过通常一个链路追踪系统或者也叫做 APM 系统 (应用性能管理) 是包含了收集存储和展示的广义分布式链路追踪系统

从客户端的请求到达系统的边界开始,记录该请求流进的每一个服务,直到客户端的响应返回,这就是一个完整的链路追踪 (Trace)。

每次 Trace 都可能会调用数量不同、不同类型的的多个服务,那么为了能够记录具体调用了哪些服务,以及调用的顺序、开始时间、执行时长等信息,每次开始调用服务前,系统都要先埋入一个调用记录,这个记录就叫做一个跨度 (Span)

这是链路追踪最大的两个基本概念 “追踪” “跨度”

一般来说,每一次的 Trace 都是由若干个具有非线性关系且具有嵌套关系的 Span 组成的树状结构,你说有没有是线性关系的 span?应该也有。 trace-spans

链路追踪的目的主要有两个故障定位和分析性能

只要整理好每一个 trace 中各个 span 的调用关系,响应耗时和响应结果,就能定位出错误和性能。通常我们会绘制出来一个 trace 的 span 拓扑图,比如 go 语言的 pprof (opens new window) 工具就可以绘制出来一个 trace 的 span 拓扑图,这样就能直观的看到整个系统的调用关系,以及每个 span 的耗时,pprof 中的火焰图非常直观的能分析出性能问题。

如果把此次 trace 的数据跟往期对比,大概就能横向对比出整个系统的性能走向了。

trace 在实现上有以下四个难点,或者说必须突破的点:

  • 必须低损耗,不能影响业务
  • 对应用本身是透明的,不能干涉业务代码,通常链路追踪都是运维在后期加入的,不能直接在业务代码中写进去
  • 要求自动扩容,跟随应用的扩容而扩容
  • 持续监控,要求 24 小时不间断的监控整个系统。

链路追踪数据收集的三种方法

基于日志的追踪

基本原理就是将 trace 和 span 的内容直接记录在日志中,然后跟随所有节点的内容汇集分析,从全文日志中去分析出 trace 的拓扑图和各种信息,好处就是对于对网络消息完全没有侵入性,对应用程序只有很少量的侵入性,对性能的影响也非常低,坏处就是日志系统本身并不要求绝对的连续性和完整性以及一致性,实时性,所以可能造成追踪丢失的情况,小公司或者小项目可以使用这种方式,大公司或者大项目不推荐使用这种方式。

如果要使用这种方案,elastic stack 是一个不错的选择,因为 elastic stack 已经集成了日志收集和日志分析,所以只需要把日志收集到 elastic stack 中,然后就可以分析出整个系统的调用关系,以及每个 span 的耗时。

基于服务的追踪

通过某些手段给目标应用注入追踪探针,是基于服务的追踪的基本原理。

探针是一个寄生于服务上的一个独立系统,通常是一个小型分布式服务,它有自己的注册中心,心跳检测等系统,它可以从目标系统中监控得到的服务调用信息,通过另一次独立的 HTTP 或者 RPC 请求,发送给追踪系统,所以这种方法的侵入性较强同时消耗资源也大,但优点也是好的很啊,它可以从应用最底层去监控整个系统,而且监控的实时性,完整性,精度都非常的高,并且跟日志系统完全不牵涉,所以适合于大型分布式系统。

比如 Jaeger (opens new window) 就是一个基于服务的追踪系统,它可以通过注入探针的方式,监控整个系统的调用关系,以及每个 span 的耗时。

基于服务网格中边车代理的追踪

我们在使用服务网格的时候,通常会为每个服务都部署一个边车代理,边车代理是一个轻量级的服务,它负责拦截请求,记录请求的追踪信息,然后将追踪信息发送给追踪系统。

它也有自己独立的数据通道,追踪数据通过控制平面进行上报,避免了追踪对程序通信或者日志归集的依赖和干扰,保证了最佳的精确性。

所以这种方式也是基于服务的追踪的基本原理,但是它不需要在目标服务中注入探针,所以侵入性低,消耗资源低,但是它需要部署边车代理,并且无法监控本地应用级的的调用关系,仅仅可以监控服务级别的调用关系。

通常我们使用 istio 服务网格跟 Jaeger 结合使用,istio 会自动为每个服务部署边车代理,然后 Jaeger 就可以通过边车代理监控整个系统的调用关系,以及每个 span 的耗时。相当于 Jaeger 不用直接埋点到服务中,直接接入 istio 的控制系统就可以了,如果要持久化存储,那么就需要部署一个 Jaeger 的存储组件,比如 ElasticSearch。然后还可以通过 kibana 去展示存储在 elasticsearch 中的数据。

为什么 Istio 需要与 Jaeger 集成?

​ 服务网格的透明化追踪需求 Istio 通过 Envoy Sidecar 代理自动拦截服务间通信,但需要追踪系统 (如 Jaeger) 将分散的调用信息整合为完整的请求链路图。

​ 跨服务上下文传递 Istio 自动注入和传播追踪 Header (如 x-b3-traceid),而 Jaeger 负责解析这些上下文,还原跨服务、跨集群的调用路径。

​ 性能优化与故障排查 Jaeger 的甘特图可视化能力可清晰展示每个微服务的耗时 (Span),帮助识别高延迟服务或异常重试。

运行流程如下

  • Envoy Sidecar:拦截流量,生成初始 Span 并透传 TraceID
  • Jaeger Agent:以 DaemonSet 形式运行于每个节点,收集 Sidecar 的 Span 数据
  • Jaeger Collector:聚合数据并存储至后端 (如 Elasticsearch)
  • Jaeger UI:提供可视化查询界面,展示链路图、甘特图等

链路追踪系统架构的规范化设计

比如该怎样进行埋点、Span 上下文具体该有什么数据结构、怎样设计追踪系统与探针或者界面端的 API 接口等等,这些都是需要规范化的设计的。

目前的标准是 OpenTelemetry (opens new window)