前言

    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

条件匹配0.png

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]

时间0.png

2.2.2 Offset modifier

Offset modifier表示偏移量修饰符,允许更改查询中单个瞬间和范围向量的时间偏移量。 查询cpu使用时间在5分钟之前的数据

node_cpu_seconds_total offset 5m

时间1.png

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.png

2.3 Scalar 简单数据

简单数据没有时间属性,每个时间点数值一致。 查询cpu使用时间,所有标签的个数

count(node_cpu_seconds_total)

count0.png

3 条件匹配

PromQL支持各种条件匹配 条件匹配符有:==、!=、=~、!~ = : 选择与提供的字符串完全相同的数据 != : 选择不等于提供的字符串的数据 =~ : 选择与提供的字符串进行正则表达式匹配的数据 !~ : 选择与提供的字符串不匹配的数据

3.1 普通匹配

查询cpu第一个核的使用时间

node_cpu_seconds_total{cpu="0"}

条件匹配1.png 查询cpu除了第一个核以外的其他核的使用时间

node_cpu_seconds_total{cpu!="0"}

条件匹配2.png 查询cpu第一个、第二个核的使用时间

node_cpu_seconds_total{cpu=~"0|1"}

条件匹配3.png 查询cpu除了第一个、第二个核的使用时间

node_cpu_seconds_total{cpu!~"0|1"}

条件匹配4.png

3.2 正则匹配

查询cpu第一个核,idle、iowait、irq态的使用时间

node_cpu_seconds_total{cpu="0",mode=~"i.*"}

条件匹配5.png

4 运算符

PromQL支持各种运算符

4.1 比较运算符

比较运算符有:==、!=、>、<、>=、<= 比较运算符与数学中和其他语言的含义是一样的 查询cpu使用时间为0的标签

node_cpu_seconds_total == 0

比较运算符1.png 查询cpu使用时间不为0的标签

node_cpu_seconds_total != 0

比较运算符2.png 查询cpu使用时间大于100的标签

node_cpu_seconds_total > 100

比较运算符3.png 查询cpu使用时间小于4的标签

node_cpu_seconds_total < 4

比较运算符4.png >=、<=的用法与上面类似,这里就不在举例子了。

4.2 算术运算符

算数运算符有:+、-、*、/、%、^ 与数学里面的算数运算符含义一样,分别为:加、减、乘、除、取余、幂次方 需要注意的是:做算数运算符的两个指标,必须标签完全一样 计算主机cpu使用时间和主机上面虚拟机使用时间的和

node_cpu_seconds_total + node_cpu_guest_seconds_total

算数运算符1.png 计算主机cpu使用时间和主机上面虚拟机使用时间的差

node_cpu_seconds_total - node_cpu_guest_seconds_total

算数运算符2.png 计算2倍cpu使用时间

node_cpu_seconds_total - node_cpu_guest_seconds_total

算数运算符3.png cpu使用时间除以1000

node_cpu_seconds_total - node_cpu_guest_seconds_total

算数运算符4.png cpu使用时间的平方

node_cpu_seconds_total - node_cpu_guest_seconds_total

算数运算符5.png

4.3 逻辑运算符

逻辑运算符有:and、or、unless 对应其他语言的与、或、非 查询cpu的时间大于0并且小于10的标签

node_cpu_seconds_total > 0 and node_cpu_seconds_total < 10

逻辑运算符1.png 查询cpu的时间大于100或者小于1的标签

node_cpu_seconds_total > 100 or node_cpu_seconds_total < 1

逻辑运算符2.png 查询cpu的时间不是小于100的标签

node_cpu_seconds_total unless node_cpu_seconds_total < 100

逻辑运算符3.png

4.4 聚合运算符

4.4.1 sum:求和

计算cpu所有核的总使用时间

sum(node_cpu_seconds_total)

sum.png

4.4.2 count:计数

计算cpu的核数

count(node_cpu_seconds_total{mode="system"})

count.png

4.4.3 max:求最大值

查询第一个cpu左右运行状态中使用时间最大的那一个状态

max(node_cpu_seconds_total{cpu="0"})

max.png

4.4.4 min:求最小值

查询第一个cpu左右运行状态中使用时间最小的那一个状态

min(node_cpu_seconds_total{cpu="0"})

min.png

4.4.5 avg:求平均值

