Prometheus之PromQL用法详解
前言
Promethues是目前一个比较流行的开源监控项目,被使用也越来越多。我们都知道Prometheus是通过时序数据库来保存数据的,那么Prometheus采集到数据后,是如何保存在自已的时序数据库中的呢?通常我们看到Prometheus的数据指标都类似这样:node_cpu_seconds_total{cpu="0",instance="10.20.9.183:9101",job="node_exporter",mode="system"},可以看到它是通过指标名称(metrics name)以及对应的一组标签(labelset)来唯一标识一条时间序列。那么如何对这种数据进行筛选查询、四则运算、聚合运算,以实现我们想要绘制的指标监控数据,就需要用到下面将要讲到的PromeQL了。
1 PromQL介绍
Prometheus提供了一种称为PromQL(Prometheus Query Language)的功能性查询语言,让用户可以实时选择和聚合时间序列数据。在Prometheus默认的浏览器中,表达式的结果既可以显示为图形,也可以以表格数据的形式显示,或者由外部系统通过HTTP API使用。
PromeQL在很多地方都会被用到,在Prometheus UI界面搜索监控指标时会用到,在Alertmanager配置告警时也会用到,在Grafana配置监控展示的时候也会用到。
下面在讲解的过程中,使用到了很多例子,可能部分例子在真实的监控环境中并没有具体的实际含义,仅仅是为了进行演示,便于理解学习。因为在实际的生产环境中,要得到一些目标监控值,是要综合多个运算符或者函数来实现的。
2 数据类型
在Prometheus的表达式语言中,表达式或子表达式的计算结果可以为四种类型之一:
Instant vector - 瞬时数据:一组时间序列,每个时间序列包含一个样本,都共享相同的时间戳;
Range vector - 区间数据:一组时间序列,其中包含每个时间序列随时间变化的一系列数据点;
Scalar - 简单数据:一个简单的数字浮点值;
String - 字符串值:一个简单的字符串值,目前未使用。
2.1 Instant vector 瞬时数据
瞬时数据表示的是在当前时刻的数据。
查询cpu的使用时间
node_cpu_seconds_total
2.2 Range vector 区间数据
区间数据表示的是在某一个时间范围内的数据,可以分为以下几种。
2.2.1 Time Durations
Time Durations表示持续时间,计量单位有以下这些:
ms - 毫秒
s - 秒
m - 分
h - 时
d - 天
w - 周
y - 年
查询cpu 1分钟内的使用时间
采样周期是15s,可以看到1分钟内,每个指标有4个值
node_cpu_seconds_total[1m]
2.2.2 Offset modifier
Offset modifier表示偏移量修饰符,允许更改查询中单个瞬间和范围向量的时间偏移量。
查询cpu使用时间在5分钟之前的数据
node_cpu_seconds_total offset 5m
2.2.3 @ modifier
@修饰符,后面加上一个unix时间戳,可以查询该时间点的数据。
注意:这种用法需要prometheus启动的时候添加参数:--enable-feature=promql-at-modifier,并且以前老版本的prometheus可能不支持这种用法。
查询2022.01.25 00:00:00 这个时刻cpu的使用时间
node_cpu_seconds_total @ 1643040000
2.3 Scalar 简单数据
简单数据没有时间属性,每个时间点数值一致。
查询cpu使用时间,所有标签的个数
count(node_cpu_seconds_total)
3 条件匹配
PromQL支持各种条件匹配
条件匹配符有:==、!=、=~、!~
= : 选择与提供的字符串完全相同的数据
!= : 选择不等于提供的字符串的数据
=~ : 选择与提供的字符串进行正则表达式匹配的数据
!~ : 选择与提供的字符串不匹配的数据
3.1 普通匹配
查询cpu第一个核的使用时间
node_cpu_seconds_total{cpu="0"}
查询cpu除了第一个核以外的其他核的使用时间
node_cpu_seconds_total{cpu!="0"}
查询cpu第一个、第二个核的使用时间
node_cpu_seconds_total{cpu=~"0|1"}
查询cpu除了第一个、第二个核的使用时间
node_cpu_seconds_total{cpu!~"0|1"}
3.2 正则匹配
查询cpu第一个核,idle、iowait、irq态的使用时间
node_cpu_seconds_total{cpu="0",mode=~"i.*"}
4 运算符
PromQL支持各种运算符
4.1 比较运算符
比较运算符有:==、!=、>、<、>=、<=
比较运算符与数学中和其他语言的含义是一样的
查询cpu使用时间为0的标签
node_cpu_seconds_total == 0
查询cpu使用时间不为0的标签
node_cpu_seconds_total != 0
查询cpu使用时间大于100的标签
node_cpu_seconds_total > 100
查询cpu使用时间小于4的标签
node_cpu_seconds_total < 4
>=、<=的用法与上面类似,这里就不在举例子了。
4.2 算术运算符
算数运算符有:+、-、*、/、%、^
与数学里面的算数运算符含义一样,分别为:加、减、乘、除、取余、幂次方
需要注意的是:做算数运算符的两个指标,必须标签完全一样
计算主机cpu使用时间和主机上面虚拟机使用时间的和
node_cpu_seconds_total + node_cpu_guest_seconds_total
计算主机cpu使用时间和主机上面虚拟机使用时间的差
node_cpu_seconds_total - node_cpu_guest_seconds_total
计算2倍cpu使用时间
node_cpu_seconds_total - node_cpu_guest_seconds_total
cpu使用时间除以1000
node_cpu_seconds_total - node_cpu_guest_seconds_total
cpu使用时间的平方
node_cpu_seconds_total - node_cpu_guest_seconds_total
4.3 逻辑运算符
逻辑运算符有:and、or、unless
对应其他语言的与、或、非
查询cpu的时间大于0并且小于10的标签
node_cpu_seconds_total > 0 and node_cpu_seconds_total < 10
查询cpu的时间大于100或者小于1的标签
node_cpu_seconds_total > 100 or node_cpu_seconds_total < 1
查询cpu的时间不是小于100的标签
node_cpu_seconds_total unless node_cpu_seconds_total < 100
4.4 聚合运算符
4.4.1 sum:求和
计算cpu所有核的总使用时间
sum(node_cpu_seconds_total)
4.4.2 count:计数
计算cpu的核数
count(node_cpu_seconds_total{mode="system"})
4.4.3 max:求最大值
查询第一个cpu左右运行状态中使用时间最大的那一个状态
max(node_cpu_seconds_total{cpu="0"})
4.4.4 min:求最小值
查询第一个cpu左右运行状态中使用时间最小的那一个状态
min(node_cpu_seconds_total{cpu="0"})
4.4.5 avg:求平均值
求cpu内核态(system)的平均使用时间
avg(node_cpu_seconds_total{mode="system"})
4.4.6 topk:取前面几个较大值
查询cpu内核态(system)的排名前两个的核
topk(2,node_cpu_seconds_total{mode="system"})
4.4.7 bottomk:取后面几个较小值
查询cpu内核态(system)的排名后两个的核
bottomk(2,node_cpu_seconds_total{mode="system"})
4.5 匹配运算
4.5.1 on:关联标签
将两个指标进行算数运算时,如果两个指标的标签不完全相同,可以匹配相同的标签进行计算
计算cpu每个核系统态和用户态的总使用时间
node_cpu_seconds_total{mode="system"} + on (instance,cpu) node_cpu_seconds_total{mode="user"}
4.5.2 ignoring:忽略标签
将两个指标进行算数运算时,如果两个指标的标签不完全相同,可以忽略不一样的标签进行计算
忽略运行状态,计算cpu每个核系统态和用户态的总使用时间,实现的效果与上面相同
node_cpu_seconds_total{mode="system"} + ignoring (mode) node_cpu_seconds_total{mode="user"}
4.5.3 by:以某一个标签进行计算
计算每个cpu核的各个状态的总使用时间
sum(node_cpu_seconds_total) by (cpu) 或者 sum by (cpu) (node_cpu_seconds_total)
计算每个状态下cpu所有核的总使用时间
sum(node_cpu_seconds_total) by (mode) 或者 sum by (mode) (node_cpu_seconds_total)
4.5.4 without:舍弃某个标签进行计算
作用与by相反
计算每个cpu核的各个状态的总使用时间,与上面by的效果相同
sum (node_cpu_seconds_total) without (mode) 或者 sum without (mode) (node_cpu_seconds_total)
5 函数
5.1 速率函数
5.1.1 increase:求增长值
求cpu第一个核,system态的使用时间在过去1分钟的增加量
increase(node_cpu_seconds_total{cpu="0",mode="system"}[1m])
求cpu第一个核,system态的使用时间在过去1分钟的每秒平均增速
increase(node_cpu_seconds_total{cpu="0",mode="system"}[1m]) / 60
5.1.2 rate:求每秒平均增长率
与上面的increase求增速效果相同
求cpu第一个核,system态的使用时间在过去1分钟的每秒平均增速
rate(node_cpu_seconds_total{cpu="0",mode="system"}[1m])
5.1.3 irate:求每秒瞬时增长率
求cpu第一个核,system态的使用时间在过去1分钟的每秒瞬时增速
irate(node_cpu_seconds_total{cpu="0",mode="system"}[1m])
5.1.4 rate和irate区别
rate会取指定时间范围内所有样本数据点,然后计算平均增长速率,可能容易陷入“长尾问题”,无法反应出突发变化,比如机器在一个时间窗口内,可能出现CPU占用100%的情况,但是通过计算在时间窗口内的平均增长率,数据就会被平均,就无法反应出该问题。
irate是通过在指定时间范围内最后两个样本数据来计算区间增长速率,反应出的是瞬时增长率。这种方式可以避免在时间窗口范围内的“长尾问题”,体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。
虽然rate和rate都会用于计算某个指标在一定时间间隔内的变化速率,irate适合快速变化的计数器(counter),而rate适合缓慢变化的计数器(counter)。
5.2 取整函数
5.2.1 ceil:向上取整
查看第一个cpu的使用时间,向上取整
ceil(node_cpu_seconds_total{cpu="0"})
5.2.2 floor:向下取整
查看第一个cpu的使用时间,向下取整
floor(node_cpu_seconds_total{cpu="0"})
5.2.3 round:四舍五入
查看第一个cpu的使用时间,四舍五入
round(node_cpu_seconds_total{cpu="0"})
下面是没有经过处理的
查看cpu的使用时间,可以对比上面几个
node_cpu_seconds_total{cpu="0"}
5.3 排序函数
5.3.1 sort:排序
查看第一个cpu的使用时间,从小到大排序
sort(node_cpu_seconds_total{cpu="0"})
5.3.2 sort_desc:逆向排序
查看第一个cpu的使用时间,从小到大排序
sort_desc(node_cpu_seconds_total{cpu="0"})
5.4 其他函数
5.4.1 abs:求绝对值
求cpu使用时间的绝对值
abs(node_cpu_seconds_total{cpu="0"})
5.4.2 absent:设置值
当指标存在时不会返回值,当指标不存在时会返回值,切回将值设置为1
node_cpu_seconds_total{cpu="0",mode="system"}本身存在,返回空
absent(node_cpu_seconds_total{cpu="0",mode="system"})
node_cpu_cpu_seconds_total{cpu="0",mode="system"}本身不存在,返回1
absent(node_cpu_cpu_seconds_total{cpu="0",mode="system"})
参考文档
https://prometheus.io/docs/prometheus/latest/querying/basics/