如何在追踪里找回仪表板上看到的那个尖峰
一句话总结
指标和跟踪必须带有同名同值的属性,并且采样比例要写在跨度上,这样才能在跟踪里重新找到在仪表板上看到的峰值。
为什么需要它
故障复盘中提出的要求很简单。“错误率面板在 09:40 突然冲高,我们就打开那一刻失败的一个请求看看。”可是打不开。指标上带着标签 handler="/order/:id",而跨度上带着 http.target="/order/8821"。名称不同,值也不同。能把两者连起来的只有人的脑子,所以从峰值到跟踪没有路。
更糟的事在后面。有人说“用跨度来数不就行了”,从转储文件里算出了错误率,拿回来的数字是 29%。指标在同一区间指向的是 7.5%。过了很久才知道原因——那个服务是出错的请求全部保留,成功的请求每五个只保留一个。采样偏向错误一侧,用这份样本数出来的比例不可能正确。
工作原理
要把两个信号配对,需要三样东西。
第一,公共属性必须以相同的名称和相同的值同时出现在两边。指标出于基数的考虑,不能直接使用地址,而是使用已登记的路径模板。跟踪原本可以把地址原样放进去,但用于配对的属性必须与指标的值相同。所以跨度上要同时放两样东西——用于配对的 http.route,和用于调查的原始地址。名称由 HTTP 跨度语义约定 和 HTTP 指标语义约定 统一定为同一个 http.route,所以不要自己起名,直接用更好。
第二,比例不能用跨度来数。指标统计所有请求,跟踪却只保留样本。即使样本抽得均匀,按跨度数出来的个数也只是几分之一;如果样本偏向错误一侧,比例本身就整体错了。实验里会亲手用同一份数据做出 7.5% 和 29.0%。
第三,必须把采样概率写在跨度上,才能还原。一个跨度代表多少个请求,是它被保留的概率的倒数。以 1/5 的概率保留下来的跨度代表五个请求,以 1 的概率保留下来的跨度代表一个请求。把这些权重加起来,个数和比例大体就还原了——这种算法在标准里称为 adjusted count,定义见 概率采样规范。
也必须清楚哪些东西无法还原。样本中一个都没有进来的组合,仍然是 0——罕见的错误在仪表板上整个消失,就是这样发生的。分位数同样还原不了。权重修正的是个数,修正不了分布的形状,尤其是尾部,样本越少,抖动越厉害。所以个数和比例要从指标里读,跟踪用来找一个具体的例子才是正确的用法。
从指标走向跟踪的桥梁,是预先把那个例子挑出来。每个区间留下一个代表性请求的 trace_id,就能直接从面板跳到那条跟踪。这种方式的标准名称是 exemplar,暴露格式的规范见 Prometheus 暴露格式文档 和 OpenTelemetry 指标数据模型。不过在这个实验环境里,无法真正存储或查询 exemplar。这个镜像里根本没有 Prometheus,可观测性实验 Pod 的 Prometheus 也是关闭了 exemplar 存储功能运行的。所以这里只做到挑选代表和制作那张表,指标作为暴露格式文本文件来处理。
限制也照实写下。代表每个区间只有一个,所以无法跳到普通的请求,而且被样本丢掉的请求,不管多慢,都不可能成为代表。
这里不讨论的内容也要说清楚。指标 SDK 的累积与增量的选择、视图的配置,是指标时间性模块的事;决定把哪些事件作为 SLI,是 SLI 模块的事。本模块做的是两者之间——把埋点做成两个信号可以配对,产出的不是指标配置,也不是 SLI 定义,而是属性规则文件和对照两个信号的检查器。
在现场相遇的样子
最常见的事故是路径值错位。指标一侧由框架自动放入路径模板,而跟踪一侧是手工埋点,常常把地址原样放进去。于是九条时间序列的指标,要和三十多个跨度分组面对面,根本没有办法配对。这种错位因为仪表板看起来一切正常,所以在出故障之前谁也不知道。
第二种是把错误全部保留的采样策略。出发点是好意,但一定会出现用这份样本去数比例的人。把采样概率写在跨度上,至少还有还原的路;不写,这份数据就永远不能用来计算比例。
下一项实验要做什么
先只用跨度转储文件数出请求数、错误数和延迟分布。然后与指标一侧给出的暴露格式文件对照,做一张表看名称和值是怎样错位的,修正埋点,让两个信号用同一个键关联起来。接着换着用采样器,亲手让同一份数据得出不同的错误率,用采样概率的倒数校正还原,再写下校正也修不了的东西。最后做一座为每个区间挑选代表性跟踪的桥梁,把规则固化成文件,应用到第二个服务,并用检查器确认两个信号是否一致。