目录

 

引言

问题场景

尝试解决

deriv函数解析

最终解决

总结与思考


 

引言

        Promethues是k8s以及云原生下的标准监控告警系统,提供了很多内置的函数,功能已经十分强大,但是,依然有一些需求不能直接使用内置函数来解决。比如,Promethues中计算变化率可以使用内置的rate或者irate函数,但是这两个函数只能作用于counter类型,如果是guage类型,则没有同样功能的内置函数来实现。笔者在实际工作中遇到了类似的问题,本文记录了整体的解决过程和思路。

        期望直接看解答的,可以直接查看最下面最终解决部分。

问题场景

        用户有一个qps指标,记录的是当前的瞬时qps,使用的是guage类型。用户侧期望配置一个监控,能够在qps出现剧烈波动的情况下发出告警,具体规则是突增或突降30%以上。

尝试解决

        对于Promethues变化率,百度和google可以搜到很多,不过都是rate的用法。官方文档上也明确说了,rate和irate只适用于counter类的指标。

rate should only be used with counters and native histograms where the components behave like counters.

irate should only be used when graphing volatile, fast-moving counters.

        又google了一下,发现How to calculate the instant rate of a guage metric中指出可以使用deriv函数来解决。

        百度和google查了一圈,除了官方文档中的解释和一些中文翻译,几乎没有deriv函数相关的资料,那么只能实际的测试一把了。

        使用下面的公式:

deriv(sum({__name__=~"current_qps"})[1m])

        得到结果:

prometheus java step计算 prometheus rate计算方法_百度

        看起来也还比较符合趋势,就没有进行深究,设置了对应的告警策略(加上abs取绝对值):

abs(deriv(sum({__name__=~"current_qps"})[1m])) > 30

        加上告警后,因为触发的比较多,所以对比了计算触发告警两个点之间的差值,发现不够30%,差距较大。

deriv函数解析

        因为监控告警不准,所以要认真解析下deriv函数了。同时,由于网上资源过于稀少,所以只能从源码入手了。

        查看Promethues源码,functions.md中指出:

## `deriv()`

 

`deriv(v range-vector)` calculates the per-second derivative of the time series in a range

vector `v`, using [simple linear regression](https://en.wikipedia.org/wiki/Simple_linear_regression).

The range vector must have at least two samples in order to perform the calculation. When `+Inf` or

`-Inf` are found in the range vector, the slope and offset value calculated will be `NaN`.

 

`deriv` should only be used with gauges.

        deriv实际上是计算了范围向量每秒的导数,根据高中数学知识(已经忘完了,重新百度的)导数是平面上点切线的斜率,因此我理解这个deriv求出的应该是斜率。那具体是怎么计算斜率呢,文档中指出,计算依据的数学方法是简单线性回归:

prometheus java step计算 prometheus rate计算方法_prometheus_02

        看到这里,因为数学知识的匮乏和长久不用的生疏,盲目相信Stack Overflow文章 的解答,自己直接认为导数==斜率==比率,为了验证自己的想法,继续查看Promethues的源码,看看是如何计算的:

        funcDeriv中首先获取样品值,数据少于2的话直接return,关键的处理步骤是:

slope, _ := linearRegression(samples.Points, samples.Points[0].T)

prometheus java step计算 prometheus rate计算方法_sed_03

        Matrix的定义:

prometheus java step计算 prometheus rate计算方法_prometheus_04

        Series的定义:

prometheus java step计算 prometheus rate计算方法_斜率_05

         继续查看linearRegression 代码:

prometheus java step计算 prometheus rate计算方法_sed_06

        基本上就是简单线性回归求斜率的实现,其中涉及到了一个kahanSumInc函数,这里笔者一开始认为是逻辑处理,后来查询资料发现是一个公共的Kahan求和算法,查询了一下kahan求和算法

prometheus java step计算 prometheus rate计算方法_prometheus_07

        回到Promethues的工程实现:

prometheus java step计算 prometheus rate计算方法_斜率_08

         学到这里,也是有所收获,以后如果遇到处理多float类型的加和精度损失问题,可以借鉴与参考,尤其是一些涉及到钱的功能。

        而至于deriv函数,其得出的值确实是导数,或者说是斜率。它可以代表指标变化的加速度,但是无法代表变化率,因此想要求变化率,还得另辟蹊径。

最终解决

        我们知道,变化率=(X2-X1) / X1,对于Promethues的指标来说,我们可以拿到当前的值X2,据我的了解,可能不全面,没有函数和方法能直接拿到前一个时刻X1的值,但是,我们可以通过delta函数来拿到差值,也就是说:

delta=X2-X1

所以:

X1=X2-delta

所以:
变化率=delta / (X2 - delta)

最终的监控指标写法:

1、如果要监控最近两个数据时间点的变化率,类似于irate,使用以下方法:

abs(idelta(sum({__name__=~"current_qps"})[1m]) / (sum({__name__=~"current_qps"})- idelta(sum({__name__=~"current_qps"})[1m]))) > 0.3

2、如果要看1min平均的,类似于rate,使用以下方法:

abs(delta(sum({__name__=~"current_qps"})[1m]) / (sum({__name__=~"current_qps"})- delta(sum({__name__=~"current_qps"})[1m]))) > 0.3

在线上验证了下,数据符合预期。

总结与思考

        遇到问题我们习惯于百度与google,而不是自己分析。google出来的答案不一定是合理的,而且由于大部分是英文的,理解也可能有偏差。因此,对于问题和需求,在google出的资料不是特别符合的情况下,还是要从需求本身出发,转换思维模式,利用手头上有的资源来实现我们的目的。