首页 > 解决方案 > 正确检测 Prometheus 计数指标的变化

问题描述

我试图编写一个 PromQL 查询来检测计数指标的变化。

我的抓取间隔是 15 秒。

我这样查询指标:

http_server_requests_seconds_count{outcome!="REDIRECTION",outcome!="SUCCESS"}

它显示了有多少http_server_requests不是重定向并且没有成功。

我尝试使用此指标编写警报表达式如下所示:

sum by(service, method, outcome, status, uri) (
  rate(
    http_server_requests_seconds_count{
      outcome!="REDIRECTION",
      outcome!="SUCCESS"
    }[1m]
  )
) * 60

我的想法是 [1m] 乘以 60 秒的速率将是1发生变化时,但据我所知,我得到了2

这些图表清楚地表明了这一点:

普罗米修斯图

上图是求和表达式,下图是服务器请求计数的变化。当底部图计数 +1 时,顶部图也应该暂时上升1(但实际上它上升到2)。

我究竟做错了什么?我是不是误会了什么?如何编写一个查询,1在发生更改时为我提供值?我应该期望能够编写这样的查询吗?

谢谢!

标签: prometheuspromql

解决方案


这是因为 Prometheus 优先考虑范围的一致定义而不是准确性。即它总是将范围定义为落在(包含)区间[now() - range, now()] 内的所有样本。这个定义对仪表非常有意义:如果你想计算avg_over_time()一个时间范围等于步长的,你希望每个输入样本都包含在恰好一个输出样本的计算中。

但对于柜台来说,情况并非如此。在时间范围等于步长的情况下,一个输入值(即两个连续样本之间的增量)基本上被丢弃。(有关更多详细信息,请参阅 Prometheus 问题#37463806。)为了弥补它丢弃的数据,Prometheus 使用外推法来调整计算结果。

这意味着如果(如您的情况)您使用的时间范围是您的刮擦间隔的 2 倍(刮擦间隔的1m范围30s),Prometheus 将(平均)在每个范围内找到 2 个样本,但这 2 个样本所涵盖的实际时间范围将待在身边30s。因此,Prometheus 将1m通过将值加倍来帮助将速率推断为请求的值。因此结果是 2 而不是预期的 1。您还会注意到,由于连续样本之间的一些增加被丢弃(即使没有样本),并不是您的计数器中的所有增加都显示在您的rate()图表中。(即没有跳转rate()对应于第三次计数器增加。如果你在不同的时间刷新,不同的增加会出现和消失。Grafana 通过始终将请求的范围与步骤对齐来“解决”后者,因此始终缺少相同的增加。)

Prometheus 开发人员建议的解决方案是计算更长持续时间的费率。但所做的只是减少误差(你会得到 1.5 与 3x 范围,1.33 与 4x 范围,1.25 为 5x 范围等),永远不会摆脱它。普罗米修斯的推断被平稳地增加计数器隐藏得足够好,但像你自己的计数器一样突出,很少增加。)

这个问题的唯一解决方法(没有修复 Prometheus,我已经为此提交了 PR 并正在维护一个 fork)是对 Prometheus 的rate(). 即假设一个30s表达式的抓取间隔rate(foo[1m])应该替换为:

rate(foo[90s]) * 60 / 90

或更一般地(注意括号内的表达式需要是时间文字,不能是计算)

rate(foo[intended_range + scrape_interval]) * intended_range / (intended_range + scrape_interval)

这样做的原因是该intended_range + scrape_interval范围将为您提供足够的样本来覆盖 的增加intended_range,这就是您想要的。但是随后您必须撤消 Prometheus 的推断引入的变化,因此随之而来的乘法和除法。这是一个丑陋的黑客,取决于你知道你的抓取间隔并将其硬编码到你的记录规则和/或 Grafana 查询中。

请注意,无论您使用哪种方法,您都可能不会得到正好为 1 的值。由于服务、网络和内部 Prometheus 延迟,样本通常不会在毫秒内对齐,因此每秒的增长率会略低于或略高于预期值。


推荐阅读