一、promQL查询语言
理解普罗米修斯查询语言对于执行有见地的仪表盘、容量规划和警报非常重要。但为此我们需要从学习基础知识开始
1.选择器
普罗米修斯被设计用来处理成千上万的时间序列。根据标签的组合,每个指标名称可以有几个不同的时间序列;当来自不同工作的类似名称的指标混在一起时,查询正确的数据可能看起来很困难,甚至完全令人费解。在普罗米修斯中,选择器指的是一组标签匹配器。度量名称也包含在这个定义中,因为从技术上讲,他的内部表示也是一个标签,尽管是一个特殊的标签:__ name __ 。选择器中的每个标签名称/值对称为标签匹配器,多个匹配器可用于进一步筛选选择器匹配的时间序列。标签匹配器用花括号括起来。如果不需要匹配器,可以省略花括号。选择器可以返回即时或范围向量。下面是一个选择器的例子:
$ prometheus_build_info{version='2.31.1'}
上卖弄的选择器等同于如下:
$ {__name__="prometheus_build_info",verison="2.31.1"}
2.标签匹配器
匹配器用于将查询搜索限制为特定一组标签值,下面使用'node_cpu_seconds_total' metric来阐述标签匹配的操作:=,!=,=,和!。如果没有任何匹配的规范,今次度量就会返回一个包含度量名称的所有可用时间序列的即时向量,以及所有的CPU核心数(cpu="0",cpu="1")和CPU的型号(mode="idle",mode="iowait",mode="irq",mode="irq",mode="nice",mode="softirq",mode="steal",mode="user",mode="system")
示例1:查询关于所有cpu的结果
$ node_cpu_seconds_toal
示例2:查询cpu=0的结果
$ node_cpu_seconds_toal{cpu="0"}
查询cpu不等于0的结果。
$ node_cpu_seconds_total{cpu!="0"}
=和!支持RE2类型的正则表达式
# 比如只对'mode="user"'和'mode="system"'的感兴趣,那么可以执行如下:
node_cpu_seconds_total{mode=~"(user|system)"}
# 除了'mode="user"'和'mode="system"'的:
node_cpu_seconds_total{mode=!~"(user|system)"}
3.范围、偏移及子查询
1.范围向量
如果要定义一个范围向量选择查询,你必须设置一个即时向量选择器和使用 []
追加一个范围。
下表是对定义一个时间范围:
缩写 | 单位 |
---|---|
s | Seconds |
m | Minutes |
h | Hours |
d | Days |
w | Weeks |
y | Years |
(1)检查最后两分钟HTTP的相应代码是200的。
$ prometheus_http_requests_total{code="200"}[2m]
2.偏移量的修饰符
offset的修饰符查询过去的数据。也就是说可双选择相对于当前时间的多长时间以前。
查询1小时前的最后两分钟相应代码是200的。
$ prometheus_http_requests_total{code="200"}[2m] offset 1h
3.子查询
使用下面的案例来解释子查询语法:
max_over_time(tate(http_requests_total{handler="/health",instance="192.168.1.121.9090"}[5m])[1h:1m])
组件 | 描述 |
---|---|
rate(http_requests_total{handler="/health",instance="192.168.1.121:9090"}[5m]) | 内部的查询,它将五分钟的数据聚合成一个即时向量。 |
[1h | 就像范围向量选择器一样,它定义了相对于查询求值时间的范围大小。 |
:1m] | 要使用的间隔值。如果没有定义,它默认为全局计算区间。 |
max_over_time | 子查询返回一个范围向量,随时间的推移,这个范围向量现在可以成为这个聚合操作的参数。 |
4.PromQL的操作符
1.二进制操作
1.1算术操作符
算数运算符提供两个操作数之间的基本数学运算。
操作符 | 描述 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取余 |
^ | 平方 |
1.2对比操作符
操作符 | 描述 |
---|---|
== | 等于 |
!= | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
比如,下面的即时向量:
process_open_fds{instance="192.168.1.121:9100",job="node"}
应用的对比操作如下:
process_open_fds{job="node"} > 5
此外,我们可以使用bool修饰符不仅返回所有匹配的时间序列,还可以将每个返回的样本修改为1或0,具体取决于比较操作符是保留还是删除样本。
因此,我们还可以应用bool修饰符来做相同的查询:
process_open_fds{job="node"} > bool 5
2.向量匹配
2.1 one-to-one
: 如前所述,由于二进制操作符需要两个操作数,当相同大小和标签集的向量位于一个操作符(即一对一)的每一侧时,将具有完全相同的标签/值对的样本匹配在一起,同时删除度量名称和所有不匹配的元素。
案例1:
1.如下案例将会是我们使用的即时向量:
node_filesystem_avail_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/"}
node_filesystem_avail_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/boot"}
node_filesystem_size_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/"}
node_filesystem_size_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/boot"}
2.应用如下操作:
node_filesystem_avail_bytes{}/node_filesystem_size_bytes{}*100
3.将返回如下的瞬时向量:
{device="/dev/mapper/rhel-root", fstype="xfs", instance="192.168.1.121:9100", job="node", mountpoint="/"}
84.15377159509026
{device="/dev/sda1", fstype="xfs", instance="192.168.1.121:9100", job="node", mountpoint="/boot"}
85.6955744575937
{device="rootfs", fstype="rootfs", instance="192.168.1.121:9100", job="node", mountpoint="/"}
84.15377159509026
{device="tmpfs", fstype="tmpfs", instance="192.168.1.121:9100", job="node", mountpoint="/run"}
98.70732796887634
{device="tmpfs", fstype="tmpfs", instance="192.168.1.121:9100", job="node", mountpoint="/run/user/0"}
100
2.2 Many-to-one 和 one-to-many
有时,您需要执行这样的操作:一边的元素与另一边的几个元素相匹配。当这种情况发生时,您需要向普罗米修斯提供解释这种操作的方法。如果较高的基数在操作的左侧,你可以在on或ignoring后使用group_left修饰符;假如在它的右侧,那么可以使用group_right。
3.逻辑操作
这些操作符是PromQL中唯一可以对多工作的操作符。有三个逻辑运算符可以在表达式之间使用:
操作 | 描述 |
---|---|
and | intersetion |
or | Union |
unless | Complement |
3.1 and的应用案例
1.使用下面的案例:
node_filesystem_avail_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/"}
node_filesystem_avail_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/boot"}
node_filesystem_size_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/"}
node_filesystem_size_bytes{instance="192.168.1.121:9100",job="node",mountpoint="/boot"}
2.应用如下表达式:
node_filesystem_avail_bytes and node_filesystem_size_bytes < 2000000000
3.返回如下结果:
node_filesystem_avail_bytes{device="/dev/sda1", fstype="xfs", instance="192.168.1.121:9100", job="node", mountpoint="/boot"}
911163392
node_filesystem_avail_bytes{device="tmpfs", fstype="tmpfs", instance="192.168.1.121:9100", job="node", mountpoint="/run"}
1429970944
node_filesystem_avail_bytes{device="tmpfs", fstype="tmpfs", instance="192.168.1.121:9100", job="node", mountpoint="/run/user/0"}
289742848
3.2 or的应用案例
node_filesystem_avail_bytes > 2000000 or node_filesystem_avail_bytes < 2500000
3.3unless应用案例
unless逻辑运算符将返回第一个表达式中与第二个表达式的标签名/值对不匹配的元素。在集合理论中,这叫做补集。实际上,这个操作符的工作方式与and相反,这意味着它也可以用作if not语句。
node_filesystem_avail_bytes unless node_filesystem_avail_bytes < 2000000
4.聚合操作
通过使用聚合操作符,我们可以获取一个即时向量并聚合它的元素,从而得到一个新的即时向量,通常包含更少的元素。像这样的即时向量的每次聚合都以我们在垂直聚合中描述的方式工作。
有效的聚合操作如下:
操作符 | 描述 | 必须 |
---|---|---|
sum | 元素的和 | |
min | 选择最小的元素 | |
max | 选择最大的元素 | |
avg | 计算元素的平均值 | |
stdvar | 计算元素的标准方差 | |
count | 计算元素的数量 | |
count_values | 计算具有相同值的元素的数目 | |
bottomk | k以下的元素 | 请求使用一个(K)作为标尺 |
topq | k以上的元素 | 请求使用一个(K)作为标尺 |
quantile | 计算元素的分位数 | Requires the quantile (0<= x <= 1) definition as a scalar |
使用以下查询的样本数据作为演示:
rate(prometheus_http_requests_total[5m])
产生的结果如下:
{code="200", handler="/-/ready", instance="192.168.1.121:9090", job="prometheus"}
0
{code="200", handler="/api/v1/label/:name/values", instance="192.168.1.121:9090", job="prometheus"}
0
{code="200", handler="/api/v1/metadata", instance="192.168.1.121:9090", job="prometheus"}
0
{code="200", handler="/api/v1/query", instance="192.168.1.121:9090", job="prometheus"}
0.0034020804597701152
{code="200", handler="/graph", instance="192.168.1.121:9090", job="prometheus"}
0
{code="200", handler="/metrics", instance="192.168.1.121:9090", job="prometheus"}
0.19732066666666667
{code="400", handler="/api/v1/query", instance="192.168.1.121:9090", job="prometheus"}
0
如果我们像知道所有请求的总和,可以应用以下表达式:
sum(rate(prometheus_http_requests_total[5m])
返回的结果如下:
{} 0.2203397299651863
5.Binary操作优先级
当使用PromQL查询时,应用二进制操作符的顺序由操作符优先级决定。下表显示了从高到低的优先顺序
优先级 | 操作符 | 描述 |
---|---|---|
1 | ^ | Evaluater right to left,for example,123 is evaluated as 1 (23) |
2 | *,/,% | Evaluater left to right,for example,1/23 is evaluated as (1/2)3 |
3 | +,- | Evaluater left to right |
4 | ==,!=,<=,<,>=,> | Evaluater left to right |
5 | and,unless | Evaluater left to right |
6 | or | Evaluater left to right |
二、PromQL函数
1.函数
PromQL对于各种用例(比如math)有近50个不同的函数;排序;计数器、guage和直方图操作;标签转换;随着时间的推移聚合;类型转换;最后是日期和时间函数。在下面的福分中,我们将介绍一些最常用的发那个发,并提供一些示例,说明他们为什么如此相关。
1.1absent()
函数的作用是:获取一个瞬时向量作为参数,并返回以下内容:
如果传递给它的向量参数具有样本数据,则返回空向量;
如果传递的向量参数没有样本数据,则返回不带度量指标名称且带有标签的时间序列,且样本值为1。
1.当监控度量指标时,如果获取到的样本数据是空的,使用absent方法对告警是非常有用的。例如:
absent(prometheus_http_requests_total)
2.返回的结果:
no data
3.我们使用一个表达式与标签matcher使用不存在的标签值,就像下面的例子;
absent(prometheus_http_requests_total2)
{} 1
1.2 lable_join()和lable_replace()
这些函数用于操作标签----他们允许您将标签连接到其它标签上,提取标签值得一部分,甚至删除标签(尽管使用标准的聚合操作更容易、更符合人体工程学)。在这两个函数中,如果定义的目标标签是一个新的,它将被添加到标签集;如果它是一个现有的标签,它将被取代。
1.在使用label_join时,您需要提供一个即时向量,定义一个结果标签,识别结果连接的分隔符,并建立要连接的标签,如下面的语法所示:
label_join(<vector>,<resulting_label>,<sepatator>,source_label1,source_labelN)
比如,使用如下的案例:
http_requests_total{code="200",endpoint="het-port",handler="/",instance="192.168.1.121:8000",job="hey-service",method="get"} 1366
http_requests_total{code="200",endpoint="het-port",handler="/health",instance="192.168.1.121:8000",job="hey-service",method="get"} 942
应用如下表达式:
label_join(http_requests_total{instance="192.168.1.121:9090"},"url","","instance","handler")
我们得到如下的瞬时向量:
http_requests_total{code="200",endpoint="hey-port",handler="/",instance="192.168.1.121:8000",job="hey-service",method="get",url="192.168.1.121:8080/"} 1366
http_requests_total{code="200",endpoint="hey-port",handler="/health",instance="192.168.1.121:8000",job="hey-service",method="get",url="192.168.1.121/health"} 942
1.2 label_replace()
当需要对标签进行任意操作时,可以使用label_replace函数。它的工作方式时将正则表达式应用于所选源标签的值,并将匹配的捕获组存储在目标标签上。源和目标可以时同一个标签,有效地替换其值。这听起来很复杂,但实际上并不复杂;让我们来看看label_replace的语法:
label_replace(<vector>,<destination_label>,<regex_match_result>,<source_label>,<regex>)
假设我们使用前面的示例数据并应用下面的表达式:
label_replace(http_requests_total{instance="192.168.1.121:8000"},"port","$1","instance",".*:(.*)")
然后,结果将是与新标签(称为port)匹配的元素:
http_requests_total{code="200",endpoint="hey-port",handler="/",instance="192.168.1.121:8000",job="hey-service",method="get",port="8000"} 1366
http_requests_total{code="200",endpoint="hey-port",handler="/health",instance="192.168.1.121:8000",job="hey-service",method="get",port="8000"} 942
在使用label_replace时,如果正则表达式与标签值不匹配,则原始时间序列将不加更改地返回。
1.3 predict_linear()
predict_linear(v range-vector,t scalar)函数可以预测时间序列v在t秒后的值。它基于简单线性回归的方式,对时间窗口内的样本数据进行统计,从而可以对时间序列的变化趋势做出预测。该函数的返回结果不带有度量指标,只有标签列表。
例如,基于2小时的样本数据,来预测主机可用磁盘空间的是否在4小时后被占满,可以使用如下表达式:
predict_linear(node_filesystem_free_bytes{job="node"}[2h],4*3600) < 0
我们将应用下面的表达式,它使用一个1小时数据范围内的predict_linear,并推断未来4小时的样本值(60(秒)60(分钟)4):
predict_linear(node_filesystem_free_bytes{mountpoint="/run"}[1h], 60*60*4)
{device="tmpfs", fstype="tmpfs", instance="192.168.1.121:9100", job="node", mountpoint="/run"}
1.4 rate()和irate()
rate(v range-vector)函数可以直接计算区间向量v在时间窗口内平均增长速率,它会在单调性发生变化时(如由于采样目标重启引起的计数器复位)自动中断。该函数的返回结果不带有度量指标,只有标签列表。
例如,以下表达式返回区间向量中每个时间序列过去5分钟内HTTP请求数的每秒增长率:
rate(http_requests_total[5m])
结果:
{code="200",handler="label_values",instance="192.168.1.121:9090",job="prometheus",method="get"} 0
{code="200",handler="query_range",instance="192.168.1.121:9090",job="prometheus",method="get"} 0
{code="200",handler="prometheus",instance="192.168.1.121:9090",job="prometheus",method="get"} 0.2
irate(v range-vector) 函数用于计算区间向量的增长率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个样本数据来计算区间向量的增长速率,它会在单调性发生变化时(如由于采样目标重启引起的计数器复位)自动中断。这种方式可以避免在时间窗口范围内的”长尾问题“,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。
例如,以下表达式返回区间向量中每个时间序列过去5分钟内会后两个样本数据的HTTP请求数的增长率:
irate(http_requests_total{job="api-server"}[5m])
irate只能用于绘制快速变化的计数器,在长期趋势分析或者告警中更推荐使用rate函数。因为使用irate函数时,速度间断变化会重置FOR语句,形成的图形有很多波峰,难以阅读。
1.5 sort()和sort_desc()
sort接收一个向量并根据样本值按升序排序,而sort_desc执行相同的函数,但按降序排序。