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

Posted

技术标签:

【中文标题】Prometheus 中的 increase() 有时会使值翻倍:如何避免?【英文标题】:increase() in Prometheus sometimes doubles values: how to avoid? 【发布时间】:2018-06-21 11:37:33 【问题描述】:

我发现对于某些图表,我从 Prometheus 获得双倍值,其中应该只是一个:

我使用的查询:

increase(signups_count[4m])

抓取间隔设置为recommended maximum 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() 就是a syntactic sugar for a specific use-case of the rate() function,我想在这种情况下它应该是这样工作的。

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

我了解 Prometheus isn't an exact sort of tool,所以如果我不是在任何时候,但在大多数时候都有一个好的数字,我就可以了。

我还缺少什么?

【问题讨论】:

试试MetricsQL。它提供了increase() 函数,可以为缓慢增加的时间序列返回正确的整数结果。 【参考方案1】:

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

例如,在 1515722220 处执行的查询只看到 580@1515722085.194 和 581@1515722205.194 样本。即在 2 分钟内增加 1,在 4 分钟内推断为增加 2 - 这与预期一致。

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

【讨论】:

此声明“任何基于指标的监控系统都会有类似的工件,如果你想要 100% 的准确度,你需要日志。”实际上是不正确的,这不仅是 Prometheus 独有的,而且许多人已向 Prometheus 提交了错误,并提出了针对此问题的修复建议。【参考方案2】:

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

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

    increase() 是(正如您所观察到的)rate() 的语法糖,即它是 rate() 返回的值乘以您指定范围内的秒数。在你的情况下,它是rate() * 240rate() 在其计算中使用外推法。在绝大多数情况下,4 分钟的范围将恰好返回 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

它需要你知道范围的长度和抓取间隔,但它会给你你想要的东西:“大多数时候,一个对一个,两个对两个”。有时您会得到1.01 而不是1.0,因为刮擦是119 秒,而不是相隔120 秒,有时,如果您的评估与刮擦紧密对齐,则边界上的某些点可能包含或不包含在数据中点计算,但还是比2.0更好的答案。

【讨论】:

这不是一个好建议,因为它假定恰好有 2 个样本始终在范围内。首先,这对失败的刮擦没有弹性,因为它仍在使用太低的范围。其次,它对你不能依赖的抓取的相位和抖动做出假设,并可能导致答案比它应该的大得多。 它假定大多数时间恰好有 2 个样本在该范围内。这正是 OP 所要求的:“大多数情况下,一个对一个,两个对两个。而普通的 rate()/increase() 在相​​位和抖动方面有完全相同的问题:它们会产生更大的答案比他们应该的(除了他们已经产生的答案已经是平均情况下应该有的两倍大的事实。 另外,您仍然可以使用相同的基本方法来延长范围,并且仍然可以改进 increase() 返回的内容。没有什么强迫您使用 2 倍的刮擦间隔。只是你可以而且在大多数情况下会得到比 increase() 更好的答案。

以上是关于Prometheus 中的 increase() 有时会使值翻倍:如何避免?的主要内容,如果未能解决你的问题,请参考以下文章

使用 $__interval 在 grafana 中进行 Prometheus 查询

prometheus 实践

prometheus grafana sql 常用函数参数

promethues常用的函数

第八讲:prometheus命令行使用扩展

监控工具之---Prometheus表达式