Prometheus 中的 increase() 有时会使值加倍:如何避免?

increase() in Prometheus sometimes doubles values: how to avoid?

我发现对于一些图表,我从普罗米修斯那里得到了双精度值,而应该只是一个:

我使用的查询:

increase(signups_count[4m])

抓取间隔设置为 2 分钟。

如果我查询实际存储的数据:

curl -gs 'localhost:9090/api/v1/query?query=(signups_count[1h])'

"values":[
     [1515721365.194, "579"],
     [1515721485.194, "579"],
     [1515721605.194, "580"],
     [1515721725.194, "580"],
     [1515721845.194, "580"],
     [1515721965.194, "580"],
     [1515722085.194, "580"],
     [1515722205.194, "581"],
     [1515722325.194, "581"],
     [1515722445.194, "581"],
     [1515722565.194, "581"]
],

我看到只有两次增加。事实上,如果我查询这些时间,我会看到预期的结果:

curl -gs 'localhost:9090/api/v1/query_range?step=4m&query=increase(signups_count[4m])&start=1515721965.194&end=1515722565.194'

"values": [
     [1515721965.194, "0"],
     [1515722205.194, "1"],
     [1515722445.194, "0"]
],

但是 Grafana(和 GUI 中的 Prometheus)倾向于在查询中设置不同的 step,对于不熟悉 Prometheus 内部工作原理的人来说,我得到了非常意外的结果。

curl -gs 'localhost:9090/api/v1/query_range?step=15&query=increase(signups_count[4m])&start=1515721965.194&end=1515722565.194'

... skip ...
 [1515722190.194, "0"],
 [1515722205.194, "1"],
 [1515722220.194, "2"],
 [1515722235.194, "2"],
... skip ...

知道 increase() 只是 ,我想这就是在这种情况下它应该如何工作。

如何避免这种情况?我如何让 Prometheus/Grafana 在大多数情况下显示一个对一个,两个对两个?除了增加刮擦间隔(这将是我最后的手段)。

我了解普罗米修斯 isn't an exact sort of tool,所以如果我不是一直都有一个好号码,但大多数时候都很好,那对我来说很好。

我还缺少什么?

这被称为 aliasing,是信号处理中的一个基本问题。您可以通过提高采样率来稍微改善这一点,4m 范围对于 2m 范围来说有点短。尝试 10 米范围。

这里例如在 1515722220 执行的查询只看到 580@1515722085.194 和 581@1515722205.194 样本。在 2 分钟内增加了 1,在 4 分钟内推断增加了 2 - 这符合预期。

任何基于指标的监控系统都会有类似的工件,如果你想要 100% 的准确性,你需要日志。

increase() 将始终(大约)使您的设置实际增加一倍。

原因是(按照目前的实施):

  1. increase() 是(如您观察到的)rate() 的语法糖,即它是 return 乘以 rate() 的值乘以秒数在你指定的范围内。在您的情况下,它是 rate() * 240.
  2. rate() 在其计算中使用外推法。在绝大多数情况下,4 分钟的范围将 return 恰好 2 个数据点,几乎恰好相隔 2 分钟。然后将速率计算为最后一个和第一个之间的差异(即您的案例中的 2 点)除以 2 点的时间差(在 99.99% 的案例中约为 120 秒)乘以您请求的范围(恰好 240 秒) ).因此,如果两点之间的增幅为零,则利率为零。如果 2 点之间的增量为 1.0,则计算出的 rate() 将接近 2.0 / 240,因此,increase() 将为 2.0

这种方法在计数器平稳增加时效果很好(例如,如果您每 2 分钟有或多或少固定的注册数量)。但是对于一个很少增加的计数器(就像你的注册计数器一样)或者一个尖锐的计数器(比如 CPU 使用率)你会得到奇怪的高估(比如你看到的增加 2)。

您基本上可以对 Prometheus 的实现进行逆向工程,并通过乘以 (requested_range - scrape interval) 并除以 requested_range 得到(非常接近)实际增加量,本质上是回溯 Prometheus 所做的外推。

在你的情况下,这意味着

increase(signups_count[4m]) * (240 - 120) / 240

或者,更简洁地说,

increase(signups_count[4m]) / 2

它要求您了解范围的长度和刮擦间隔,但它会给您您想要的:"ones for ones, and twos for twos, most of the time"。有时你会得到 1.01 而不是 1.0 因为刮痕是 119 秒,而不是相隔 120 秒,有时,如果你的评估与刮痕紧密对齐,可能会包括边界上的一些点或不是在数据点计算中,但它仍然是比 2.0.

更好的答案
Prometheus 中的

increase()rate() 函数可能会 return 由于外推而产生意外结果。参见 this issue for technical details. Prometheus developers are going to fix this issue in the future - see this design doc

同时尝试 VictoriaMetrics - it addresses the issue in increase() and rate() functions, so they return the expected results (e.g. increase(signups_count[4m]) would return the correct integer results). See this article and this comment 了解技术细节。