01
—
Counter指标类型
在prometheus客户端(比如golang、python、java等客户端)中,提供了4种核心的指标类型
Counter(计数器)
Gauge(仪表盘)
Histogram(直方图)
Summary(摘要)
本次分享主要是针对Counter类型,Counter就是一个计数器类型,是一种单调递增的指标,在没有发生重置的情况下,其样本值应该是不断的增大的。
比如我们访问prometheus服务端提供的metrics的url,会返回很多样本数据,其中process_cpu_seconds_total
就是一个Counter类型的,该样本数据表示prometheus服务端进程已经消耗了多少秒CPU的时间,该值会不断的增长,除非你重启了prometheus服务端。
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.# TYPE process_cpu_seconds_total counterprocess_cpu_seconds_total 90662.26
02
—
Counter的常用函数
rate(v range-vector)
irate(v range-vector)
increate(v range-vector)
它们都接受一个区间向量,然后返回一个瞬时向量。
03
—
什么是rate()和irate()
在开始使用rate()的时候,我其实还是蛮困惑的,网上搜了一通都说是平均增长率,这个时候我就很困惑,平均增长率?是每秒还是每分钟,那在rate前面加一个i的irate又有什么区别呢?今天就来彻底的做一个了断!
我们现在来看两句比较官网的解释:
rate:rate函数可以直接计算区间向量v在时间窗口内的平均增长速率;
irate:irate函数是PromQL针对长尾效应专门提供的灵敏度更高的函数,用于计算区间向量的增长速率,但是其反映出的是瞬时增长速率。
好了,我们忘记上面两句话。
接下来带大家看一下rate到底是怎么计算的:
我们可以看下prometheus的源码:
var FunctionCalls = map[string]FunctionCall{ "irate": funcIrate,... "rate": funcRate,...}// === rate(node parser.ValueTypeMatrix) Vector ===func funcRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) Vector { return extrapolatedRate(vals, args, enh, true, true)}// extrapolatedRate is a utility function for rate/increase/delta.// It calculates the rate (allowing for counter resets if isCounter is true),// extrapolates if the first/last sample is close to the boundary, and returns// the result as either per-second (if isRate is true) or overall.func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper, isCounter bool, isRate bool) Vector {... var ( counterCorrection float64 lastValue float64 ) for _, sample := range samples.Points { // 该for循环用于获取区间的最后一个值lastValue, if isCounter && sample.V < lastValue { counterCorrection += lastValue } lastValue = sample.V } resultValue := lastValue - samples.Points[0].V + counterCorrection // samples.Points[0].V 为区间的第一个值 ... if isRate { resultValue = resultValue / ms.Range.Seconds() } return append(enh.Out, Sample{ Point: Point{V: resultValue}, })
这里的counterCorrection
用来纠正由于客户端重启之后,counter值的重置,比如现在现在有如下几个样本值
2 3 4 5 6 1 2 3
当counter值增加到6的时候,counter值重置了,lastValue=3
,samples.Points[0].V=2
, counterCorrection=6
,resultValue=3-2+6=7
,结果为7
,如果中间客户端没有重启,就是:
2 3 4 5 6 7 8 9
那么计算的结果resultValue=9-2+0=7
,所以两次得到的结果一样。
简单的来说,在没有重置的情况下,rate就是(区间最新一个样本值-区间的第一个样本值)/区间的秒数
,也就是平均每秒增长了多少值。
为了验证这个结果,我们可以自己动手计算一下:
获取5分钟内的样本数据:process_cpu_seconds_total{instance="192.168.18.158:6443",job="kubernetes-apiservers"}[5m]
获取平均每秒增长的速率:rate(process_cpu_seconds_total{instance="192.168.18.158:6443",job="kubernetes-apiservers"}[5m])
,得到0.07975862068967124
计算:
从计算出来的结果就可以看出,rate就是这么计算的(由于浮点数精度丢失的原因,在结果上可能有微小的区别)。
接下来我们看看irate
首先来看下prometheus的源码:
func instantValue(vals []parser.Value, out Vector, isRate bool) Vector { samples := vals[0].(Matrix)[0] // No sense in trying to compute a rate without at least two points. Drop // this Vector element. if len(samples.Points) < 2 { return out } lastSample := samples.Points[len(samples.Points)-1] // 最后一个样本值 previousSample := samples.Points[len(samples.Points)-2] // 倒数第二个样本值 var resultValue float64 if isRate && lastSample.V < previousSample.V { // Counter reset. resultValue = lastSample.V } else { resultValue = lastSample.V - previousSample.V } sampledInterval := lastSample.T - previousSample.T // 获取最后一个样本值和倒数第二个样本值的时间间隔 if sampledInterval == 0 { // Avoid dividing by 0. return out } if isRate { // Convert to per-second. resultValue /= float64(sampledInterval) / 1000 } return append(out, Sample{ Point: Point{V: resultValue}, })
从源码中我们可以看出,irate得到的结果就是(区间的最后一个样本值-区间的倒数第二个样本值)/(区间的最后一个样本值的时间戳-区间的倒数第二个样本值的时间戳)
。
同样,我们自己来计算一下:
获取5分钟内的样本数据:process_cpu_seconds_total{instance="192.168.18.158:6443",job="kubernetes-apiservers"}[5m]
获取irate增长的速率:irate(process_cpu_seconds_total{instance="192.168.18.158:6443",job="kubernetes-apiservers"}[5m])
,得到0.05499999999883585
计算:
由结果所看,这就是我们想要得到的。
04
—
rate()和irate()的区别
irate函数只能用于绘制快速变化的计数器,在长期趋势分析或者告警中推荐使用rate函数;
irate的曲线比较曲折,rate的曲线比较平缓;
当被检测对象处于异常状态时,irate会产生过于敏感的告警,并带有很多误报。由于rate函数利用的是许多样本的平均值,因此可以抵抗短暂的骤降和尖峰。在长期告警中要使用rate函数而不是irate函数。
在grafana的的dashboard中,我们也可以看irate的曲线比rate的曲线更加的曲折:
最后,increate函数就是区间向量的最后一个样本值-区间向量的第一个样本值
。
参考:
https://zhangguanzhang.github.io/2020/07/30/prometheus-rate-and-irate/
https://github.com/prometheus/prometheus/blob/acee998df69777b2f7e6b7634a87fd0031ff96f4/promql/functions.go#L159
《Prometheus云原生监控:运维与开发实战》朱政科 著