我试图编写一个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
的查询?我应该期望能够编写这样的查询吗?
谢谢!
答案 0 :(得分:1)
这是因为Prometheus优先考虑范围超出精度的一致定义。即它始终将范围定义为落入(包括)区间[now()-range,now()]内的所有样本。此定义对于量规来说是很有意义的:如果要计算时间范围等于步长的avg_over_time()
,则希望每个输入样本都包括在一个输出样本的计算中。
但是对于计数器却不是这样。在等于步长的时间范围内,一个输入值(即两个连续采样之间的增加)基本上被丢弃。 (有关更多详细信息,请参见Prometheus问题#3746和3806。)为弥补所丢弃的数据,Prometheus使用外推法调整了计算结果。
意思是,如果(按照您的情况)您使用的时间范围是抓取间隔的2倍(1m
抓取间隔的30s
范围),则Prometheus将(平均)在每个样本中找到2个样本范围,但是这两个样本所覆盖的实际时间范围大约为30s
。因此,Prometheus会将值加倍,从而有助于将速率推断到请求的1m
。因此,结果2是预期的1,而不是预期的1。您还将注意到,由于连续样本之间的某些增加被丢弃了(即使没有样本),所以rate()
图中并未显示计数器中的所有增加。 (即rate()
中没有对应于第三个计数器增加的跳动。如果在不同的时间刷新,则会出现不同的增加并消失。Grafana通过始终将请求范围与步长对齐来“解决”了后者)始终缺少相同的增长。)
Prometheus开发人员建议的解决方案是计算更长持续时间的费率。但是,所有要做的就是减少错误(在3倍范围内得到1.5,在4倍范围内得到1.33,在5倍范围内得到1.25,等等),永远不要摆脱它。平滑增加计数器可以很好地隐藏Prometheus的外推法,但像您自己的计数器一样,拇指酸痛而突出,很少增加。)
此问题的唯一解决方法(未解决Prometheus,我已经为其提交了PR,并维护a 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
所需要的数量。但是随后您必须撤消普罗米修斯外推法所带来的变化,从而随之而来的乘法和除法。这是一个丑陋的骇客,它取决于您知道抓取间隔并将其硬编码为录制规则和/或Grafana查询。
请注意,无论使用哪种方法,您都可能无法获得正好为1的值。由于服务,网络和内部Prometheus延迟,样本通常不会在毫秒内对齐,因此每秒的增长率将略低于或略高于预期值。
答案 1 :(得分:0)
这里有一个计算计数器指标正确变化的替代方法:
max_over_time(http_server_requests_seconds_count{outcome!="REDIRECTION",outcome!="SUCCESS"}[1m])-min_over_time(http_server_requests_seconds_count{outcome!="REDIRECTION",outcome!="SUCCESS"}[1m])
我花了一段时间才弄清楚的另一件事是,在绘制上述内容时,需要确保绘图的分辨率不大于刮取间隔。否则,您希望看到的部分或全部峰值将不会显示。