事务采样
在 Elastic Cloud Hosted 中,APM Server 接收来自 Elastic APM Agent 的数据并将其转换为 Elasticsearch 文档。而在 Elastic Cloud Serverless 中,实际上并没有运行 APM Server,而是由托管摄取服务 (managed intake service) 接收并转换数据。
分布式追踪可以生成大量的数据。更多的数据可能意味着更高的成本和更多的噪声。采样的目的是减少摄取的数据量以及分析这些数据所需的工作量——同时仍然能够轻松地查找应用程序中的异常模式、检测故障、跟踪错误并缩短平均恢复时间 (MTTR)。
Elastic APM 支持两种类型的采样
在基于头部的采样中,每个追踪的采样决策是在该追踪启动时做出的。每个追踪被采样的概率是确定且相等的。
例如,采样值为 .2 表示事务采样率为 20%。这意味着只有 20% 的追踪会发送并保留其所有关联信息。其余追踪将丢弃上下文信息,以减少追踪的传输和存储大小。
基于头部的采样设置快速且简便。它的缺点是完全随机——有趣的数据可能会纯粹由于机缘巧合而被丢弃。
请参阅配置基于头部的采样以开始使用。
在分布式追踪中,采样决策仍然在追踪启动时做出。每个后续服务都会遵循初始服务的采样决策,无论其配置的采样率如何;结果是采样百分比与发起服务相匹配。
在图 1 的示例中,Service A 发起四个事务,采样率为 .5 (50%)。上游采样决策得到了遵循,因此即使在 Service B 和 Service C 中定义了采样率且值不同,所有服务的采样率仍将是 .5 (50%)。
图 1. 遵循上游采样决策
在图 2 的示例中,Service A 发起四个事务,采样率为 1 (100%)。同样,上游采样决策得到了遵循,因此所有服务的采样率将为 1 (100%)。
图 2. 遵循上游采样决策
除了设置采样率之外,您还可以指定要使用的追踪延续策略。有三种追踪延续策略:continue、restart 和 restart_external。
continue 追踪延续策略是默认策略,其行为类似于分布式追踪部分中的示例。
在经 Elastic 监控的服务上使用 restart_external 追踪延续策略,以便在上一个服务没有包含 es 厂商数据的 traceparent 标头时启动新的追踪。如果某个事务包含一个接收来自未监控服务请求的经 Elastic 监控的服务,这会非常有用。
在图 3 的示例中,Service A 是一个经 Elastic 监控的服务,它发起四个事务,采样率为 .25 (25%)。由于 Service B 未受到监控,因此在 Service A 中启动的追踪将在那里结束。Service C 是一个经 Elastic 监控的服务,它发起四个事务,并以新的采样率 .5 (50%) 启动新的追踪。由于 Service D 也是一个经 Elastic 监控的服务,因此遵循了在 Service C 中定义的上游采样决策。最终结果将是三个被采样的追踪。
图 3. 使用 restart_external 追踪延续策略
在经 Elastic 监控的服务上使用 restart 追踪延续策略,无论上一个服务是否有 traceparent 标头,都启动新的追踪。如果经 Elastic 监控的服务是对外公开的,并且您不希望追踪数据可能被用户请求伪造,这会非常有用。
在图 4 的示例中,Service A 和 Service B 是使用默认追踪延续策略的经 Elastic 监控的服务。Service A 的采样率为 .25 (25%),该采样决策在 Service B 中得到了遵循。Service C 是一个使用 restart 追踪延续策略且采样率为 1 (100%) 的经 Elastic 监控的服务。由于它使用了 restart,因此在 Service C 中不遵循上游采样率,并且所有四个追踪都将作为新追踪在 Service C 中被采样。最终结果将是五个被采样的追踪。
基于头部的采样直接在 APM 代理和 SDK 中实现。必须在各个服务与托管摄取服务之间传播采样率,以便生成准确的指标。
OpenTelemetry 提供了多个采样器。但是,大多数采样器不会传播采样率。这会导致基于跨度的指标(如 APM 吞吐量、延迟和错误指标)不准确。
在使用 OpenTelemetry 的基于头部采样时,为了获得准确的基于跨度的指标,您必须使用一致概率采样器。这些采样器在服务和托管摄取服务之间传播采样率,从而产生准确的指标。
OpenTelemetry 并非在所有语言中都提供一致概率采样器。OpenTelemetry 用户应考虑改用基于尾部的采样。
请参考您所使用的 OpenTelemetry 代理或 SDK 的文档,以获取有关一致概率采样器可用性的更多信息。
对基于尾部采样的支持
基于尾部的采样仅在写入 Elasticsearch 时受支持。如果您使用的是其他输出,则不支持基于尾部的采样。
在基于尾部的采样中,每个追踪的采样决策是在追踪完成后做出的。这意味着将根据一组规则或策略对所有追踪进行分析,这些规则或策略将决定其采样率。
与基于头部的采样不同,每个追踪被采样的概率并不相等。由于较慢的追踪比较快的追踪更有研究价值,因此基于尾部的采样使用加权随机采样——因此,根事务持续时间较长的追踪比根事务持续时间较快的追踪更有可能被采样。
基于尾部采样的一个缺点是,它会导致从 APM 代理向 APM 服务器发送更多数据。因此,与基于头部采样相比,APM 服务器将使用更多的 CPU、内存和磁盘。但是,由于基于尾部的采样决策发生在 APM 服务器中,因此从 APM 服务器传输到 Elasticsearch 的数据更少。因此,在靠近已检测服务的地方运行 APM 服务器可以减少基于尾部采样带来的传输成本增加。
请参阅配置基于尾部的采样以开始使用。
使用基于尾部的采样时,会观察所有追踪,并且只有在追踪完成后才会做出采样决策。
在此示例中,Service A 发起四个事务。如果对于结果为 success 的追踪,我们的采样率为 .5 (50%),而对于结果为 failure 的追踪,采样率为 1 (100%),则被采样的追踪大致如下
基于尾部的采样完全在 APM 服务器中实现,并且适用于由 Elastic APM 代理或 OpenTelemetry SDK 发送的追踪。
由于使用 tailsamplingprocessor 时存在OpenTelemetry 基于尾部采样的限制,我们建议改用 APM 服务器的基于尾部采样。
顾名思义,基于尾部的采样 (TBS) 要求在本地临时存储事件,以便在做出采样决策时可以检索和转发这些事件。
在 APM 服务器实现中,为了获得更好的可扩展性,事件被临时存储在磁盘上而不是内存中。因此,它需要与 APM 事件摄取率成正比的本地磁盘存储,以及用于促进磁盘读写的额外内存。如果存储限制不足,则会根据写入失败时丢弃配置对追踪事件进行索引或丢弃。
启用基于尾部的采样时,建议使用快速磁盘,最好是具有高每秒输入/输出操作数 (IOPS) 的固态硬盘 (SSD)。磁盘吞吐量和 I/O 可能会成为基于尾部采样以及整体 APM 事件摄取的性能瓶颈。磁盘写入与事件摄取率成正比,而磁盘读取与事件摄取率和采样率均成正比。
为了展示性能开销和要求,以下是部署在 AWS EC2 上的独立 APM 服务器在满负荷下接收仅包含追踪的 APM 事件时的部分参考数据。这些数字假设 Elasticsearch 没有背压、尾部采样策略中的统一采样率为 10%、由 1024 个代理并发发送事件,并且磁盘空间充足。
这些数据仅供参考,可能会因采样率、平均事件大小以及每个分布式追踪的平均事件数等因素而有所不同。
术语
- 事件摄取率 (Event Ingestion Rate):使用 Intake v2 协议(Elastic APM 代理使用的协议)从 APM 代理到 APM 服务器的吞吐量,以每秒事件数衡量。
- 事件索引率 (Event Indexing Rate):从 APM 服务器到 Elasticsearch 的吞吐量,以每秒事件数或每秒文档数衡量。请注意,它应该大致等于“事件摄取率 * 采样率”。
- 内存使用量 (Memory Usage):在整个基准测试中观察到的 APM 服务器进程的最大常驻集大小 (RSS)。
- TBS:基于尾部的采样 (Tail-based sampling)。
- IOPS:每秒输入/输出操作数,是衡量磁盘性能的指标。
| EC2 实例大小 | TBS 和磁盘配置 | 事件摄取率 (事件/秒) | 事件索引率 (事件/秒) | 内存使用量 (GB) | 磁盘使用量 (GB) |
|---|---|---|---|---|---|
| c6gd.xlarge | 关闭 TBS | 45120 | 45120 (100% 采样) | 0.95 | 0 |
| c6gd.xlarge | 启用 TBS,带 3000 IOPS 的 EBS gp3 卷 | 17120 | 1527 | 1.48 | 11.3 |
| c6gd.xlarge | 启用 TBS,来自 c6gd 实例的本地 NVMe SSD | 19490 | 1661 | 1.48 | 12.3 |
| c6gd.2xlarge | 关闭 TBS | 63460 | 63460 (100% 采样) | 1.45 | 0 |
| c6gd.2xlarge | 启用 TBS,带 3000 IOPS 的 EBS gp3 卷 | 26340 | 2248 | 2.09 | 17.8 |
| c6gd.2xlarge | 启用 TBS,来自 c6gd 实例的本地 NVMe SSD | 36620 | 3041 | 2.22 | 21.8 |
| c6gd.4xlarge | 关闭 TBS | 119800 | 119800 (100% 采样) | 1.44 | 0 |
| c6gd.4xlarge | 启用 TBS,带 3000 IOPS 的 EBS gp3 卷 | 27620 | 2485 | 2.49 | 16.6 |
| c6gd.4xlarge | 启用 TBS,来自 c6gd 实例的本地 NVMe SSD | 46260 | 3909 | 2.43 | 25.8 |
| EC2 实例大小 | TBS 和磁盘配置 | 事件摄取率 (事件/秒) | 事件索引率 (事件/秒) | 内存使用量 (GB) | 磁盘使用量 (GB) |
|---|---|---|---|---|---|
| c6gd.xlarge | 关闭 TBS | 45480 | 45480 (100% 采样) | 0.95 | 0 |
| c6gd.xlarge | 启用 TBS,带 3000 IOPS 的 EBS gp3 卷 | 11420 | 11.55 | 5.92 | 30.81 |
| c6gd.xlarge | 启用 TBS,来自 c6gd 实例的本地 NVMe SSD | 12630 | 86.52 | 5.82 | 27.70 |
| c6gd.2xlarge | 关闭 TBS | 61900 | 61900 (100% 采样) | 1.45 | 0 |
| c6gd.2xlarge | 启用 TBS,带 3000 IOPS 的 EBS gp3 卷 | 12920 | 37.31 | 11.31 | 30.98 |
| c6gd.2xlarge | 启用 TBS,来自 c6gd 实例的本地 NVMe SSD | 23300 | 574 | 13.31 | 50.99 |
| c6gd.4xlarge | 关闭 TBS | 122800 | 122800 (100% 采样) | 1.45 | 0 |
| c6gd.4xlarge | 启用 TBS,带 3000 IOPS 的 EBS gp3 卷 | 13280 | 34.20 | 22.61 | 32.01 |
| c6gd.4xlarge | 启用 TBS,来自 c6gd 实例的本地 NVMe SSD | 35810 | 2480 | 30.41 | 86.86 |
在解释这些数字时,请注意以下几点:
- APM Server 9.x 的性能数据基于 9.2.2 版本,该版本包含与 9.0 相比的优化,并代表了典型的 9.x 系列性能特征。
- 各项指标是相互关联的。例如,当事件摄取率较高时,出现更高的内存使用量和磁盘使用量是合理的。
- 在正常运行情况下,事件索引率除以事件摄取率应接近配置的采样率(本例中为 10%)。但是,在上面 8.19 版本的数字中,由于 APM 服务器处于满负荷状态,采样决策处理因磁盘读取操作与摄取路径写入竞争磁盘 I/O 资源而滞后,导致事件索引率明显低于预期。
- 不同版本的内存使用量测量方式有所不同:9.x 版本的数据仅反映 APM 服务器进程 RSS(不包括操作系统缓存),而 8.19 版本的数据包括操作系统缓存,因为数据库使用了内存映射。尽管存在这种测量差异,但由于 9.0+ 版本的数据库占用空间小得多,因此总体上使用的内存显著减少。
- 较低的采样率会导致较高的事件摄取率,因为采样决策所需的开销更少。例如,在 9.x 版本中将采样率从 10% 降低到 5% 会使事件摄取率提高 5-10%(上述表格中未显示此数据)。
与 8.x 版本相比,9.x 版本中的基于尾部采样实现提供了明显更好的性能,这主要归功于重写了存储层。这个新实现可以压缩数据,并更可靠地清理过期数据,从而减轻对磁盘、内存和计算资源的负载。这种改进在较慢磁盘上的事件索引率上尤为明显。在 8.x 版本中,随着数据库变大,性能下降可能会变得不成比例。
被采样的追踪会保留与其相关联的所有数据。未采样的追踪会丢弃所有跨度和事务数据。1 无论采样决策如何,所有追踪都会保留错误数据。
APM 应用中的某些可视化(如延迟)由聚合的事务和跨度指标驱动。这些指标的计算方式取决于所使用的采样方法
- 基于头部的采样:根据所有被采样的事件计算指标。
- 基于尾部的采样:根据所有事件计算指标,无论它们最终是否被采样。
- 结合头部和尾部采样:当两种方法同时使用时,指标根据被基于头部采样策略采样的所有事件进行计算。
对于所有采样方法,指标都会通过基于头部采样策略的反向采样率进行加权,以提供总体群体的估计值。例如,如果您的基于头部采样率为 5%,则每个被采样的追踪计为 20。随着延迟方差的增加或基于头部采样率的降低,这些计算中的误差水平可能会增加。
这些计算方法确保 APM 应用在给定当前使用的采样策略的情况下提供尽可能准确的指标,同时也考虑了基于头部的采样率来估计追踪的完整群体。
1 真实用户监控 (RUM) 追踪是这条规则的一个例外。利用 RUM 数据的 Kibana 应用依赖于事务事件,因此未采样的 RUM 追踪会保留事务数据——仅丢弃跨度数据。
最佳采样率是多少?遗憾的是,并没有一个固定的最佳答案。采样取决于您的数据、应用程序的吞吐量、数据保留策略以及其他因素。从 .1% 到 100% 的采样率都被认为是正常的。您可能会针对不同的场景决定一个独特的采样率。以下是一些示例
- 流量明显多于其他服务的服务可以安全地以较低的速率进行采样
- 比其他路由更重要的路由可以以更高的速率进行采样
- 生产服务环境可能比开发环境需要更高的采样率
- 失败的追踪结果可能比成功的追踪更有研究价值——因此需要更高的采样率
无论上述情况如何,注重成本的客户通常对较低的采样率感到满意。
有三种方法可以调整 APM 代理的基于头部采样率
可以使用 Kibana 中的 APM 代理配置,按服务和按环境动态更改事务采样率(无需重新部署)。
APM 代理配置公开了一个 API,可用于以编程方式更改代理的采样率。有关示例,请参阅代理配置 API 参考。
每个代理都提供用于设置事务采样率的配置值。有关更多详细信息,请参阅相关代理的文档
- Go:
ELASTIC_APM_TRANSACTION_SAMPLE_RATE - Java:
transaction_sample_rate - .NET:
TransactionSampleRate - Node.js:
transactionSampleRate - PHP:
transaction_sample_rate - Python:
transaction_sample_rate - Ruby:
transaction_sample_rate
使用基于尾部的采样需要增强的权限。有关更多信息,请参阅创建基于尾部的采样角色。
通过启用基于尾部的采样来启用该功能。启用后,追踪事件将被映射到采样策略。每个采样策略必须指定一个采样率,并且可选择指定其他条件。必须满足策略的所有条件,追踪事件才能与其匹配。
追踪事件将按指定的顺序与策略进行匹配。每个策略列表必须以一个默认策略结束——该策略仅指定采样率。此默认策略用于捕获与更严格策略不匹配的剩余追踪事件。要求提供此默认策略可确保仅有意丢弃追踪。如果您启用基于尾部的采样并发送不匹配任何策略的事务,APM 服务器将拒绝该事务并返回 no matching policy 错误。
请注意,从 9.0.0 版本开始,APM Server 的存储限制为无限,但当数据库所在的磁盘达到 80% 的使用率时,将停止写入。由于限制的计算和强制执行方式,实际磁盘空间仍可能略微超过此基于磁盘使用率的限制或任何配置的存储限制。
此示例定义了三个基于尾部的采样策略
- sample_rate: 1
service.environment: production
trace.name: "GET /very_important_route"
- sample_rate: .01
service.environment: production
trace.name: "GET /not_important_route"
- sample_rate: .1
- 对
production中追踪名称为"GET /very_important_route"的 100% 追踪进行采样 - 对
production中追踪名称为"GET /not_important_route"的 1% 追踪进行采样 - 默认策略:以 10% 的比例对所有其余追踪进行采样,例如不同环境(如
dev)中的追踪,或具有任何其他名称的追踪
当追踪起源于 Service A 然后调用 Service B 时,采样率由追踪启动所在的服务决定
- sample_rate: 0.3
service.name: B
- sample_rate: 0.5
service.name: A
- sample_rate: 0.1
- 回退:始终设置默认值
- 由于 Service A 是追踪的根,因此应用其策略 (0.5),而 Service B 的策略 (0.3) 被忽略。
- 如果追踪改为从 Service B 开始(然后传递到 Service A),则将应用 Service B 的策略。
基于尾部的采样规则是在追踪级别根据哪个服务发起了分布式追踪来评估的,而不是根据事务或跨度的服务。
策略按顺序评估,并应用第一个满足所有匹配条件的策略。这意味着,在实践中,应将策略从最具体(狭窄的匹配器)到最通用进行排序,最后以兜底(回退)策略结束。
# Example A: prioritize service origin, then failures
- sample_rate: 0.2
service.name: A
- sample_rate: 0.5
trace.outcome: failure
- sample_rate: 0.1
- 兜底
# Example B: prioritize failures, then a specific service
- sample_rate: 0.2
trace.outcome: failure
- sample_rate: 0.5
service.name: A
- sample_rate: 0.1
- 在示例 A 中,来自 Service A 的追踪以 20% 的比例进行采样,所有其他失败的追踪(无论哪个服务)都以 50% 的比例进行采样。
- 在示例 B 中,每个失败的追踪都以 20% 的比例进行采样,包括那些源自 Service A 的追踪。
有关基于尾部采样配置选项的完整参考,请参阅基于尾部的采样。