【博客674】警惕Prometheus 中的重复样本和无序时间戳错误
但 Prometheus 可能会出现错误配置,导致多个目标共享相同的标签集,这可能会导致结果时间序列之间的标签集冲突。然后,TSDB 会将多个原始系列的流视为单个系列,但当它们的样本因无序或重复时间戳而相互冲突时,会拒绝无效追加。但是,Prometheus 可能会出现错误配置,导致 Prometheus 尝试附加到 TSDB 时获得重复或无序的样本时间戳,但未能成功。当样本具有不同的样本值时,它还
警惕Prometheus 中的重复样本和无序时间戳错误
1、场景
您的 Prometheus 服务器日志中是否遇到过以下错误?
"Error on ingesting out-of-order samples"
"Error on ingesting samples with different value but same timestamp"
"duplicate sample for timestamp"
那么您的设置中可能存在配置错误,导致多个系列相互碰撞和冲突。在这篇文章中,我们将解释这些错误背后的背景、可能导致这些错误的原因以及如何调试和修复它们。
2、背景:TSDB(通常)仅追加
Prometheus 是一个实时监控系统,通常只需要随着时间的推移以稳定持续的方式跟踪指标。Prometheus TSDB 通过(通常)仅支持附加到任何现有系列的末尾来反映这一点。这意味着它会拒绝时间戳早于同一系列中最新样本的传入样本。当样本具有不同的样本值时,它还会拒绝与系列中最新样本具有相同时间戳的样本(否则,它们将被忽略)。
但是,Prometheus 可能会出现错误配置,导致 Prometheus 尝试附加到 TSDB 时获得重复或无序的样本时间戳,但未能成功。这通常是由于多个时间序列之间的意外标签集冲突导致序列看起来相同(相同的指标名称和标签集),但也可能有其他原因。发生这种情况时,向 TSDB 追加违规样本将失败,Prometheus 将记录相应的错误。
注意: Prometheus 2.39引入了一项实验性out_of_order_time_window配置设置,允许在有限的时间内摄取无序样本,但默认情况下不启用此功能。在某些无法实时收集所有数据的情况下,它可能很有用。
3、样本时间戳错误的根本原因
3-1、重复目标:
在正确配置的 Prometheus 设置中,每个目标都有自己独特的目标标签集。Prometheus 将这些标签附加到从目标提取的所有系列,以帮助消除来自不同目标的相同指标的歧义。但 Prometheus 可能会出现错误配置,导致多个目标共享相同的标签集,这可能会导致结果时间序列之间的标签集冲突。然后,TSDB 会将多个原始系列的流视为单个系列,但当它们的样本因无序或重复时间戳而相互冲突时,会拒绝无效追加。
这是一种轻松引发这种重复的方法,通过将目标组的标签显式设置job为与您的 中具有相同目标的另一个作业相同的值prometheus.yml:
global:
scrape_interval: 5s
scrape_configs:
- job_name: demo
static_configs:
- targets:
- demo.promlabs.com:10000
- job_name: demo2
static_configs:
- labels:
# Override the "job" label to also be "demo" instead of "demo2".
job: demo
targets:
- demo.promlabs.com:10000
job上述的另一种变体可以通过将目标重新标记规则添加到第二个抓取配置并以此方式覆盖标签来实现"demo"。
当其中一个重复目标在另一个目标之前开始抓取,但最后完成抓取时,您将收到一条"Error on ingesting out-of-order samples"错误消息,因为第一个目标尝试将其样本插入到 TSDB 中,其抓取时间戳早于第二个已完成的目标。作为完整的日志行,它看起来像这样:
ts=2022-12-14T12:21:05.833Z caller=scrape.go:1681 level=warn component="scrape manager" scrape_pool=demo target=http://demo.promlabs.com:10000/metrics msg="Error on ingesting out-of-order samples" num_dropped=254
当两个重复的目标在完全相同的毫秒开始抓取时(这可能并且确实发生在来自不同抓取池的目标之间),它们将尝试使用相同的抓取时间戳存储其结果样本。但其中一个附加操作将失败并显示"Error on ingesting samples with different value but same timestamp"错误消息:
ts=2022-12-14T12:18:10.841Z caller=scrape.go:1684 level=warn component="scrape manager" scrape_pool=demo target=http://demo.promlabs.com:10000/metrics msg="Error on ingesting samples with different value but same timestamp" num_dropped=58
通常,错误消息中的scrape_pool和字段应该足以将您指向配置文件中的正确位置,以查找并修复冲突的目标。target但是,如果远程服务发现机制产生重复的目标,或者服务发现源和目标重新标记规则之间存在交互,导致消除歧义的标签丢失,则重复在配置文件本身中可能不可见。在这种情况下,您可能还需要查看/targetsPrometheus 服务器的页面,以发现多个抓取池或抓取配置之间具有相同标记的目标(同一个抓取池中不可能有相同的目标,因为 Prometheus 会自动对它们进行重复数据删除)成一个)。这/service-discovery页面还显示所有发现的目标及其重新标记前后的标签集。要修复摄取错误,请确保所有目标都有唯一的标签。
3-2、有问题的客户端时间戳:
通常,Prometheus/metrics端点不会包含它们公开的样本的显式时间戳。相反,Prometheus 为所有提取的样本分配自己的抓取开始时间戳。但是,也可以以Prometheus 指标说明格式为样本提供显式的客户端时间戳。例如,如果您想要公开一个test_metric值为42且毫秒 Unix 时间戳为 的指标1671021749332,则公开格式将如下所示:
# HELP foo A test metric with an explicit timestamp.
# TYPE foo gauge
test_metric 42 1671021749332
鉴于最初提到的 TSDB 限制,如果该功能被滥用或错误实现,这里可能会出现很多问题:如果客户端时间戳在两次抓取之间向后移动,您将收到无序错误。如果它保持不变,但样本值发生变化,您将收到重复时间戳错误。
这种情况下的错误消息看起来与重复目标的错误消息类似,因为它们都源自抓取层。然而,错误日志中的简单目标信息可能不足以在损坏的目标中找到特定的有问题的指标。幸运的是,在 Prometheus 服务器上设置该–log.level=debug标志会增加日志记录的详细程度,以便您可以看到series导致问题的每个系列(在日志消息字段中)。
对于重复的时间戳,将如下所示:
ts=2022-12-14T10:23:40.297Z caller=scrape.go:1730 level=debug component="scrape manager" scrape_pool=prometheus_dupe target=http://demo.promlabs.com:10000/metrics msg="Duplicate sample for timestamp" series=go_goroutines
对于无序时间戳,如下所示:
ts=2022-12-14T13:12:45.844Z caller=scrape.go:1725 level=debug component="scrape manager" scrape_pool=demo target=http://demo.promlabs.com:10000/metrics msg="Out of order sample" series=go_goroutines
在这种情况下,您必须修复损坏目标中的指标说明才能解决问题。
3-3、规则中的重复系列:
还可以在多个记录规则的输出之间创建冲突系列。colliding_name以此规则文件为例,它记录同一规则组中两个规则的指标,但具有不同的样本值:
groups:
- name: dupe
rules:
- record: colliding_name
expr: 0
- record: colliding_name
expr: 1
尽管同一组中的多个规则的评估是按顺序发生的,但 Prometheus 将以相同的查询评估时间戳运行所有规则。这会导致规则评估失败并出现"duplicate sample for timestamp"错误:
ts=2022-12-15T10:07:25.976Z caller=manager.go:684 level=warn component="rule manager" file=dupes-rules.yml group=dupe name=colliding_name index=1 msg="Rule evaluation result discarded" err="duplicate sample for timestamp" sample="{__name__=\"colliding_name\"} => 1 @[1671098845972]"
此错误还有助于记录规则组、记录的指标名称以及示例的违规标签集。这样,您应该能够修复规则文件中的问题。
作为一种变型,这种冲突也可能发生在由记录规则产生的系列与目标刮擦之间。
3-4、远程写入发送错误数据:
如果您使用Prometheus 服务器自己的远程写入接收器将样本推送到 Prometheus,您还可能会遇到一个或多个推送源发送冲突的指标和时间戳的情况。在这种情况下,您可能会在发送端收到"Out of order sample from remote write"或请求错误:“duplicate sample for timestamp”
ts=2022-12-15T10:49:51.792Z caller=dedupe.go:112 component=remote level=error remote_name=45e379 url=http://localhost:9090/api/v1/write msg="non-recoverable error" count=6 exemplarCount=0 err="server returned HTTP status 400 Bad Request: out of order sample"
或者:
ts=2022-12-15T10:51:31.437Z caller=dedupe.go:112 component=remote level=error remote_name=45e379 url=http://localhost:9090/api/v1/write msg="non-recoverable error" count=3 exemplarCount=0 err="server returned HTTP status 400 Bad Request: duplicate sample for timestamp"
在接收 Prometheus 服务器中,您将收到类似的错误消息(一旦修复了我们在撰写本文过程中发现的错误):
ts=2022-12-15T11:04:23.006Z caller=write_handler.go:107 level=error component=web msg="Out of order sample from remote write" err="out of order sample" series="{__name__=\"foo\", instance=\"localhost:8080\", job=\"remote\"}" timestamp=1671102261810
后一条消息告诉您确切的违规系列,因此您有望找到并修复其来源。
3-5、故意摄取无序数据:
现在,有一些有效的用例,您可能实际上想要提取无序数据,甚至是通常太旧而根本无法提取到 TSDB 中的数据。您可能有一些数据源无法持续生成指标,而只能提供批量或延迟的结果。对于这些情况,如上所述,您需要打开 Prometheus 新的实验性out_of_order_time_windowTSDB 配置设置,该设置允许在有限的时间内摄取无序样本,以消除这些错误消息。
注意:并非所有碰撞都会导致错误!
正如我们上面刚刚了解到的,Prometheus 可以注意到并告诉我们多种系列碰撞条件。但在某些情况下,普罗米修斯会默默地忽略一系列碰撞,或者根本无法检测到它们。因此,没有看到错误并不能保证一切正常。
3-6、度量重新标记导致的冲突静默失败:
其中一种情况是,意外地使用度量重新标记(在抓取过程中应用于每个拉取的系列)从单个目标中删除重要的系列消除歧义标签。例如,以下配置quantile从所有拉取的指标中删除标签,但在go_gc_duration_seconds指标中,需要此标签来区分 3 个不同的系列:
global:
scrape_interval: 5s
scrape_configs:
- job_name: prometheus
static_configs:
- targets:
- localhost:9090
metric_relabel_configs:
- action: labeldrop
target_label: quantile
Prometheus 当前的行为是存储第一个冲突样本,但在没有任何警告的情况下默默地丢弃后续样本。
3-7、两个规则或目标写入同一个系列,没有时间冲突:
很容易发生这样的情况:Prometheus 要么抓取两个重复的目标,要么从写入同一序列的两个规则中获取样本,但样本总是以完美的同步方式到达,而不会创建无序条件或重复的时间戳。在这种情况下,来自两个源的样本将被写入相同的时间序列,并且相同的值在两个输入序列之间来回变化。
colliding_name例如,如果您创建了这样的规则文件,则使用样本值0和来记录指标1:
groups:
- name: collision
rules:
- record: colliding_name
expr: 0
- name: collision2
rules:
- record: colliding_name
expr: 1
那么您可能不会看到任何记录的错误(除非两个规则组碰巧以完全相同的重复时间戳运行)。但是,您将看到该系列的样本值在0和之间交替1:
这种行为通常没有有效的用例,但 Prometheus 目前无法自动检测这种情况。
4、使用指标检测这些问题
除了读取记录的错误消息之外,您还可以通过监控 Prometheus 公开的以下指标来检测无序和重复的时间戳:
- prometheus_tsdb_out_of_order_samples_total:由于禁用乱序支持而导致尝试提取失败的乱序样本总数。
- prometheus_target_scrapes_sample_duplicate_timestamp_total:具有重复时间戳的抓取样本总数。
- prometheus_target_scrapes_sample_out_of_order_total:抓取的乱序样本总数。
- prometheus_http_requests_total{code=“400”,handler=“/api/v1/write”}400:远程写入接收器(在接收 Prometheus 服务器中)收到的状态码请求总数。400这也可能包括导致 a 的错误。
- prometheus_remote_storage_samples_failed_total:发送 Prometheus 服务器中通过远程写入发送失败的样本总数。这还可能包括其他错误。
不幸的是,规则评估中的系列冲突错误尚未显示在指标中。有一个prometheus_rule_evaluation_failures_total指标,但仅计算规则评估的总失败次数,而不计算评估期间丢弃的单个系列。然而,有问题的输出系列的规则仍将被标记为不健康,并且页面/rules将向您显示相应的错误:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)