Elastic 如何利用磁盘支持的追踪存储将 OpenTelemetry 尾部采样内存占用降低 65%

作者:来自 Carson Ip

Elastic 向 OTel Collector 的尾部采样处理器上游贡献了两项功能。 span-ingest 策略让采样决策能够更早发生,而 Pebble 尾部存储则将追踪缓冲区迁移到磁盘。虽然这样会消耗更多 CPU,但运维人员可以提高 decision_waitnum_traces,而无需担心因内存耗尽( OOM )而导致进程被终止。

Elastic 向 OpenTelemetry Collector 的尾部采样处理器( tailsamplingprocessor )上游贡献了两项改进,最多可将内存使用量降低 65%。sampling_strategy: span-ingest 让采样决策能够在数据摄入时发生,在 decision_wait 到期之前释放追踪数据。pebbletailstorageextension 将追踪缓冲移动到磁盘上的 Pebble LSM 数据库中,使存储能力由磁盘容量决定,而不是由 RAM 决定。这意味着运维人员可以增加 decision_waitnum_traces,而无需担心因内存耗尽( OOM )导致进程被终止。代价是大约增加 2 倍 CPU 消耗。

什么是尾部采样?

分布式追踪对于调试非常有用,但在生产规模下,它会带来处理开销和存储成本,此时采样成为一种自然的方法,可以在控制成本的同时保持追踪的价值。基于尾部的采样( tail-based sampling ),也称为尾部采样,是一种在稍后阶段根据条件做出采样决策的技术,因此错误或慢事务等高价值追踪更有可能被采样。与之相反的是头部采样( head sampling ),它在追踪开始时就做出决策,此时还无法获得这些信息。

尾部采样处理器如何工作?

尾部采样处理器会缓冲 100% 的输入追踪数据(或 span,两者可以互换使用),然后在应用采样策略后转发被采样的子集。缓冲是内存使用的重要来源,并且它会随着 span 数量增长而按比例增加,这是社区中众所周知的痛点。

内存使用量受到 decision_waitnum_traces 等配置参数限制。将 decision_wait 设置为 1 分钟意味着系统会在 1 分钟后对一个追踪做出采样决策,在此期间,该追踪的所有 span 都应该已经到达。如果一个追踪耗时超过 1 分钟,那么做出决策时会缺少部分 span。

补充说明一下,扩展尾部采样部署规模需要使用 loadbalancingexporter,以满足一个要求:同一个追踪中的所有 span 必须被路由到同一个 collector。这会引入一些运维复杂性,并且在 collector 重启期间可能导致数据丢失。但本文关注的是单个尾部采样处理器实例的内存使用情况,而不考虑水平扩展。

为什么尾部采样会导致内存压力?

这些参数在数据丢失和内存使用之间引入了权衡,并且它们需要基于对追踪形态的假设:追踪可能持续多慢、包含多少 span、每个 span 有多大。随着埋点方式不断演进,这些假设可能会逐渐失效。

为了限制内存使用,可以接受多少数据丢失?这种权衡是否可以进一步优化?下面介绍的两个贡献旨在为运维人员提供更大的灵活性。

span-ingest 如何通过提前释放 span 降低尾部采样内存使用

sampling_strategy 是尾部采样处理器中新增加的配置选项,加入于 v0.149.0

sampling_strategy 默认值为 trace-complete ,这与原始行为一致:只有当 decision_wait 时间结束后,采样策略才会被评估,此时追踪被认为已经完成。(还有一个类似的配置 decision_wait_after_root_received 用于优化,但为了简单起见,本文不展开讨论。)这意味着无论是否能够更早做出决策,所有 span 都会在内存中缓冲大约 decision_wait 时间,然后才被释放。例如,应该始终被丢弃的健康检查 span 仍然会一直保存在内存中,直到策略评估时间到达。