求cpu内核态(system)的平均使用时间

avg(node_cpu_seconds_total{mode="system"})

avg.png

4.4.6 topk:取前面几个较大值

查询cpu内核态(system)的排名前两个的核

topk(2,node_cpu_seconds_total{mode="system"})

topk.png

4.4.7 bottomk:取后面几个较小值

查询cpu内核态(system)的排名后两个的核

bottomk(2,node_cpu_seconds_total{mode="system"})

bottomk.png

4.5 匹配运算

4.5.1 on:关联标签

将两个指标进行算数运算时,如果两个指标的标签不完全相同,可以匹配相同的标签进行计算 计算cpu每个核系统态和用户态的总使用时间

node_cpu_seconds_total{mode="system"} + on (instance,cpu) node_cpu_seconds_total{mode="user"}

on.png

4.5.2 ignoring:忽略标签

将两个指标进行算数运算时,如果两个指标的标签不完全相同,可以忽略不一样的标签进行计算 忽略运行状态,计算cpu每个核系统态和用户态的总使用时间,实现的效果与上面相同

node_cpu_seconds_total{mode="system"} + ignoring (mode) node_cpu_seconds_total{mode="user"}

ignoring.png

4.5.3 by:以某一个标签进行计算

计算每个cpu核的各个状态的总使用时间

sum(node_cpu_seconds_total) by (cpu) 或者 sum by (cpu) (node_cpu_seconds_total)

by.png 计算每个状态下cpu所有核的总使用时间

sum(node_cpu_seconds_total) by (mode) 或者 sum by (mode) (node_cpu_seconds_total)

by2.png

4.5.4 without:舍弃某个标签进行计算

作用与by相反 计算每个cpu核的各个状态的总使用时间,与上面by的效果相同

sum (node_cpu_seconds_total) without (mode) 或者 sum without (mode) (node_cpu_seconds_total)

without.png

5 函数

5.1 速率函数

5.1.1 increase:求增长值

求cpu第一个核,system态的使用时间在过去1分钟的增加量

increase(node_cpu_seconds_total{cpu="0",mode="system"}[1m])

increase1.png 求cpu第一个核,system态的使用时间在过去1分钟的每秒平均增速

increase(node_cpu_seconds_total{cpu="0",mode="system"}[1m]) / 60

increase2.png

5.1.2 rate:求每秒平均增长率

与上面的increase求增速效果相同 求cpu第一个核,system态的使用时间在过去1分钟的每秒平均增速

rate(node_cpu_seconds_total{cpu="0",mode="system"}[1m])

rate.png

5.1.3 irate:求每秒瞬时增长率

求cpu第一个核,system态的使用时间在过去1分钟的每秒瞬时增速

irate(node_cpu_seconds_total{cpu="0",mode="system"}[1m])

irate.png

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"})

ceil.png

5.2.2 floor:向下取整

查看第一个cpu的使用时间,向下取整

floor(node_cpu_seconds_total{cpu="0"})

floor.png

5.2.3 round:四舍五入

查看第一个cpu的使用时间,四舍五入

round(node_cpu_seconds_total{cpu="0"})

round.png 下面是没有经过处理的 查看cpu的使用时间,可以对比上面几个

node_cpu_seconds_total{cpu="0"}

ceilfloor.png

5.3 排序函数

5.3.1 sort:排序

查看第一个cpu的使用时间,从小到大排序

sort(node_cpu_seconds_total{cpu="0"})

sort.png

5.3.2 sort_desc:逆向排序

查看第一个cpu的使用时间,从小到大排序

sort_desc(node_cpu_seconds_total{cpu="0"})

sort_desc.png

5.4 其他函数

5.4.1 abs:求绝对值

求cpu使用时间的绝对值

abs(node_cpu_seconds_total{cpu="0"})

abs.png

5.4.2 absent:设置值

当指标存在时不会返回值,当指标不存在时会返回值,切回将值设置为1 node_cpu_seconds_total{cpu="0",mode="system"}本身存在,返回空

absent(node_cpu_seconds_total{cpu="0",mode="system"})

absent1.png node_cpu_cpu_seconds_total{cpu="0",mode="system"}本身不存在,返回1

absent(node_cpu_cpu_seconds_total{cpu="0",mode="system"})

absent2.png

参考文档

https://prometheus.io/docs/prometheus/latest/querying/basics/