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() * 240
。
rate()
在其计算中使用外推法。在绝大多数情况下,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() 有时会使值翻倍:如何避免?的主要内容,如果未能解决你的问题,请参考以下文章