内置函数
Counter 指标增长率
Counter 类型的监控指标其特点是只增不减,在没有发生重置(如服务器重启,应用重启)的情况下其样本值应该是不断增大的。为了能够更直观的表示样本数据的变化剧烈情况,需要计算样本的增长速率。
increase
increase(v range-vector) 函数是PromQL 中提供的众多内置函数之一。其中参数v 是一个区间向量,increase 函数获取区间向量中的第一个后最后一个样本并返回其增长量。因此,可以通过以下表达式Counter 类型指标的增长率:
increase ( node_cpu[2m] ) / 120
这里通过node_cpu[2m] 获取时间序列最近两分钟的所有样本,increase 计算出最近两分钟的增长量,最后除以时间120 秒得到node_cpu 样本在最近两分钟的平均增长率。并且这个值也近似于主机节点最近两分钟内的平均CPU 使用率。
rate
rate(v range-vector)可以直接计算区间向量v 在时间窗口内平均增长速率,它会在单调性发生变化时( 如由于采样目标重启引起的计数器复位) 自动中断。该函数的返回结果不带有度量指标,只有标签列表。以下示例表达式返回区间向量中每个时间系列在过去5 分钟内测量的每秒HTTP 请求率:
rate ( http_requests_total { job = "api-server" } [5m] )
rate 应仅用于Counter ,在长期趋势分析或者告警中推荐使用这个函数。
注意,当将rate() 与聚合运算符(例如sum() )或随时间聚合的函数(任何以_over_time
结尾的函数)组合时,始终首先采用rate() ,然后聚合。否则,当目标重新启动时,rate() 无法检测计数器重置。
irate
rate 或者increase 函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。例如,对于主机而言在2 分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU 占用100% 的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。
irate(v range-vector) 函数用于计算区间向量的增长率,但是其反应出的是瞬时增长率。irate 函数是通过区间向量中最后两个两本数据来计算区间向量的增长速率,它会在单调性发生变化时( 如由于采样目标重启引起的计数器复位) 自动中断。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate 函数绘制的图标能够更好的反应样本数据的瞬时变化状态。
例如,以下表达式返回区间向量中每个时间序列过去5 分钟内最后两个样本数据的HTTP 请求数的增长率:
irate ( http_requests_total { job = "api-server" } [5m] )
irate 函数相比于rate 函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate 的这种灵敏度反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用rate 函数。当将irate() 函数与聚合运算符(例如sum() )或随时间聚合的函数(任何以 _over_time
结尾的函数)一起使用时,必须先执行irate 函数,然后再进行聚合操作,否则当采样目标重新启动时irate() 无法检测到计数器是否被重置。
预测Gauge 指标变化趋势
在一般情况下,系统管理员为了确保业务的持续可用运行,会针对服务器的资源设置相应的告警阈值。例如,当磁盘空间只剩512MB 时向相关人员发送告警通知。这种基于阈值的告警模式对于当资源用量是平滑增长的情况下是能够有效的工作的。但是如果资源不是平滑变化的呢?比如有些某些业务增长,存储空间的增长速率提升了高几倍。这时,如果基于原有阈值去触发告警,当系统管理员接收到告警以后可能还没来得及去处理问题,系统就已经不可用了。因此阈值通常来说不是固定的,需要定期进行调整才能保证该告警阈值能够发挥去作用。那么还有没有更好的方法吗?
PromQL 中内置的predict_linear(v range-vector, t scalar) 函数可以帮助系统管理员更好的处理此类情况,predict_linear 函数可以预测时间序列v 在t 秒后的值。它基于简单线性回归的方式,对时间窗口内的样本数据进行统计,从而可以对时间序列的变化趋势做出预测。例如,基于2 小时的样本数据,来预测主机可用磁盘空间的是否在4 个小时候被占满,可以使用如下表达式:
predict_linear ( node_filesystem_free { job = "node" } [2h] , 4 * 3600 ) < 0
统计Histogram 指标的分位数
Histogram 和Summary 都可以同于统计和分析数据的分布情况。区别在于Summary 是直接在客户端计算了数据分布的分位数情况。而Histogram 的分位数计算需要通过histogram_quantile(φ float, b instant-vector) 函数进行计算。其中φ (0<φ<1)表示需要计算的分位数,如果需要计算中位数φ 取值为0.5 ,以此类推即可。
以指标http_request_duration_seconds_bucket 为例:
# HELP http_request_duration_seconds request duration histogram
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket { le = "0.5" } 0
http_request_duration_seconds_bucket { le = "1" } 1
http_request_duration_seconds_bucket { le = "2" } 2
http_request_duration_seconds_bucket { le = "3" } 3
http_request_duration_seconds_bucket { le = "5" } 3
http_request_duration_seconds_bucket { le = "+Inf" } 3
http_request_duration_seconds_sum 6
http_request_duration_seconds_count 3
当计算9 分位数时,使用如下表达式:
histogram_quantile ( 0.5 , http_request_duration_seconds_bucket )
通过对Histogram 类型的监控指标,用户可以轻松获取样本数据的分布情况。同时分位数的计算,也可以非常方便的用于评判当前监控指标的服务水平。需要注意的是通过histogram_quantile 计算的分位数,并非为精确值,而是通过http_request_duration_seconds_bucket 和http_request_duration_seconds_sum 近似计算的结果。
动态标签替换
一般来说来说,使用PromQL 查询到时间序列后,可视化工具会根据时间序列的标签来渲染图表。例如通过up 指标可以获取到当前所有运行的Exporter 实例以及其状态:
up {instance="localhost:8080" ,job="cadvisor" } 1
up {instance="localhost:9090" ,job="prometheus" } 1
up {instance="localhost:9100" ,job="node" } 1
这是可视化工具渲染图标时可能根据,instance 和job 的值进行渲染,为了能够让客户端的图标更具有可读性,可以通过label_replace 标签为时间序列添加额外的标签。label_replace 的具体参数如下:
label_replace(v instant-vector , dst_label string , replacement string , src_label string , regex string )
该函数会依次对v 中的每一条时间序列进行处理,通过regex 匹配src_label 的值,并将匹配部分relacement 写入到dst_label 标签中。如下所示:
label_replace(up, "host" , "$1 " , "instance" , "(.*):.*" )
函数处理后,时间序列将包含一个host 标签,host 标签的值为Exporter 实例的IP 地址:
up {host="localhost" ,instance="localhost:8080" ,job="cadvisor" } 1
up {host="localhost" ,instance="localhost:9090" ,job="prometheus" } 1
up {host="localhost" ,instance="localhost:9100" ,job="node" } 1
除了label_replace 以外,Prometheus 还提供了label_join 函数,该函数可以将时间序列中v 多个标签src_label 的值,通过separator 作为连接符写入到一个新的标签dst_label 中:
label_join(v instant-vector , dst_label string , separator string , src_label_1 string , src_label_2 string , ...)
label_replace 和label_join 函数提供了对时间序列标签的自定义能力,从而能够更好的于客户端或者可视化工具配合。