prometheus 时间戳_【Prometheus】你真的搞懂了rate()和irate()的区别了吗?

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到底是怎么计算的:

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=6resultValue=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]

prometheus 时间戳_【Prometheus】你真的搞懂了rate()和irate()的区别了吗?_第1张图片

  • 获取平均每秒增长的速率:rate(process_cpu_seconds_total{instance="192.168.18.158:6443",job="kubernetes-apiservers"}[5m]),得到0.07975862068967124

prometheus 时间戳_【Prometheus】你真的搞懂了rate()和irate()的区别了吗?_第2张图片

计算:

1c0cdecf4fe5c224d35879aa44bf2ff0.png

从计算出来的结果就可以看出,rate就是这么计算的(由于浮点数精度丢失的原因,在结果上可能有微小的区别)。

接下来我们看看irate

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]

prometheus 时间戳_【Prometheus】你真的搞懂了rate()和irate()的区别了吗?_第3张图片

  • 获取irate增长的速率:irate(process_cpu_seconds_total{instance="192.168.18.158:6443",job="kubernetes-apiservers"}[5m]),得到0.05499999999883585

prometheus 时间戳_【Prometheus】你真的搞懂了rate()和irate()的区别了吗?_第4张图片

计算:

d9ef722c52dec10d1fa3ebfa27d46948.png

由结果所看,这就是我们想要得到的。

04

rate()和irate()的区别

  • irate函数只能用于绘制快速变化的计数器,在长期趋势分析或者告警中推荐使用rate函数;

  • irate的曲线比较曲折,rate的曲线比较平缓;

  • 当被检测对象处于异常状态时,irate会产生过于敏感的告警,并带有很多误报。由于rate函数利用的是许多样本的平均值,因此可以抵抗短暂的骤降和尖峰。在长期告警中要使用rate函数而不是irate函数。

在grafana的的dashboard中,我们也可以看irate的曲线比rate的曲线更加的曲折:

prometheus 时间戳_【Prometheus】你真的搞懂了rate()和irate()的区别了吗?_第5张图片

最后,increate函数就是区间向量的最后一个样本值-区间向量的第一个样本值

参考:

  • https://zhangguanzhang.github.io/2020/07/30/prometheus-rate-and-irate/

  • https://github.com/prometheus/prometheus/blob/acee998df69777b2f7e6b7634a87fd0031ff96f4/promql/functions.go#L159

  • 《Prometheus云原生监控:运维与开发实战》朱政科 著

你可能感兴趣的:(prometheus,时间戳)