另一种方式是将 sampling_strategy 设置为 span-ingest ,此时 span 会在摄入时被单独评估。这允许更早做出终止决策,特别是 dropsampled 决策,通过在 decision_wait 到期之前丢弃或导出该追踪目前已经缓冲的所有 span 来释放内存。在健康检查示例中,可以配置一个策略:只要根 span 属于健康检查,就立即丢弃整个追踪。需要注意的是,与明确的 drop 不同,unsampled 决策不是终止性的,因为它可能被同一个追踪中另一个 span 的 sampleddrop 决策覆盖,因此 unsampled 追踪无法提前释放。

trace-complete 切换到 span-ingest 需要调整策略,因为策略不能再假设评估时所有 span 都已经可用。此外,并非所有策略类型都支持 span-ingest 策略。

使用 Pebble 的磁盘支持尾部采样存储

即使使用 span-ingest ,所有 span 仍然会被缓存在内存中。当增加 decision_wait 以适应慢速追踪,并增加 num_traces 以减少数据丢失时,collector 最终仍然会达到内存限制并被 OOM 终止,从而导致进一步的数据丢失。

如果追踪数据改为缓存在磁盘上会怎样?磁盘通常具有高出一个数量级的容量。主要缺点是性能:即使使用 SSD,磁盘吞吐量和延迟也至少比内存慢一个数量级,因此磁盘写入必须非常高效。出于这个原因,选择了 LSM 数据库 Pebble 作为存储后端,因为它具有快速写入性能。当 sampling_strategy 设置为 span-ingest 时,读取只会发生在被采样的追踪子集上,因此读取性能的重要性较低。

该实现引入了用于追踪存储操作的 TailStorage 接口,并在 v0.150.0 中新增了 tail_storage 选项(通过功能开关 processor.tailsamplingprocessor.tailstorageextension 启用),用于配置存储后端。默认的内存存储行为保持不变,但现在可以替换为其他存储后端,例如由 Elastic 贡献给 Collector Contrib 的新 pebbletailstorageextension

尾部采样内存基准测试:使用 Pebble 的 trace-complete 与 span-ingest 对比

基准测试设置:带扇出的 OpenTelemetry Demo

下面的基准测试通过运行 OpenTelemetry Demo,并增加负载到一个管道 collector 来生成。该管道 collector 接收所有 span,并将它们分发到两个正在观测的相同 collector( CUO-ACUO-B ),两者唯一不同之处是尾部采样配置不同。测量指标包括管道 collector 吞吐量、接收的 span 数量、发送的 span 数量(已采样)、CPU 使用量以及内存使用量。

基准测试设置示意图

markdown 复制代码
 `1.                        demo ns
2.       +----------------------------------------+
3.       |  opentelemetry-demo                     |
4.       |    loadgenerator (locust)               |
5.       |    services: frontend, cart, ...        |
6.       |    demo-collector                       |
7.       +----------------------------------------+
8.                            |  OTLP/gRPC
9.                            v
10.                       chamber ns
11.       +----------------------------------------+
12.       |             pipe-collector             |
13.       |         receive once, fan out          |
14.       |      exporters: [otlp/a, otlp/b]       |
15.       +----------------------------------------+
16.                | OTLP                  | OTLP
17.                v                       v
18.       +----------------+      +----------------+
19.       |     CUO-A      |      |     CUO-B      |
20.       | tail_sampling  |      | tail_sampling  |
21.       |   (config A)   |      |   (config B)   |
22.       +----------------+      +----------------+`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

尾部采样处理器配置

CUO-A

yaml 复制代码
`

1.  config:
2.    processors:
3.      tail_sampling:
4.        sampling_strategy: trace-complete
5.        decision_wait: 5m
6.        num_traces: 5000000
7.        block_on_overflow: true
8.        decision_cache:
9.          sampled_cache_size: 10000
10.          non_sampled_cache_size: 200000
11.        policies:
12.          - name: root_1pct
13.            type: and
14.            and:
15.              and_sub_policy:
16.                - name: root_span_only
17.                  type: ottl_condition
18.                  ottl_condition:
19.                    error_mode: ignore
20.                    span:
21.                      - "IsRootSpan()"
22.                - name: root_probabilistic
23.                  type: probabilistic
24.                  probabilistic:
25.                    sampling_percentage: 1.0

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

CUO-B

CUO-B 使用与 CUO-A 相同的尾部采样处理器配置,唯一不同的是它设置了 sampling_strategy: span-ingesttail_storage: pebble_tail_storage/main ,以及与之对应的 pebbletailstorageextension 配置。

markdown 复制代码
`

