Loading

(硬核中的硬核)链路追踪落地过程中的挑战与解决方案

🎬作者简介:大家好,我是蓝胖子🥇

☁️博客首页:博客园主页蓝胖子的编程梦

🌄每日一句:编程中我们会遇到多少挫折?表放弃,沙漠尽头必是绿洲。

插画风主题图书馆公众号封面首图__2024-02-01+15_14_04.jpeg

大家好,我是蓝胖子,关于链路追踪的demo的讲解文章其实很多,但是想把链路追踪真正融入到生产环境却不是那么容易,今天我们来聊聊链路追踪技术在实际落地过程中不得不解决的两个问题。

实际落地链路追踪时需要考虑的问题

我们来看看这两个问题:

  • 大量链路追踪数据的存储与查询
  • 研发改造成本比较大,需要对不同组件进行埋点统计

👇🏻👇🏻 下面给出我对于这两个问题的一些思考与解决方案。

采样方式的选择

首先是链路追踪数据的存储与查询问题,因为线上环境产生的链路追踪数据往往是及其庞大的,那么肯定是不能全量采集的,并且全量采集链路追踪数据也必然会导致很多没有追踪意义的数据被采集下来,增加了成本。

这就涉及到采样方式的选择,本质上减少采样的数据量,让采样的数据都有意义就能解决产生大量链路追踪数据的问题。

减少采样数据量是一个优化方向,如果减少了采样数据量以后,采样数据还是足够庞大就只能从架构上扩容来满足大量的数据采集的需求,比如如果是用es来存储trace数据,那么可以新增存储的es集群节点来满足大量trace数据存储的问题,并且将数据采集的collector,比如如果使用Opentelemetry,那么可以将Opentelemetry Collector 进行扩容来满足大量数据采集的问题。

关于采样方式的选择,一般分为两种方式,前置采样和后置采样,我们挨个来看看。

前置采样

前置采样是指在采集trace数据第一个节点的时候就判断数据是否被采集,举一个例子,如果有链路A,B,C,如果在A处决定了要对trace数据进行采样,那么这个采样策略会被B,C所继承,B,C也会被采样到。

前置采样比较好理解,也比较容易实现,但是前置采样在生产环境上的使用效果并不好,因为很多时候我们都是需要完整的链路才容易判断整条链路是不是应该被采集到,比如如果整条链路有报错,那么我们是希望整条链路是被采集下来的,但前置采样则无法在采集第一个阶段时就知道后续的span中有错误发生,这是前置采样的缺陷。为了不遗漏线上失败的(有意义的)trace数据,我们往往也会关闭点概率采样,这就导致使用前置采样时,要想确保错误发生时,trace数据一定被采集下来,那么就只有对trace数据进行全部采样了,这显然不是我们需要的。

后置采样

所以,我们来看看,关于链路追踪的另一种采样方式,后置采样,后置采样则是在等待整条链路全部被采集完毕后才会去判断整条链路是否需要被采集。

所以后置采样有个缓存trace数据的地方,拿Opentelemetry Collector的后置采样配置举例,tail_sampling 中的配置是关于后置采样的相关配置。

在接收到trace的span数据后,并不是马上决定该trace是否应该被采样,而是等待decision_wait时间后,将该trace相关的span从内存缓存中取出来判断是否应该被采集,内存不是无限增大的,num_traces限制了最大能存储的trace的条数。

注意📢,num_traces 不要设置的太小,否则容易在等待时间decision_wait内,达到num_traces的阈值从而导致未被处理的trace数据被丢弃。

processors:  
  tail_sampling:  
    decision_wait: 5s  
    num_traces: 1  
    expected_new_traces_per_sec: 1  
    policies:  
      [  
        {  
          name: test-policy-11,  
          type: boolean_attribute,  
          boolean_attribute: { key: slowsql, value: true }  
        }  
      ]

在上述配置中,设置了采样的策略,即采集到的span中如果有slowsql属性设置为true则认为该条trace应该被采集。

注意📢,只要一条trace轨迹中,有一个span被确认为需要被采集,那么整条trace都会被采集到。

可以注意到,通过后置采样,我们可以设计如下的一些有意义的采集策略,

  • 对请求链路中有错误发生的链路进行采集
  • 对有慢查询的链路进行采集
  • 对慢http请求的链路进行采集

通过这样的策略,我们最大化的减少无效数据的存储,能大大减少链路追踪的采集存储查询成本。接着来看下实施后置采样可以用哪些组件,下面我用一个业界比较常见的后置采样架构举例。

技术选型

后置采样能解决仅采集有效trace数据的问题,但不是所有链路追踪工具都具有后置采样的功能,比如jaeger就仅仅支持前置采样,在使用jaeger时, 需要配合Opentelemetry Collector,借用Opentelemetry Collector 的后置采样功能来进行trace数据的过滤,然后再由jaeger 去进行收集和展示。

整个采集架构类似于这样,

Pasted image 20240201115634.png

Opentelemetry Collector 将后置采样后的数据发往jaeger Collector 去进行收集,最后通过jaeger 的ui组件显示出来。这个架构也是jaeger官方推荐的一种架构模型。下面是其相关的部署模式链接。

https://www.jaegertracing.io/docs/1.52/architecture/

自动化监测的实现方案

上面通过后置采样的方案算是解决了大量trace数据采集的问题,接着我们来看看关于项目服务接入链路追踪时要做的基础组件改造问题。

目前golang 服务在接入链路追踪功能时,主要还是通过在基础框架中进行埋点处理,这涉及到一定的研发成本,和java不同,java的字节码注入技术可以实现对业务服务的零侵入接入链路追踪技术。

不过在golang这块,目前业界已经有开源项目opentelemetry-go-instrumentation ,它的目的是通过epbf技术在不修改服务代码的前提下,实现golang的自动化监控。大家可以关注下这块儿,不过目前它支持的自动化监控范围并不多,下面是该项目的访问地址。

https://github.com/keyval-dev/opentelemetry-go-instrumentation/tree/master

所以,总结下来,golang构建的服务,还是会更多的手动去改造基础框架,后续有空再介绍相关框架如何改造来支持链路追踪的吧。

posted @ 2024-02-01 15:26  蓝胖子的编程梦  阅读(294)  评论(0编辑  收藏  举报