# PromQL 快速入门(二)
# 一. PromQL 基础概念
# 1.1 什么是 PromeQL
Prometheus 内置了⼀种强⼤的查询语⾔:PromQL,即 PrometheusQuery Language。PromQL 允许⽤户实时查询监控数据,并对这些数据执⾏复杂的聚合和计算操作。
在 PromQL 中,查询的结果被称为 “向量(vector)”,分为两种类型:
1、即时向量(Instant vector):即时向量查询返回的是⼀组时间序列数据,但每个时间序列中只包含单个的最新数据点。例如:查询当前时刻服务器 1 分钟的负载,所得到的结果就是⼀个即时向量。
2、范围向量(Range vector):范围向量查询的结果包含了⼀个时间范围内的所有数据点。例如:查询过去 1 分钟内服务器负载的变化情况,返回的数据集就是⼀个范围向量。
在 PromQL 中,除了有向量类型的数据,还有其他的数据类型,具体有:
标量(Scalar):标量仅代表⼀个单⼀的浮点数值。可以将向量的某⼀个值通过 scalar () 函数转为标量,然后进⾏数值运算,但使⽤较少。
字符串(String):在 PromQL 查询结果中,每个时间序列都伴随着⼀组标签,这些标签和标签值,就是使⽤字符串类型来定义。这些标签为时间序列提供了元数据信息,例如 {job="nginx",instance="prom-node01.oldxu.net", method="get", url="/api"} ,通过标签能很好的对数据进⾏分类和识别。
# 1.2 PromQL 应⽤场景
Prometheus 的核⼼就是 PromQL,PromQL 在⽇常的数据可视化,查询、定义告警规则都会是⽤到,因此掌握 PromQL 基本上就掌握了 Prometheus;
1、临时查询:使⽤ PromQL,你可以实时地查询监控数据,这对于调试和诊断问题⾮常有帮助。通常,我们通过 Prometheus ⾃带的表达式浏览器来执⾏这些查询。
2、数据可视化,PromQL 可以帮助我们创建数据的可视化展示,这些可视化通常是通过集成⼯具如 Grafana 来实现的,可以将我们 PromQL 查询的结果展示得更直观。
3、监控告警:Prometheus 可以直接使⽤ PromQL 对指标的查询结果来设置告警。⼀旦查询结果满⾜指定的条件,就会触发告警,⼀个完整的报警规则如下所示:
⼀个告警规则包括了告警的名称、条件、持续时间等信息。
groups: | |
- name: 告警组 | |
rules: | |
- alert: 节点宕机 # 告警名称 | |
expr: up == 0 # 告警条件 | |
for: 1m # 持续时间 | |
labels: | |
severity: critical | |
annotations: # 定义邮件中收到的告警详细内容 | |
summary: "节点宕机警告 - 实例:" | |
description: "作业 的节点⽆法访问,已经持续超过1分钟。" |
# 二. PromQL 基础使⽤
# 2.1 指标查询
1、查询指标的最直接⽅式是输⼊ “指标的名称”。⽐如,你想知道系统的⼀分钟负载(node_load1)情况:
# 查询表达式 | |
node_load1{} | |
# 查询结果(结果展示了所有被监控节点的⼀分钟负载。) | |
node_load1{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 0 | |
node_load1{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 0.02 | |
node_load1{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 0.13 |
2、但是,在实际监控时,我们通常只需要关注 “特定节点” 的指标数据。例如,如果我只想查看 prom-node01.oldxu.net 这个节点的⼀分钟负载情况,那么就可需要是⽤ “标签匹配器” 来筛选出所需的指标。
# 查询表达式 | |
node_load1{instance="prom-node01.oldxu.net:9100"} | |
# 查询结果(由于指定了过滤条件,因此只会展示 node01 节点的 1 分钟负载) | |
node_load1{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 0.35 |
3、因此,标签匹配器就是通过标签和标签值,来筛选出我们所需要的指标数据。使⽤标签匹配器时,可以按照以下语法构造查询表达式:
<metric_name>{<label_name>=<label_value>, <label_name2>=~<regex>, ...} | |
其中各个部分的含义如下: | |
# = 表示⼀个标签必须严格等于⼀个给定的值。 | |
# != 表示排除等于特定值的标签。 | |
# =~ 表示标签的值必须匹配⼀个正则表达式。 | |
# !~ 表示标签的值不应匹配⼀个正则表达式。 |
实例 1:查询所有实例,CPU 的第 0 个核⼼,中的 user ⽤户空间所占⽤ CPU 的时间,指标名称:node_cpu_seconds_total
node_cpu_seconds_total{cpu="0",mode="user"} |
实例 2:查询所有实例,eth0 ⽹卡发送总⼤⼩,指标名称:node_network_transmit_bytes_total
node_network_transmit_bytes_total{device="eth0"} | |
# 转为 GB | |
node_network_transmit_bytes_total{device="eth0"} / 1024 /1024 /1024 |
实例 3:查询所有实例的⽹卡接收字节数,排除 lo 接⼝,指标名称:node_network_receive_bytes_total
node_network_receive_bytes_total{device!="lo",device!="docker0"} | |
node_network_receive_bytes_total{device!~"lo|docker0"} | |
# 转为 GB | |
node_network_receive_bytes_total{device!="lo",device!="docker0"} /1024 /102 | |
4 /1024 |
实例 4:查询:挂载点以 /run 开头的⽂件系统可⽤字节数,指标名称 node_filesystem_avail_bytes
node_filesystem_avail_bytes{mountpoint=~"^/run.*"} | |
# 字节转 MB | |
node_filesystem_avail_bytes{mountpoint=~"^/run.*"} /1024/1024 |
实例 5:块设备名字不包含 vda 和 sr0 的读字节数,指标名称 node_disk_read_bytes_total
node_disk_read_bytes_total{device!~".*sr0.*"} |
# 2.2 时间范围查询
在 Prometheus 中,范围向量选择器使我们能够提取时间序列中⼀段时间范围内的数据点。要定义⼀个范围向量选择器,你只需要在指标名称后⾯加上⽅括号 [],并在其中指定⼀个时间⻓度。时间⻓度有:s 秒,m 分钟,h ⼩时,d 天,w 周,y 年。使⽤范围向量选择器,你可以灵活查询从最近⼏分钟到⼏年的数据,便于分析和监控指标随时间的变化。
1、例如,我们想查询 prom-node01.oldxu.net 这个实例,在过去 2 分钟,负载所有数据点。
node_load1{instance="prom-node01.oldxu.net:9100"}[2m] | |
node_load1{instance="prom-node01.oldxu.net:9100", job="node_exporter"} | |
0 @1751165619.66 | |
0 @1751165634.66 | |
0 @1751165649.66 | |
0 @1751165664.66 | |
0 @1751165679.66 | |
0 @1751165694.66 | |
0 @1751165709.66 | |
0 @1751165724.66 |
2、在 Prometheus 中,通常使⽤相对时间(如过去⼏分钟、⼩时或天)来查询数据。但是,如果你需要查询⼀个绝对时间点的数据,您可以直接使⽤ UNIX 时间戳来指定查询的具体时间。(在 Prometheus 中通常是以毫秒为单位的)
例如,查询 2025 年 6 ⽉ 29 ⽇上午 10:58:10 的 prom-node01.oldxu.net 实例的负载状态。需要先将时间转为 UNIX 时间戳,然后执⾏如下查询语句。
#指定时间查询表达式 | |
node_load1{instance="prom-node01.oldxu.net:9100"} @ 1751165890 | |
# 查询结果 | |
node_load1{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 0.27 |
# 2.3 时间偏移查询
除了能够查询过去⼏分钟或⼏⼩时的数据,以及查询指定时间点的数据,同时 Prometheus 也允许您通过 offset 修饰符来指定查询从当前时间往回推某个具体的时间段。这种⽅式常⽤于⽐较,例如:今天 QPS 是 10000,昨天这个时间是 5000,我们就可以计算它们的增⻓率之类的。
1、如果您想要查看昨天整天的数据,可以使⽤以下查询表达式:
# 假设是⽤如下表达式查询的结果是 2023-12-26 17:20 | |
node_load1 | |
# 那么如下表达式查询的数据将返回:(2023-12-24 17:20 - 2023-12-25 17:20) | |
node_load1[1d] offset 1d |
2、如果想要查看⼀周前的 “时间范围内” 的数据,可以使⽤如下查询:
# 查询表达式 | |
node_load1[5m] offset 1w |
这个查询会返回当前时间往回推⼀周,并从那个时间点开始,持续 5 分钟的数据范围内的 node_load1 指标。例如,如果现在是 12 ⽉ 26 ⽇星期⼀下午 17:30,那么该查询将返回 12 ⽉ 19 ⽇星期⼀下午 17:25 到 17:30 这 5 分钟范围内的所有数据点。
3、对⽐分析实战:对⽐ “当前时间点接收的⽹络流量” 与 “1 ⼩时前的流量” 进⾏⽐对分析,以判断流量是增⻓还是减少。
- 计算同环⽐的增⻓率或减少率的公式:同环⽐率 =(当前流量−过去 1 ⼩时的流量) / 过去 1 ⼩时的流量 ×100
- 例如,如果当前总接收流量是 6000MB,⽽ 1 ⼩时前接收的总流量是 5000MB,
- 那么计算公式为: (6000 - 5000) / 5000 * 100 = 20% (意味着相⽐ 1 ⼩时前,当前流量增⻓了 20%)
# 表达式 | |
(node_network_receive_bytes_total{device="eth0"} - node_network_receive_bytes_total{device="eth0"} offset 1h) / node_network_receive_bytes_total{device="eth0"} offset 1h * 100 | |
# 结果 | |
{device="eth0", instance="prom-node01.oldxu.net:9100", job="node_exporter"} -87.68874820331817 # 负增⻓ 80% | |
{device="eth0", instance="prom-node02.oldxu.net:9100", job="node_exporter"} 4231.893284128109 # 增⻓ 4231% | |
{device="eth0", instance="prom-node03.oldxu.net:9100", job="node_exporter"} 24.658781604740295 # 增⻓ 24% |
# 三. PromQL 常⽤函数
# 3.1 Count 类型常⽤函数
前⾯我们讲过,Counter 类型的监控指标只增不减,因此其样本值应该是不断增⼤的。因此单纯的 Counter 总数并没有直接作⽤,⽽是需要借助于 rate、irate、increase 和等函数来计算样本数据的变化状况(增⻓率);
1、rate ⽤于计算平均增⻓速率:计算公式:通过指定时间范围内的样本,使⽤最后⼀个样本的值减去第⼀个样本的值,⽽后除以这两个样本之间的间隔时⻓。
例如: rate (nginx_http_requests_total [1m]) ,表示要获取 1 分钟内,该指标上的 http 总请求数的平均增⻓速率;
2、irate ⽤于计算瞬时的增⻓速率(灵敏度较⾼):计算公式:通过指定时间范围内的样本最后⼀个样本的值减去前⼀个样本的值,⽽后除以这两个样本之间的间隔时⻓。
例如: irate (nginx_http_requests_total [1m]) ,表示要获取 1 分钟内,该指标上的 http 总请求数的瞬时增⻓速率;
3、increase ⽤于计算指定时间范围内样本值的增加量:计算公式:通过指定时间范围内的样本最后⼀个样本的值减去第⼀个样本的值。注意:increase 可能会引⽤时间范围边界之前的样本值,以便于计算能覆盖到指定的整个时间范围。
例如: increase (nginx_http_requests_total [1m]) ,表示要获取 1 分钟内,http 请求的增量;
# 3.2 Gauge 类型常⽤函数
Gauge 类型的指标,存储的值是随着时间会变发⽣变化的,它常⽤求和、取平均值、最⼩值、最⼤值等;也会结合 PromQL 的 predict_linear 和 delta 函数使⽤;
1、predict_linear (v range-vector, t, scalar):预测时间序列 v 在 t 秒后的值,它通过线性回归的⽅式来预测样本数据的变化趋势;
例如: predict_linear (node_filesystem_avail_bytes [4h], 60*60*24*30) ,使⽤过去 4 ⼩时的数据来预测接下来 30 天(60*60*24*30)
的磁盘空间趋势。
predict_linear(node_filesystem_avail_bytes{mountpoint="/"}[4h], 60*60*24*30) /1024 /1024 /1024 |
2、delta (v range-vector):计算范围向量中每个时间序列上的第⼀个样本值与最后⼀个样本值之差;其计算结果与 increase 函数相同;但 delta 更适⽤于没有重置的场景,或者⽤来监控那些可能上升或下降的指标,例如温度、磁盘空间等。
例如: delta (cpu_temp_celsius {host="prom01.oldxu.net"}[2h]) ,返回该服务器上的 CPU 温度与 2 ⼩时之前的差异;
delta(cpu_temp_celsius{host="prom01.oldxu.net"}[2h]) |
例如: delta (node_filesystem_avail_bytes [10m]) /1024 /1024 ,返回服务器上磁盘可⽤空间与 10 分钟之前的差异;
delta(node_filesystem_avail_bytes[10m]) /1024 /1024 | |
#创建大文件 | |
[root@prom-node01 ~]# dd if=/dev/zero of=/bigdata count=1 bs=200M |
3、changes () :计算监控时间范围内某个时间序列的数据变化的次数。它只关⼼变化的次数,⽽不关⼼具体变化的值是什么。
例如: changes (nginx_up [10m]) 监控 Nginx 服务在给定时间内变化的次数,如果停⽌了变化次数 + 1,启动了变化次数 + 1。
changes(up[10m]) | |
#模式测试 | |
[root@prom-node02 ~]# systemctl stop node_exporter | |
[root@prom-node02 ~]# systemctl start node_exporter | |
{instance="prom-node01.oldxu.net:3000", job="grafana"} 0 | |
{instance="prom-node01.oldxu.net:9090", job="prometheus"} 0 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 0 | |
{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 2 | |
{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 0 |
# 四. PromQL ⼆元运算符
PromQL 提供了⼀系列⼆元运算符,包括算术运算 (+ - * /)、⽐较运算 ( == <= >=)、以及集合运算 ( and or unless)。在 PromQL 中,⽤户可以执⾏以下类型的运算:
1、标量与标量之间的运算
2、即时向量与标量之间的运算
3、两个即时向量之间的运算。(当涉及到两个即时向量的运算时标签必须一致,PromQL 遵循向量匹配机制(Vector Matching),定义其运算逻辑)
# 4.1 算术运算符介绍
在 PromQL 中算术运算符,是⽤来对指标数据执⾏基本的数学运算。⽀持的运算符有:+(加)、-(减)、*(乘)、/(除)、%(取模)和 ^(幂运算)
1、标量与标量之间进⾏数学运算,其最终得到的也是标量(使⽤较少)
# 标量与标量算数运算表达式 | |
5 + 5 | |
# 结果 | |
scalar 10 |
2、即时向量与标量进⾏运算,例如将 node_memory_MemTotal_bytes (节点内存总⼤⼩)的默认 bytes 单位转为 MB
# 即时向量与标量算数运算表达式 | |
node_memory_MemTotal_bytes / 1024 /1024 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 1970.05859375 | |
{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 1970.05859375 | |
{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 1970.05859375 |
3、即时向量与即时向量进⾏运算,它们需要遵循向量的匹配逻辑,也就是向量与向量的标签必须完全匹配⼀致才可以进⾏运算,如果它们的标签不⼀致,则不会执⾏这个运算。例如:我们想计算内存的可⽤百分⽐,计算公式为 “(内存可⽤空间 / 内存总空间 * 100)= 内存可⽤百分⽐” 这两个向量的标签是完全⼀致的,因此可以直接进⾏运算,否则⽆法正常进⾏运算,除⾮进⾏向量匹配特殊的处理。
# 即时向量与即时向量算数运算表达式 | |
node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 52.0608325815 | |
18234 # 可⽤内存还有 52% | |
{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 70.7339367682 | |
1954 # 可⽤内存还有 70% | |
{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 41.0098446469 | |
1128 # 可⽤内存还有 41% |
# 4.2 算术运算符实践
实例 1:计算所有实例节点的 eth0 ⽹卡,接收的总流量和发送的总流量之和(以 GB 显示)
- node_network_receive_bytes_total :节点⽹络接收的总⼤⼩(以字节为单位)
- node_network_transmit_bytes_total :节点⽹络发送的总⼤⼩(以字节为单位)
# 表达式( 计算公式:(eth0 接收的总流量 + eth0 发送的总流量) /1024 / 1024 /1024 ) | |
(node_network_receive_bytes_total{device="eth0"} + node_network_transmit_bytes_total{device="eth0"}) /1024 /1024 /1024 | |
# 结果 | |
{device="eth0", instance="prom-node01.oldxu.net:9100", job="node_exporter"} 65.10437232814729 # 65GB | |
{device="eth0", instance="prom-node02.oldxu.net:9100", job="node_exporter"} 19.22064190544188 | |
{device="eth0", instance="prom-node03.oldxu.net:9100", job="node_exporter"} 35.516745982691646 |
实例 2:计算所有实例节点的 / 分区 已经使⽤了,多少空间(以 GB 显示)
- node_filesystem_size_bytes :⽂件系统总⼤⼩(以字节为单位)
- node_filesystem_avail_bytes :⽂件系统可⽤空间(以字节为单位)
# 表达式( 计算公式:(总空间 - 剩余空间) /1024 / 1024 /1024 ) | |
(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) /1024 /1024 /1024 | |
# 结果 | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node01.oldxu.net:9100", job="node_exporter", mountpoint="/"} 7.806545257568359 # 已使⽤ 7.8G | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node02.oldxu.net:9100", job="node_exporter", mountpoint="/"} 3.9103240966796875 # 已使⽤ 3.9G | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node03.oldxu.net:9100", job="node_exporter", mountpoint="/"} 5.496803283691406 # 已使⽤ 5.4G |
实例 3:计算所有实例节点的 / 分区 "已⽤空间百分⽐"
# 表达式( 计算公式:(总空间 - 可⽤的空间) / 总空间 * 100 ) | |
(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) / node_filesystem_size_bytes{mountpoint="/"} * 100 | |
# 结果 | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node01.oldxu.net:9100", job="node_exporter", mountpoint="/"} 3.965885742336228 # 已⽤空间占⽐百分之 3.96G | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node02.oldxu.net:9100", job="node_exporter", mountpoint="/"} 1.9864746546908878 # 已⽤空间占⽐百分之 1.98G | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node03.oldxu.net:9100", job="node_exporter", mountpoint="/"} 2.792465443218097 # 已⽤空间占⽐百分之 2.79G |
实例 4:计算所有节点内存的 “已⽤百分⽐”
- node_memory_MemTotal_bytes :总内存⼤⼩(单位字节)
- node_memory_MemAvailable_bytes :内存可⽤⼤⼩(单位字节)
# 表达式( 计算公式:(总内存 - 可⽤内存) / 总内存 * 100 ) | |
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 48.12356865972023 # 已⽤ 48% 内存 | |
{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 29.767713920310907 # 已⽤ 29% 内存 | |
{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 58.8624624505537 # 已⽤ 58% 内存 |
# 4.3 ⽐较运算符介绍
在 PromQL 中⽐较运算符,是⽤来对指标的数据进⾏条件判断,⼀般在告警规则中定义何时应该触发告警。PromQL ⽀持的⽐较运算符有如下⼏个:
- == :等于,当两边的数值相等时为真。
- != :不等于,当两边的数值不相等时为真。
- > :⼤于,当左边的数值⼤于右边时为真。
- < :⼩于,当左边的数值⼩于右边时为真。
- >= :⼤于等于,当左边的数值⼤于或等于右边时为真。
- <= :⼩于等于,当左边的数值⼩于或等于右边时为真。
在 PromQL 中,使⽤⽐较运算符时,默认情况下,如果⽐较结果为假(即条件不满⾜),则相关的时间序列不会出现在结果中。但是,如果在测试时,想要明确地看到哪些时间序列满⾜条件(为真)和哪些不满⾜(为假),可以使⽤ bool 修饰符,这个修饰符会将所有的时间序列都显示在结果中,满⾜条件的序列会有⼀个值为 1(true),不满⾜的序列会有⼀个值为 0(false)。
1、标量与标量之间进⾏⽐较运算
# 标量与标量⽐较表达式 | |
5 == bool 5 | |
# 结果 | |
scalar 1 # 说明条件成⽴ |
2、即时向量与标量进⾏⽐较运算,例如判断服务器 1 分钟的负载,是否有⼤于 0 以上的节点
# 即时向量与标量进⾏⽐较表达式 | |
node_load1 > 0 | |
# 结果 | |
node_load1{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 0.01 | |
node_load1{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 0.01 |
3、即时向量与即时向量进⾏⽐较运算,它们需要遵循向量的匹配逻辑,也就是向量与向量的标签必须完全匹配⼀致才可以进⾏运算,如果它们的标签不⼀致,则不会执⾏这个运算。例如:我们可以⽐较 “可⽤内存” 是否⼤于 “空闲内存”,如果满⾜该条件,那么会显示左侧的指标名称和指标当前的值。
# 即时向量与即时向量⽐较表达式 | |
node_memory_MemAvailable_bytes > node_memory_MemFree_bytes | |
# 结果 | |
node_memory_MemAvailable_bytes{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 1092820992 | |
node_memory_MemAvailable_bytes{instance="prom-node02.oldxu.net:9100", job="node_exporter"} 1456451584 | |
node_memory_MemAvailable_bytes{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 867491840 |
# 4.4 ⽐较运算符实践
实例 1:查询 node_exporter 这个 job 中,⽬前不存活实例有哪些(1 为存活、0 为不存活)。
# 表达式 | |
up{job="node_exporter"} != 1 | |
# 结果 | |
up{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 0 # 0 表 | |
示该节点⽬前不存活 |
实例 2:查询所有实例 “已使⽤内存” 超过 30%
# 表达式:(计算公式:(总内存 - 可⽤内存)/ 总内存 * 100 ⼤于 30 ) | |
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes * 100 > 30 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter"} 48.04405801699267 # 该节点已使⽤内存达到 48% | |
{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 58.090753170015965 # 该节点已使⽤内存达到 58% |
实例 3:查询所有的实例 “磁盘可⽤空间” 不⾜ 30% 的实例
# 表达式:(计算公式:(可⽤空间 / 总磁盘空间) * 100 ⼩于 30 ) | |
node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 < 30 | |
# 结果 | |
{device="/dev/mapper/rl-root", fstype="xfs", instance="prom-node01.oldxu.net:9100", job="node_exporter", mountpoint="/"} 26.02939746145283 # 剩余空间仅剩 26%,意味着磁盘空间快⽤完了 |
实例 4:查询所有实例中 eth0 设备的⽹络带宽,每秒发送速率超过 200Mb/s 兆 的实例
# 1、在两个测试的节点上安装:yum install iperf -y | |
# 2、服务端运⾏并指定端⼝:iperf -s -p 9999 | |
# 3、客户端模拟带宽发送命令,-b 指定发送⼤⼩,-t 指定发送持续时⻓:iperf -c <ip> -p 9999 -b 300M -t 60 | |
# 表达式(计算公式:rate (总的传输 [1m]) * 8 /1024/1024) > 200) ⽹络速度或带宽通常以位每秒(如 Mbps, Gbps)为单位。因此需要将字节乘以 8,能够将字节转换为位,这样可以更准确地描述传输速率 | |
rate(node_network_transmit_bytes_total{device="eth0"}[1m]) * 8 /1024 /1024 > 200 | |
# 结果 | |
{device="eth0", instance="prom-node03.oldxu.net:9100", job="node_exporter"} 286.0319371541341 # 该节点每秒达到了 286Mb/s 的带宽(如果不乘以 8 则单位是 MB/s) |
实例 5:查询所有 https 的域名,检查域名证书的过期时间,将还剩不到 90 天的域名列出来(需要借助后⾯的 blackbox ⿊盒监控,才能获取到对应的指标)
# 表达式,计算公式( (过期时间 - 当前时间) / 天 (24*60*60) ) | |
(probe_ssl_earliest_cert_expiry - time() ) / 86400 < 90 | |
# 结果 | |
{instance="http://hmallleasing.com", job="blackbox_http"} 43.74673421296256 # 还剩 43 天过期 | |
{instance="https://ixuyong.cn", job="blackbox_http"} 87.74673421296256 # 还剩 87 天过期 |
# 4.5 集合运算符介绍
在 Prometheus 的查询语⾔中,集合运算符主要⽤到的运算符包括 and(并且)、or(或者)和 unless(排除)
例如:我们有两个关键指标:backup_duration_seconds ⽤于记录每次备份操作的持续时间,⽽ backup_success 则指示备份操作是否成功(1 表示成功,0 表示失败)
场景 1:备份成功但时间超过 9s
当备份操作成功完成(backup_success == 1),并且执⾏时间超过 9 秒(backup_duration_seconds > 9)时,我们需要发出告警通知 “备份成功但备份时间过⻓”。这就需要使⽤ and 运算符来确保只有当这两个条件都满⾜时,才触发告警。对应的表达式为:
backup_duration_seconds > 9 and backup_success == 1 |
场景 2:备份失败或时间超过 9s
如果备份时⻓超过了 9 秒(backup_duration_seconds > 9),或者备份操作失败(backup_success == 0),则同样需要发出告警通知 “备份失败或时间过⻓”。在这种情况下,是⽤ or 运算符可以帮助我们在任⼀条件满⾜时触发告警。对应的表达式为:
backup_duration_seconds > 9 or backup_success == 0 |
场景 3:查询成功的备份,但排除耗时超过 9s
查询所有成功的备份任务,同时排除那些执⾏时间超过 9 秒的任务,这样我们就可以只关注于那些成功备份的任务,并且备份效率较⾼的。我们可以利⽤ unless 运算符来实现,对应的表达式为:
backup_success == 1 unless backup_duration_seconds > 9 |
1、and 运算符示例,查询当前实例 “1 分钟负载⼤于 2,并且 5 分钟负载⼩于
2、如果满⾜条件说明当前发了了突增的负载压⼒。注意:and 运算需要遵循向量的匹配逻辑,也就是向量与向量的标签必须完全匹配⼀致才可以进⾏匹配,如果它们的标签不⼀致,则不会执⾏匹配逻辑,除⾮使⽤ ignore 忽略不⼀致的标签来进⾏匹配。
# 模拟负载⾼命令 stress --cpu 8 --timeout 60 | |
# 表达式 | |
node_load1 > 2 and | |
node_load5 < 2 | |
# 结果 | |
node_load1{instance="prom-node03.oldxu.net:9100", job="node_exporter"} 4.19 # 说明当前 node03 节点 1 分钟负载⽐较⾼,⽽ 5 分钟负载并不⾼ |
2、or 示例,查询 prom-node01.oldxu.net:9100 上 CPU 编号为 0 的 idle 时间或 user 时间
# 表达式 | |
node_cpu_seconds_total{instance="prom-node01.oldxu.net:9100",mode="idle",cpu="0"} or | |
node_cpu_seconds_total{instance="prom-node01.oldxu.net:9100",mode="user",cpu="0"} | |
# 结果 | |
node_cpu_seconds_total{cpu="0", instance="prom-node01.oldxu.net:9100", job="node_exporter", mode="idle"} 6429449.35 | |
node_cpu_seconds_total{cpu="0", instance="prom-node01.oldxu.net:9100", job="node_exporter", mode="user"} 26207.44 |
3、unless 示例,查询 node_cpu_seconds_total 指标上 CPU 编号为 0 的,但要排除 node02 和 node03 节点,同时还要排除模式为 idle|user|system|steal|nice
# 表达式 | |
node_cpu_seconds_total{cpu="0"} unless | |
node_cpu_seconds_total{instance="prom-node02.oldxu.net:9100"} unless | |
node_cpu_seconds_total{instance="prom-node03.oldxu.net:9100"} unless | |
node_cpu_seconds_total{mode=~"idle|user|system|steal|nice"} | |
# 结果 | |
node_cpu_seconds_total{cpu="0", instance="prom-node01.oldxu.net:9100", job="node_exporter", mode="iowait"} 3243.97 | |
node_cpu_seconds_total{cpu="0", instance="prom-node01.oldxu.net:9100", job="node_exporter", mode="irq"} 7236.66 | |
node_cpu_seconds_total{cpu="0", instance="prom-node01.oldxu.net:9100", job="node_exporter", mode="softirq"} 4117.61 |
# 4.6 集合运算符实践
实例 1:查询实例的⽹络接收流量 “并且” ⽹络发送流量,每秒传输超过 200Mb/s
#模拟接收和发送流量⽐较⾼:"需要在同⼀节点" 模拟服务端和客户端,执⾏如下命令 | |
# 1、模拟服务端:iperf -s -p 9999 | |
# 2、模拟客户端:iperf -c prom-node01.oldxu.net -p 9999 -b 300M -t 60 | |
# 表达式 | |
rate(node_network_receive_bytes_total[1m]) *8 /1024 /1024 >200 and | |
rate(node_network_transmit_bytes_total[1m]) *8 /1024 /1024 >200 | |
# 结果 | |
{device="lo", instance="prom-node01.oldxu.net:9100", job="node_exporter"} 300.4421537611219 # 这个实例发送和接收同时达到了 300Mb/s |
实例 2:查询当前磁盘,可⽤空间不⾜ 20GB “或者” 当前磁盘可⽤空间不⾜ 30%
# 表达式,计算公式: 可⽤空间 / 1024/1024/1024 <= 20 OR (可⽤空间 / 总磁盘空间) *100 < 30 | |
node_filesystem_avail_bytes /1024 /1024 /1024 <=20 or | |
( node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100 < 30 | |
# 结果 |
实例 3:通过 probe_http_status_code 指标获取当前监控的⽹站返回的状态码,并从中筛选出⼩于 200 的状态码 “或者” ⼤于 400 的状态码
# 表达式 | |
probe_http_status_code <= 199 or | |
probe_http_status_code >= 400 | |
# 结果 | |
probe_http_status_code{instance="https://httpstat.us/102", job="blackbox_http"} 0 | |
probe_http_status_code{instance="http://httpbin.org/status/400", job="blackbox_http"} 400 | |
probe_http_status_code{instance="https://httpstat.us/421", job="blackbox_http"} 421 | |
probe_http_status_code{instance="https://httpstat.us/500", job="blackbox_http"} 500 | |
probe_http_status_code{instance="https://httpstat.us/502", job="blackbox_http"} 502 |
# 五. PromQL 聚合操作
# 5.1 PromQL 聚合介绍
聚合运算,是数据处理中的⽐较常⻅操作,例如统计公司所有⼈员的年龄,求公司整体的平均年龄,最⼤年龄,或最⼩年龄等。因此聚合操作它是从⼀组数据值中,计算出⼀个单⼀的值。
Prometheus 的聚合操作与此前刚才所描述的常规聚合在本质上是相似的,只不过它⽀持多种聚合运算函数,包括:
max :计算⼀组时间序列中的最⼤值。
min :计算⼀组时间序列中的最⼩值。
avg :计算时间序列的平均值。
sum :计算时间序列值的总和。
count :它不考虑时间序列的具体值,仅⽤来统计时间序列的数量。例如统计不同 OS 的数量,或者统计有多少个正在运⾏的 Pod 等等。
count_vaules :对每个样本的值进⾏数量统计,例如:http 请求的状
态码,200 出现了多少次,404 出现了多少次,500 出现了多少次;
topk :
bottomk :
除了这些基本聚合功能外,Prometheus 也提供了分组聚合的功能,它是基于时间序列的标签进⾏分组聚合:
- by :通过 by 关键字,明确指定保留哪些标签进⾏聚合,其他的标签将被忽略。
- without :与 by 相反, without 关键字⽤于指定要排除的标签,⽽剩下的标签则⽤于聚合和分组。
# 5.2 PromQL 聚合示例
为了加深 PromQL 聚合操作的理解,我们使⽤前⾯提到的城市天⽓温度数据,并通过聚合操作来展示如何获取整体的最⾼温度、最低温度以及按城市维度进⾏分组求取平均温度等。
1、下载并运⾏程序,提供天⽓温度相关的指标数据
[root@prom-node01 ~]# wget http://file.oldxu.net/prometheus/exporter/weather_exporter_oldxu | |
[root@prom-node01 ~]# mv weather_exporter_oldxu /usr/local/bin/ | |
[root@prom-node01 ~]# chmod +x /usr/local/bin/weather_exporter_oldxu | |
# 启动脚本 | |
[root@prom-node01 ~]# vim /usr/lib/systemd/system/weather_exporter.service | |
[Unit] | |
Description=weather_exporter | |
Documentation=https://prometheus.io/ | |
After=network.target | |
[Service] | |
ExecStart=/usr/local/bin/weather_exporter_oldxu --port 7001 | |
ExecReload=/bin/kill -HUP | |
TimeoutStopSec=20s | |
Restart=always | |
[Install] | |
WantedBy=multi-user.target | |
[root@prom-node01 ~]# systemctl daemon-reload | |
[root@prom-node01 ~]# systemctl start weather_exporter.service |
2、编辑 Prometheus 配置⽂件,抓取对应的指标数据
[root@prom-node01 ~]# vim /etc/prometheus/prometheus.yml | |
- job_name: "weather-exporter" | |
scrape_interval: 1m # 抓取指标频率 | |
static_configs: | |
- targets: ["prom-node01.oldxu.net:7001"] | |
# 重新加载 prometheus | |
[root@prom-node01 ~]# curl -X POST http://localhost:9090/-/reload |
示例 1:获取所有城市的整体温度总和
# 表达式 | |
sum(weather_oldxu) | |
# 结果 | |
{} 210 |
示例 2:分别展示不同城市的最⼤温度
# 表达式 | |
max(weather_oldxu) by (city) | |
# 结果 | |
{city="上海"} 30 | |
{city="北京"} 25 | |
{city="⼴州"} 29 | |
{city="武汉"} 2 |
示例 3:分别展示不同城市的最⼩温度
# 表达式 | |
min(weather_oldxu) by (city) | |
# 结果 | |
{city="上海"} 12 | |
{city="北京"} 3 | |
{city="⼴州"} 11 | |
{city="武汉"} -3 |
示例 4:仅展示 “武汉” 城市的平均温度
# 表达式 | |
avg(weather_oldxu{city="武汉"}) | |
# 结果 | |
{} 22.666666666666664 |
示例 5:是⽤ topk 获取前三个的⾼温城市。topk 的结果按温度值从⾼到低排序
# 表达式 | |
topk(3,weather_oldxu) | |
# 结果 | |
weather_oldxu{city="上海", dist="浦东新区", instance="prom-node01.oldxu.net:7001", job="weather-exporter"} 27 | |
weather_oldxu{city="⼴州", dist="越秀区", instance="prom-node01.oldxu.net:7001", job="weather-exporter"} 15 | |
weather_oldxu{city="⼴州", dist="天河区", instance="prom-node01.oldxu.net:7001", job="weather-exporter"} 14 |
示例 6:使⽤ bottomk 获取排名靠前三的低温城市。bottomk 的结果按温度值从低到⾼排列
# 表达式 | |
bottomk(3,weather_oldxu) | |
# 结果 | |
weather_oldxu{city="武汉", dist="武昌区", instance="prom-node01.oldxu.net:7001", job="weather-exporter"} -6 | |
weather_oldxu{city="武汉", dist="汉⼝区", instance="prom-node01.oldxu.net:7001", job="weather-exporter"} -5 | |
weather_oldxu{city="北京", dist="海淀区", instance="prom-node01.oldxu.net:7001", job="weather-exporter"} 2 |
示例 7:统计天⽓温度的数量,按城市进⾏区分
# 表达式 | |
count(weather_oldxu) by (city) | |
# 结果 | |
{city="上海"} 3 | |
{city="北京"} 3 | |
{city="⼴州"} 3 | |
{city="武汉"} 3 |
示例 8:统计各温度值出现的频次,按城市进⾏区分
# 表达式 count_values ("value_count", <metrics_name>) by (<label>) | |
count_values("status",weather_oldxu) by (city) | |
# 结果 | |
{city="上海", status="12"} 1 | |
{city="上海", status="13"} 1 | |
{city="上海", status="14"} 1 | |
{city="北京", status="-3"} 1 | |
{city="北京", status="6"} 1 | |
{city="北京", status="-10"} 1 | |
{city="⼴州", status="20"} 1 | |
{city="⼴州", status="24"} 1 | |
{city="⼴州", status="17"} 1 | |
{city="武汉", status="15"} 1 | |
{city="武汉", status="17"} 2 # 17 度出现了 2 次,这意味着武汉城市有 2 个不同地域报告了 17 度的温度。 |
# 5.3 PromQL 聚合实践
实例 1:查询所有节点,最近 1 分钟的负载,是否⾼于 cpu 核⼼的 2 倍
# 模拟 cpu 使⽤率⾼于核⼼数:stress --cpu 6 | |
# 获取每个节点的负载表达式 | |
sum(node_load1) by (instance) | |
# CPU 核⼼的 2 倍表达式 [核⼼数 * 2] | |
count(node_cpu_seconds_total{mode="idle"}) by (instance) * 2 | |
# 整体表达式 | |
sum(node_load1) by (instance) > count(node_cpu_seconds_total{mode="idle"}) by (instance) * 2 | |
# 结果 | |
{instance="prom-node02.oldxu.net:9100"} 4.06 # 该节点的使⽤率超过核⼼数的 2 倍了 |
实例 2:查询每个节点的 CPU 的使⽤率,指标名称: node_cpu_seconds_total
# 模拟 cpu 使⽤率达到 50%:stress --cpu 1 | |
# 表达式: (1 - CPU 整体空闲使⽤率) * 100 = CPU 使⽤率 | |
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[1m])) by (instance)) *100 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100"} 1.1555555555484487 | |
{instance="prom-node02.oldxu.net:9100"} 51.144444444459324 # 使⽤率在 50% 左右 | |
{instance="prom-node03.oldxu.net:9100"} 2.3333333333236395 |
实例 3:查询所有节点,最近 1 分钟磁盘的最⼤写⼊速率,以 MB/s 为单位,指标名称: node_disk_written_bytes_total
# 模拟数据写⼊,复制 2G 的数据,控制每秒 20M 左右的速度写 | |
# yum install pv -y | |
# dd if=/dev/zero bs=1M count=2000 | pv -L 20M > /tmp/bigdata | |
# 表达式: 获取每分钟的磁盘速率,然后提取最⼤的值,最后 / 1024/1024 得到 MB/s | |
max(rate(node_disk_written_bytes_total[1m])) by (instance) /1024 /1024 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100"} 0.007052951388888888 | |
{instance="prom-node02.oldxu.net:9100"} 19.724283854166664 #每秒写⼊速度在 19MB/s | |
{instance="prom-node03.oldxu.net:9100"} 0.011946614583333333 |
实例 4:查询所有节点,最近 1 分钟磁盘的读取写⼊速率,以 MB/s 为单位,指标名称: node_disk_read_bytes_total
# 模拟数据读取,读取 /tmp/bigdata ⽂件,然后以每秒 15MB 的速度读取 | |
# yum install pv -y | |
# pv -L 15M /tmp/bigdata > /dev/null | |
# 表达式 | |
max(rate(node_disk_read_bytes_total[1m])) by (instance) /1024 /1024 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100"} 0 | |
{instance="prom-node02.oldxu.net:9100"} 15.006076388888888 #每秒读取速度在 15MB/s | |
{instance="prom-node03.oldxu.net:9100"} 0 |
实例 5:计算 Prometheus 服务器的 HTTP 请求成功率,指标名称: prometheus_http_requests_total
# 计算公式: 请求成功的 (2xx|3xx) / 总的请求 * 100 = 请求成功率 | |
# 表达式 | |
sum (prometheus_http_requests_total{code=~"2.*|3.*"}) / sum(prometheus_http_requests_total) * 100 | |
# 结果 | |
{} 99.59027974003956 # ⽹站整体请求成功率在 99.5% |
实例 6:查询请求排名前三的 URL,指标名称: prometheus_http_requests_total
# 表达式 | |
sum (topk(3,prometheus_http_requests_total) ) by (instance,handler) | |
# 结果 | |
{handler="/metrics", instance="prom-node01.oldxu.net:9090"} 5483 | |
{handler="/api/v1/query", instance="prom-node01.oldxu.net:9090"} 1576 | |
{handler="/api/v1/metadata", instance="prom-node01.oldxu.net:9090"} 113 |
# 六. PromQL 时间聚合操作
# 6.1 PromQL 时间聚合介绍
在 Prometheus 中,除了可以 “纵向的聚合” 以外,还可以基于时间聚合也就是 “横向聚合”。
时间聚合不是在不同的序列上进⾏聚合操作,⽽是在 “单个序列” 的不同时间点之间进⾏聚合,这意味着,对于单个序列,我们可以计算过去⼀段时间内的最⼤值,最⼩值,以及平均值等。
- avg_over_time (range-vector) :区间向量内每个指标的平均值。
- min_over_time (range-vector) :区间向量内每个指标的最⼩值。
- max_over_time (range-vector) :区间向量内每个指标的最⼤值。
- sum_over_time (range-vector) :区间向量内每个指标的求和。
- count_over_time (range-vector) :区间向量内指标样本的总个数。
# 6.2 PromQL 时间聚合示例
1、获取武汉城市中武昌区,最近 5 分钟的温度数据
# 表达式 | |
weather_oldxu{city="武汉",dist="武昌区"}[5m] | |
# 结果 | |
weather_oldxu{city="武汉", dist="武昌区", instance="prom-node01.oldxu.net:5000", job="weather-exporter"} | |
12 @1704269783.3 | |
22 @1704269843.3 | |
17 @1704269903.3 | |
17 @1704269963.3 | |
5 @1704270023.3 |
2、获取武汉城市中武昌区,最近 5 分钟温度的最⼤值
# 表达式 | |
max_over_time(weather_oldxu{city="武汉",dist="武昌区"}[5m]) | |
# 结果 | |
{city="武汉", dist="武昌区", instance="prom-node01.oldxu.net:5000", job="weather-exporter"} 22 |
3、获取武汉城市中武昌区,最近 5 分钟温度的最⼩值
# 表达式 | |
min_over_time(weather_oldxu{city="武汉",dist="武昌区"}[5m]) | |
# 结果 | |
{city="武汉", dist="武昌区", instance="prom-node01.oldxu.net:5000", job="weather-exporter"} 5 |
4、获取武汉城市中武昌区,最近 5 分钟温度的平均值
# 表达式 | |
min_over_time(weather_oldxu{city="武汉",dist="武昌区"}[5m]) | |
# 结果 | |
{city="武汉", dist="武昌区", instance="prom-node01.oldxu.net:5000", job="weather-exporter"} 14.6 |
5、获取武汉城市中武昌区,当前数据总共来⾃多少个样本
# 表达式 | |
count_over_time(weather_oldxu{city="武汉",dist="武昌区"}[5m]) | |
# 结果 | |
{city="武汉", dist="武昌区", instance="prom-node01.oldxu.net:5000", job="weather-exporter"} 5 # 说明 5 分钟的样本数有 5 个 |
# 6.3 PromQL 时间聚合实践
实例 1:查询最近 1 分钟内 tcp_timewait 连接数的最⼤值,并检查是否超过 1000 个,指标名称: node_tcp_connection_states
# 模拟⼤量 tcp_timewait:ab -n 1000 -c 2 http://localhost:9090/ | |
# 表达式 | |
max_over_time(node_tcp_connection_states{state="time_wait"}[1m]) >1000 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter", state="time_wait"} 1769 # 最近 1 分钟最⼤的值是 1769 |
实例 2:查询最近 1 分钟内 tcp_established 连接数的最⼤值,并检查是否超过 100 个,指标名称: node_tcp_connection_states
# 模拟 established: | |
# 服务端(node01):nc -lk 2345 | |
# 客户端(node02):for i in {1..1000}; do nc prom-node01.oldxu.net 2345 >/dev/null 2>&1 & done | |
# 表达式 | |
max_over_time(node_tcp_connection_states{state="established"}[1m]) > 100 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="node_exporter", state="established"} 133 | |
{instance="prom-node02.oldxu.net:9100", job="node_exporter", state="established"} 106 |
实例 3:查询⽹站平均请求延迟 1 分钟⼤于 3s 的站点,指标名称: probe_duration_seconds(需要 blackbox)
# 表达式 | |
avg_over_time(probe_duration_seconds[1m]) > 3 | |
# 结果 | |
{instance="https://httpstat.us/102", job="blackbox_http"} 14.501046884666668 |
实例 4:查询 MySQL 服务器在最近 1 分钟内平均运⾏线程数超过 50 的。指标名称: mysql_global_status_threads_running
# 模拟 MySQL 线程数 | |
for i in {1..120} ; do | |
mysql -e "SELECT SLEEP(60);" & | |
done | |
# 表达式 | |
avg_over_time(mysql_global_status_threads_running[1m]) > 50 | |
# 结果 | |
{instance="prom-node03.oldxu.net:9104", job="mysqld_exporter"} 62 # 平均线程数在 62,超过阈值定义的 50 |
实例 5:查询以监控 MySQL 服务器过去 1 分钟内的线程当前打开的最⼤连接数。如果这个数值超过了服务器配置的最⼤连接数的 80% 则触发告警。指标名称
- mysql_global_status_threads_connected (表示当前打开的连接数)
- mysql_global_variables_max_connections (表示配置允许的最⼤连接数)
# 模拟 MySQL 连接数 | |
for i in {1..120} ; do | |
mysql -e "SELECT SLEEP(60);" & | |
done | |
# 表达式 | |
max_over_time(mysql_global_status_threads_connected[1m]) / mysql_global_variables_max_connections * 100 > 80 | |
# 结果 | |
{instance="prom-node03.oldxu.net:9104", job="mysqld_exporter"} 80.79470198675497 # 当前最⼤连接数已经超过了 80% |
# 七. PromQL 向量匹配
# 7.1 PromQL 向量匹配介绍
在 Prometheus 中,执⾏ “向量与向量之间的运算” 时,需要遵循向量匹配的规则。这意味着两个向量必须具有 “相同的标签”,且对应的 “标签值也必须完全相同”,这才能进⾏运算。如果有任何⼀个标签或标签值不匹配,那么此次的运算将不会执⾏。这种匹配规则也被称为 “向量的⼀对⼀匹配”。例如,下⾯两个时间序列可以成功进⾏⼀对⼀匹配,⽽后可以正常执⾏各种运算:
http_requests_total{job="webserver", instance="promnode01.oldxu.net:9100"} | |
http_requests_duration_seconds{job="webserver",instance="prom-node01.oldxu.net:9100"} |
因为它们的标签以及标签值完全⼀致,所以它们可以直接进⾏运算操作。
# 7.2 PromQL ⼀对⼀向量匹配
但是在实际监控场景中,我们会经常遇到 “标签不完全相同” 的两个向量,但它们任然需要进⾏运算。
oldxu_requests_total{job="webserver", instance="prom-node01.oldxu.net:9100"} 3200 | |
#表示该实例的 HTTP 请求总数。 | |
oldxu_requests_status_total{job="webserver", instance="prom-node01.oldxu.net:9100", method="GET"} 500 | |
#表示该实例中使⽤ GET ⽅法的 HTTP 请求总数。 |
假设我们想要计算使⽤ GET ⽅法的请求总数,占总请求数的⽐例是多少。理想的计算公式是: GET ⽅法的请求总数 / 总的请求数 * 100 = GET 请求所占的⽐例。 但由于两个向量的标签不完全相同(⼀个有 method 标签,⼀个没有),因此⽆法进⾏直接进⾏计算。
为了解决这个问题,我们可以借助 PromQL 的向量匹配选项:
- 基于标签的匹配(on):指定基于哪些标签进⾏匹配。只有当指定的 ” 标签及其值 “ 在两个向量中都相同,向量之间才能进⾏运算。
- 忽略标签的匹配(ignoring):指定忽略某些标签,也就是在运算时不考虑这些标签,只要其他标签以及标签的值相同,向量之间就可以进⾏运算。
⽅式 1:使⽤ on 关键字匹配特定标签,明明确指定仅基于 job 和 instance 标签进⾏匹配
# 表达式 | |
oldxu_requests_status_total{method="GET"} / on (job, instance) | |
oldxu_requests_total * 100 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="webserver"} 15.625 # GET ⽅法占⽐总请求 15% |
⽅式 2:使⽤ ignoring 关键字忽略特定标签,忽略不希望参与匹配的 method 标签
# 表达式 | |
oldxu_requests_status_total{method="GET"} / ignoring (method) | |
oldxu_requests_total * 100 | |
# 结果 | |
{instance="prom-node01.oldxu.net:9100", job="webserver"} 15.625 # GET ⽅法占⽐总请求 15% |
# 7.3 PromQL ⼀对多向量匹配
在实际监控中,我们还会遇到需要进⾏ “⼀对多向量匹配” 的情况,即 “⼀个时间序列中的数据点” 需要与 “另⼀个时间序列中的多个数据点” 进⾏匹配运算。
举个例⼦:假设我们有如下两个指标:
# 第⼀个时间序列:记录了不同 HTTP ⽅法和状态码的错误请求总数。 | |
oldxu_requests_error_total{job="webserver", method="GET",code="500"} 220 | |
oldxu_requests_error_total{job="webserver", method="GET",code="404"} 130 | |
oldxu_requests_error_total{job="webserver", method="PUT",code="501"} 3 | |
oldxu_requests_error_total{job="webserver", method="POST",code="500"} 34 | |
oldxu_requests_error_total{job="webserver", method="POST",code="502"} 48 | |
# 第⼆个时间序列:记录了每种 HTTP ⽅法的请求总数。 | |
oldxu_requests_instance_total{job="webserver",method="GET"} 600 | |
oldxu_requests_instance_total{job="webserver","method"="POST"} 120 |
我们的⽬标是计算每种 HTTP ⽅法(GET 和 POST)对应不同状态码(404 和 500)的请求占该⽅法总请求的⽐例。⼤体计算公式如下:
# 1、GET ⽅法为 500 的请求总数 / GET 的总请求数 * 100 = GET 500 错误⽐例。 (220 /600 * 100 = 21.666666666666668) | |
# 2、GET ⽅法为 404 的请求总数 / GET 的总请求数 * 100 = GET 404 错误⽐例。 (130 /600 * 100 = 36.666666666666664) | |
# 3、POST ⽅法为 500 的请求总数 / POST 的总请求数 * 100 = POST 500 错误⽐例。 (34 / 120 * 100 = 28.333333333333332) | |
# 4、POST ⽅法为 502 的请求总数 / POST 的总请求数 * 100 = POST 502 错误⽐例。 (48 / 120 * 100 = 40) |
为了实现这⼀⽬标,我们有两个问题需要解决:
1、标签不⼀致:
- 具体问题:两个时间序列的标签集合不完全⼀致, oldxu_requests_error_total 包含 code 标签,⽽ oldxu_requests_instance_total 不包含。
- 解决⽅法:使⽤ ignoring (code) 来忽略 code 标签,从⽽使得两个时间序列在没有 code 标签的情况下可以匹配。
2、⼀对多匹配:
- 具体问题: oldxu_requests_error_total 中的每个数据点,都需要与 oldxu_requests_instance_total 中的总请求数相除。
- 解决办法:必须明确左侧还是右侧为多的⼀边,因此我们可以使⽤ group_left 或 group_right 来指明哪个是 “多”,然后进⾏匹配。
因此完整的 PromQL 查询如下:
- 1、使⽤ ignoring (code) 忽略左侧查询( oldxu_requests_error_total )中的 code 标签。
- 2、使⽤ group_left 修饰符来确保它能够与标签较少的右侧进⾏匹配。
- 3、将匹配后的结果相除,并乘以 100 得到百分⽐。
# 表达式 | |
oldxu_requests_error_total | |
/ ignoring (code) | |
group_left | |
oldxu_requests_instance_total * 100 | |
# 结果 | |
{code="404", instance="prom-node01.oldxu.net:7002", job="webserver", method="GET"} 21.666666666666668 | |
{code="500", instance="prom-node01.oldxu.net:7002", job="webserver", method="GET"} 36.666666666666664 | |
{code="500", instance="prom-node01.oldxu.net:7002", job="webserver", method="POST"} 28.333333333333332 | |
{code="502", instance="prom-node01.oldxu.net:7002", job="webserver", method="POST"} 40 |
# 7.4 PromQL 向量匹配示例
1、下载并运⾏程序,该程序⽤于模拟 “向量匹配相关的” 指标数据;
[root@prom-node01 ~]# wget http://file.oldxu.net/prometheus/exporter/vectormatch_exporter_oldxu | |
[root@prom-node01 ~]# mv vectormatch_exporter_oldxu /usr/local/bin | |
[root@prom-node01 ~]# chmod +x /usr/local/bin/vectormatch_exporter_oldxu | |
# 启动脚本 | |
[root@prom-node01 ~]# vim /usr/lib/systemd/system/vectormatch_exporter.service | |
[Unit] | |
Description=vectormatch_exporter | |
Documentation=https://prometheus.io/ | |
After=network.target | |
[Service] | |
ExecStart=/usr/local/bin/vectormatch_exporter_oldxu --port 7002 | |
ExecReload=/bin/kill -HUP | |
TimeoutStopSec=20s | |
Restart=always | |
[Install] | |
WantedBy=multi-user.target | |
[root@prom-node01 ~]# systemctl daemon-reload | |
[root@prom-node01 ~]# systemctl start vectormatch_exporter.service |
2、编辑 Prometheus 配置⽂件,抓取对应的指标数据
[root@prom-node01 ~]# vim /etc/prometheus/prometheus.yml | |
- job_name: "webserver" | |
static_configs: | |
- targets: ["prom-node01.oldxu.net:7002"] | |
# 重新加载 prometheus | |
[root@prom-node01 ~]# curl -X POST http://localhost:9090/-/reload |
示例 1:⼀对⼀向量匹配
示例 2:⼀对多向量匹配
# 7.5 PromQL 向量匹配实践
实例 1:查询每个实例 CPU 的各个模式使⽤的时间占 “总 CPU 的时间” ⽐例是多少,也是就占多少百分⽐。
- 1、获取每个实例各个模式占⽤ CPU 的时间,按照(instance、mode)进⾏分组并求和;
- 2、获取每个实例总占⽤ CPU 时间,按照(instance)进⾏分组求和;
- 3、将每种模式所使⽤的 CPU 时间 / CPU 总的时间 * 100 = 每种模式占总 CPU 时间的百分⽐;
# 表达式 | |
sum (node_cpu_seconds_total) by (instance,job,mode) | |
/ ignoring (mode) | |
group_left | |
sum (node_cpu_seconds_total) by (instance,job) * 100 |
实例 2:查询 “每个 CPU 核⼼” 上不同模式的时间,占总 CPU 时间的⽐率是多少,也就是占多少百分⽐。
- 1、计算 “每个 CPU 核⼼” 在 “各个模式下” 的累计 CPU 使⽤时间,按照(instance、cpu、mode)进⾏分组并求和;
- 2、计算 “每个 CPU 核⼼的总 CPU 时间” 不区分模式。按照(instance、cpu)进⾏分组并求和;
- 3、每个 CPU 核⼼的各个模式 / CPU 核⼼的总时间 * 100 = 每个 CPU 核⼼的各个模式时间占⽤百分⽐
# 表达式 | |
sum (node_cpu_seconds_total) by (instance,cpu,mode) | |
/ on (instance,cpu) | |
group_left | |
sum (node_cpu_seconds_total) by (instance,cpu) |