1.  extensions:
2.    pebble_tail_storage/main:
3.      directory: /var/lib/otelcol/pebble

`AI写代码

内存、CPU 和吞吐量结果

下面的表格从内存、CPU 和吞吐量三个方面,对比了 trace-complete(CUO-A)与使用 Pebble 磁盘存储的 span-ingest(CUO-B)。进程 RSS、Go 堆分配以及每个进程的 CPU 测量数据来自 OpenTelemetry Collector 内部的进程和运行时指标,而容器工作集和容器 CPU 数据来自由 kubelet/cAdvisor 抓取的 Kubernetes cgroup 指标。

内存(时间窗口内的峰值)

指标cuo-acuo-bΔ(B 相比 A)
进程 RSS 916.4 MiB 442.7 MiB -51.7%
Go 堆分配 699.3 MiB 241.7 MiB -65.4%
容器工作集 763.0 MiB 282.9 MiB -62.9%

CPU(时间窗口内总量)

指标 cuo-a cuo-b Δ(B 相比 A)
每进程 CPU 11.9 core-s 22.7 core-s +90.7%
容器 CPU 11.9 core-s 22.7 core-s +90.1%
吞吐量(时间窗口内总量)
指标Δ(B 相比 A) cuo-a cuo-b Δ(B 相比 A)
接收的 span 数量 257,804 257,804 0.0%
发送的 span 数量 2,477 2,477 0.0%
尾部采样
指标 cuo-a cuo-b Δ(B 相比 A)
内存中的追踪峰值 29,284 29,245 -0.1%
被 root_1pct 策略采样的追踪数量 496 496 0.0%
  • cuo-a = trace-completecuo-b = span-ingest-pebble

  • 时间窗口:15 分 39 秒(t+0:00 开始,t+10:06 开始排空,t+15:39 排空结束)

结果显示,在使用 span-ingest 搭配 pebbletailstorageextension 时,内存使用量显著降低,但代价是由于事件序列化和数据库开销导致 CPU 使用量增加。

OpenTelemetry 尾部采样的下一步发展

在撰写本文时,sampling_strategypebbletailstorageextension 都仍处于早期阶段。欢迎在 OpenTelemetry Collector Contrib 仓库中提供反馈和贡献。敬请期待更多改进。

原文:OpenTelemetry tail sampling: 65% less memory with disk storage --- Elastic Observability Labs

相关推荐
.柒宇.2 小时前
ELK日志分析系统实战指南:Elasticsearch搭建与入门(8.18.8版本)
运维·elk·elasticsearch·logback·日志系统
Linux-187412 小时前
分布式链路追踪系统之二进制安装skywalking
elasticsearch·云原生·skywalking·分布式链路追踪系统·应用程序性能监控
阿里云大数据AI技术15 小时前
阿里云 Elasticsearch 日志采集与加工服务:让日志链路少一串组件,多一份稳定
人工智能·elasticsearch
heimeiyingwang17 小时前
【架构实战】可观测性三支柱:日志、指标、链路的融合
elasticsearch·架构·kubernetes
Elasticsearch19 小时前
Elastic Observability 中的 Prometheus 指标:你的 PromQL 可以保持不变地运行
elasticsearch
Elasticsearch20 小时前
为什么 Elasticsearch 平台是你的 AI 技术栈中缺失的一块拼图
elasticsearch
Elasticsearch20 小时前
数据平台赌注:为什么金融 AI 计划停滞,以及成功者如何扩展规模
elasticsearch
Elasticsearch21 小时前
在金融服务领域扩展 AI 从治理和架构开始
elasticsearch
java_logo1 天前
ELK Docker Compose 部署指南:轻松搭建日志检索平台
elk·elasticsearch·docker·容器·kibana·logstash·轩辕镜像