1 PromQL基础概念

1.1 什么是PromeQL

Prometheus 内置了⼀种强⼤的查询语⾔:PromQL,即Prometheus Query Language。PromQL 允许⽤户实时查询监控数据,并对这些数据执⾏复杂的聚合和计算操作。

在PromQL中,查询的结果被称为"向量(vector)",分为两种类型:

1、即时向量(Instant vector):即时向量查询返回的是⼀组时间序列数据,但每个时间序列中只包含单个的最新数据点。例如:查询当前时刻服务器1分钟的负载,所得到的结果就是⼀个即时向量。

2、范围向量(Range vector):范围向量查询的结果包含了⼀个时间范围内的所有数据点。例如:查询过去1分钟内服务器负载的变化情况,返回的数据集就是⼀个范围向量。

在PromQL中,除了有向量类型的数据,还有其他的数据类型,具体有:

  • 标量(Scalar):标量仅代表⼀个单⼀的浮点数值。可以将向量的某⼀个值通过scalar()函数转为标量,然后进⾏数值运算,但使⽤较少。
  • 字符串(String):在{job="nginx",method="get", url="/api"} ,通过标签能很好的对数据进⾏分类和识别。

1.2 PromQL应用场景

Prometheus 的核⼼就是PromQL,PromQL在⽇常的数据可视化,查询、定义告警规则都会是⽤到,因此掌握PromQL基本上就掌握了Prometheus;

1、临时查询:使⽤PromQL,你可以实时地查询监控数据,这对于调试和诊断问题⾮常有帮助。通常,我们通过Prometheus⾃带的表达式浏览器来执⾏这些查询

Prometheus监控之PromQL入门_数据

2、数据可视化,PromQL 可以帮助我们创建数据的可视化展示,这些可视化通常是通过集成⼯具如Grafana来实现的,可以将我们PromQL查询的结果展示得更直观。

3. 监控告警:Prometheus可以直接使⽤PromQL对指标的查询结果来设置告警。⼀旦查询结果满⾜指定的条件,就会触发告警,⼀个完整的报警规则如下所示:

⼀个告警规则包括了告警的名称、条件、持续时间等信息。

groups:
- name: 告警组
  rules:
  - alert: 节点宕机 # 告警名称
    expr: up == 0 # 告警条件
    for: 1m # 持续时间
    labels:
      severity: critical
    annotations: # 定义邮件中收到的告警详细内容
      summary: "节点宕机警告 - 实例:{{ $labels.instance }}"
      description: "作业 {{ $labels.job }} 的节点⽆法访问,已经持续超过1分钟。"

2 PromQL基础使用

2.1 指标查询

1、查询指标的最直接⽅式是输⼊"指标的名称"。⽐如,你想知道系统的⼀分钟负载(node_load1)情况:

Prometheus监控之PromQL入门_运算符_02

2、但是,在实际监控时,我们通常只需要关注"特定节点"的指标数据。例如,如果我只想查看192.168.137.128 这个节点的⼀分钟负载情况,那么就可需要是⽤"标签匹配器"来筛选出所需的指标。

node_load1{instance="192.168.137.128:9100"}

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:查询所有实例,ens33⽹卡发送总⼤⼩,指标名称:

node_network_transmit_bytes_total{device="ens33"}

实例3:查询所有实例的⽹卡接收字节数,排除lo接⼝,指标名称:

node_network_receive_bytes_total

node_network_receive_bytes_total{device!="lo",device!~"virbr0"}

node_network_receive_bytes_total{device!~"lo|virbr0"}

实例4:查询:挂载点以/run开头的⽂件系统可⽤字节数,指标名称

node_filesystem_avail_bytes

node_filesystem_avail_bytes{mountpoint=~"^/run.*"}

实例5:块设备名字不包含dm开头的读字节数,指标名称

node_disk_read_bytes_total{device!~"dm.*"}

2.2 时间范围查询

在Prometheus中,范围向量选择器使我们能够提取时间序列中⼀段时间范围内的数据点。要定义⼀个范围向量选择器,你只需要在指标名称后⾯加上⽅括号 [],并在其中指定⼀个时间⻓度。时间⻓度有:s秒,m分钟,h⼩时,d天,w周,y年。使⽤范围向量选择器,你可以灵活查询从最近⼏分钟到⼏年的数据,便于分析和监控指标随时间的变化。

1、例如,我们想查询192.168.137.128这个实例,在过去2分钟,负载所有数据点。

node_load1{instance="192.168.137.128:9100", job="node_exporter"} [2m]

Prometheus监控之PromQL入门_数据_03

2、在(如过去⼏分钟、⼩时或天)来查询数据。但是,如果你需要查询⼀个绝对时间点的数据,您可以直接使⽤UNIX 时间戳来指定查询的具体时间。(在Prometheus中通常是以毫秒为单位的)时间在线转换⼯具(https://tool.lu/timestamp/)

例如,查询2024-08-20 15:50:00的192.168.137.128 实例的负载状态。需要先将时间转为UNIX时间戳,然后执⾏如下查询语句

node_load5{instance="192.168.137.128:9100", job="node_exporter"} @1724140200

Prometheus监控之PromQL入门_数据_04

2.3 时间偏移查询

除了能够查询过去⼏分钟或⼏⼩时的数据,以及查询指定时间点的数据,同时Prometheus也允许您通过offset修饰符来指定查询从当前时间往回推某个具体的时间段。这种⽅式常⽤于⽐较,例如:今天QPS是10000,昨天这个时间是5000,我们就可以计算它们的增⻓率之类的

1、如果您想要查看一个小时前的数据,可以使⽤以下查询表达式:

node_load5{instance="192.168.137.128:9100"} [1h]一个小时前到现在

node_load5{instance="192.168.137.128:9100"} [1h] offset 1h一个小时前到两个小时前之间

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 - node_network_receive_bytes_total offset 1h)/node_network_receive_bytes_total offset 1h * 100

3 PromQL常⽤函数

3.1Count类型常用函数

前⾯我们讲过,Counter类型的监控指标只增不减,因此其样本值应该是不断增⼤的。因此单纯的Counter总数并没有直接作⽤,⽽是需要借助于rate、irate、increase和等函数来计算样本数据的变化状况(增⻓率);

1、rate⽤于计算平均增⻓速率:计算公式:通过指定时间范围内的样本,使⽤最后⼀个样本的值减去第⼀个样本的值,⽽后除以这两个样本之间的间隔时⻓。

例如:rate(node_cpu_seconds_total{mode="user"}[1m]) ,表示要获取1分钟内,该指标上的user⽤户空间所占⽤CPU的时间平均增⻓速率;

(最后值-第一个值)/45s

node_cpu_seconds_total{cpu="0",instance="192.168.137.128:9100",mode="user"}[1m]

Prometheus监控之PromQL入门_运算符_05

rate(node_cpu_seconds_total{cpu="0",instance="192.168.137.128:9100",mode="user"}[1m])

Prometheus监控之PromQL入门_数据_06

2、irate⽤于计算瞬时的增⻓速率(灵敏度较⾼):计算公式:通过指定时间范围内的样本最后⼀个样本的值减去前⼀个样本的值,⽽后除以这两个样本之间的间隔时⻓。

例如:irate(node_cpu_seconds_total{mode="user"}[1m]) ,表示要获取1分钟内,该指标上的该指标上的user⽤户空间所占⽤CPU的瞬时增⻓速率;

(最后值-倒数第二值)/15s

3、increase⽤于计算指定时间范围内样本值的增加量:计算公式:通过指定时间范围内的样本最后⼀个样本的值减去第⼀个样本的值。注意:increase可能会引⽤时间范围边界之前的样本值,以便于计算能覆盖到指定的整个时间范围。

Prometheus监控之PromQL入门_数据_07

3.2 Gauge类型常用函数

Gauge类型的指标,存储的值是随着时间会变发⽣变化的,它常⽤求和、取平均值、最⼩值、最⼤值等;也会结合PromQL的 predict_linear 和delta 函数使⽤;

1、predict_linear(v range-vector, t, scalar):预测时间序列v在t秒后的值,它通过线性回归的⽅式来预测样本数据的变化趋势;

例如:predict_linear(node_filesystem_avail_bytes{instance="192.168.137.128:9100",mountpoint="/"}[4h],60*60*24*30)  ,使⽤过去4⼩时的数据来预测接下来30天(60*60*24*30)的磁盘空间趋势。

2、delta(v range-vector):计算范围向量中每个时间序列上的第⼀个样本值与最后⼀个样本值之差;其计算结果与increase函数相同;但delta 更适⽤于没有重置的场景,或者⽤来监控那些可能上升或下降的指标,例如温度、磁盘空间等。  

例如:delta(node_filesystem_avail_bytes[10m]) /1024 /1024 ,返回服务器上磁盘可⽤空间与10分钟之前的差异;

3、changes() :计算监控时间范围内某个时间序列的数据变化的次数。它只关⼼变化的次数,⽽不关⼼具体变化的值是什么。

例如:changes(nginx_up[10m]) 监控Nginx服务在给定时间内变化的次数,如果停⽌了变化次数+1,启动了变化次数+1。

4 PromQL⼆元运算符

PromQL提供了⼀系列⼆元运算符,包括算术运算(+ - * /)、⽐较运算( == <= >=)、以及集合运算( and or unless)。在PromQL中,⽤户可以执⾏以下类型的运算:

  1. 标量与标量之间的运算
  2. 即时向量与标量之间的运算
  3. 两个即时向量之间的运算。(当涉及到两个即时向量的运算时,PromQL遵循向量匹配机制(Vector Matching),定义其运算逻辑)

4.1 算术运算符介绍

在PromQL中算术运算符,是⽤来对指标数据执⾏基本的数学运算。⽀持的运算符有:+(加)、-(减)、*(乘)、/(除)、%(取模)和^(幂运算) 

1、标量与标量之间进⾏数学运算,其最终得到的也是标量(使⽤较少)

Prometheus监控之PromQL入门_数据_08

2、即时向量与标量进⾏运算,例如

将node_memory_MemTotal_bytes (节点内存总⼤⼩)的默认bytes单位转为MB

node_memory_MemTotal_bytes/1024/1024

Prometheus监控之PromQL入门_运算符_09

3、即时向量与即时向量进⾏运算,它们需要遵循向量的匹配逻辑,也就是向量与向量的标签必须完全匹配⼀致才可以进⾏运算,如果它们的标签不⼀致,则不会执⾏这个运算。例如:我们想计算内存的可⽤百分⽐,计算公式为"(内存可⽤空间)= 内存可⽤百分⽐ "这两个向量的标签是完全⼀致的,因此可以直接进⾏运算,否则⽆法正常进⾏运算,除⾮进⾏向量匹配特殊的处理

node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100

Prometheus监控之PromQL入门_标量_10

4.2 算术运算符实践

实例1:计算所有实例节点的eth0⽹卡,接收的总流量和发送的总流量之和(以GB显示) 

node_network_receive_bytes_total :节点⽹络接收的总⼤⼩(以字节为单位) 

node_network_transmit_bytes_total :节点⽹络发送的总⼤⼩(以字节为单位

(node_network_receive_bytes_total{device=~"^ens.*"} + node_network_transmit_bytes_total{device=~"^ens.*"})/1024/1024/1024

Prometheus监控之PromQL入门_运算符_11

实例2:计算所有实例节点的/分区 已经使⽤了,多少空间(以GB显示) 

node_filesystem_size_bytes :⽂件系统总⼤⼩(以字节为单位) 

node_filesystem_avail_bytes :⽂件系统可⽤空间(以字节为单位) 

(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"})/1024/1024/1024

Prometheus监控之PromQL入门_数据_12

实例3:计算所有实例节点的 /分区"已⽤空间百分⽐" 

(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"})/node_filesystem_size_bytes{mountpoint="/"} * 100

Prometheus监控之PromQL入门_数据_13

实例4:计算所有节点内存的"已⽤百分⽐" 

node_memory_MemTotal_bytes :总内存⼤⼩(单位字节) 

node_memory_MemAvailable_bytes :内存可⽤⼤⼩(单位字节)

(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)/node_memory_MemTotal_bytes * 100

Prometheus监控之PromQL入门_标量_14

4.3 比较运算符介绍

在PromQL中⽐较运算符,是⽤来对指标的数据进⾏条件判断,⼀般在告警规则中定义何时应该触发告警。PromQL⽀持的⽐较运算符有如下⼏个:

== :等于,当两边的数值相等时为真。

!= :不等于,当两边的数值不相等时为真。

> :⼤于,当左边的数值⼤于右边时为真。

< :⼩于,当左边的数值⼩于右边时为真。

>= :⼤于等于,当左边的数值⼤于或等于右边时为真。

<= :⼩于等于,当左边的数值⼩于或等于右边时为真。

在PromQL中,使⽤⽐较运算符时,默认情况下,如果⽐较结果为假(即条件不满⾜),则相关的时间序列不会出现在结果中

但是,如果在测试时,想要明确地看到哪些时间序列满⾜条件(为真)和哪些不满⾜(为假),可以使⽤bool修饰符,这个修饰符会将所有的时间序列都显示在结果中,满⾜条件的序列会有⼀个值为1(true),不满⾜的序列会有⼀个值为0(false)。

1、标量与标量之间进⾏⽐较运算

Prometheus监控之PromQL入门_标量_15

2、即时向量与标量进⾏⽐较运算,例如判断服务器1分钟的负载,是否有⼤于0以上的节点

node_load1 > bool 0

3、即时向量与即时向量进⾏⽐较运算,它们需要遵循向量的匹配逻辑,也就是向量与向量的标签必须完全匹配⼀致才可以进⾏运算,如果它们的标签不⼀致,则不会执⾏这个运算。例如:我们可以⽐较"可⽤内存"是否⼤于"空闲内存",如果满⾜该条件,那么会显示左侧的指标名称和指标当前的值。

# 即时向量与即时向量⽐较表达式

node_memory_MemAvailable_bytes > node_memory_MemFree_bytes

4.4 比较运算符实践

实例1:查询node_exporter这个job中,⽬前不存活实例有哪些(1为存活、0为不存活)。

up != 1 

实例2:查询所有实例 "已使⽤内存"超过

(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)/node_memory_MemTotal_bytes * 100 > 30

实例3:查询所有的实例"磁盘可⽤空间"不⾜30%的实例

node_filesystem_avail_bytes / node_filesystem_size_bytes * 100 < 30

实例4:查询所有实例中eth0设备的⽹络带宽,每秒发送速率超过 200Mb/s兆 的实例

# 1、在两个测试的节点上安装:yum install iperf -y 
# 2、服务端运⾏并指定端⼝:iperf -s -p 9999 
# 3、客户端模拟带宽发送命令,-b指定发送⼤⼩,-t指定发送持续时⻓:
iperf -c 192.168.137.131 -p 9999 -b 300M -t 60

# 表达式(计算公式:rate(总的传输[1m]) * 8 /1024/1024) > 200)⽹络速度或带宽通常以位每秒(如 Mbps, Gbps)为单位。因此需要将字节乘以8,能够将字节转换为位,这样可以更准确地描述传输速率

rate(node_network_transmit_bytes_total{device="lo"}[1m]) * 8 /1024/1024 > 200

实例5:查询所有https的域名,检查域名证书的过期时间,将还剩不到90天的域名列出来(需要借助后⾯的blackbox⿊盒监控,才能获取到对应的指标)

# 表达式,计算公式( (过期时间-当前时间) / 天(24*60*60) ) 

(probe_ssl_earliest_cert_expiry - time() ) / 86400 < 90

4.5 集合运算符介绍

在Prometheus的查询语⾔中,集合运算符主要⽤到的运算符包括(并且)、or(或者)和(排除) 

例如:我们有两个关键指标:backup_duration_seconds ⽤于记录每次备份操作的持续时间,⽽ backup_success 则指示备份操作是否成功(1表示成功,0表示失败)

场景1:备份成功但时间超过9s

当备份操作成功完成(backup_success == 1),并且执⾏时间超过9秒 (backup_duration_seconds > 9)时,我们需要发出告警通知"备份成功但备份时间过⻓"。这就需要使⽤backup_duration_seconds > 9 and backup_success == 1

场景2:备份失败或时间超过9s

如果备份时⻓超过了9秒(backup_duration_seconds > 9),或者备份操作失败(backup_success == 0),则同样需要发出告警通知"备份失败或时间过⻓"。在这种情况下,是⽤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

2、or示例,查询192.168.137.128:9100上CPU编号为 0 的idle 时间或 user 时间

node_cpu_seconds_total{cpu="0",instance="192.168.137.128:9100",mode="idle"}
or
node_cpu_seconds_total{cpu="0",instance="192.168.137.128:9100",mode="user"}

2、unless示例,查询 node_cpu_seconds_total 指标上CPU编号为0的,但要排除192.168.137.131节点,同时还要排除模式为idle|user|system|steal|nice

node_cpu_seconds_total{cpu="0"}
unless
node_cpu_seconds_total{instance="192.168.137.131:9100"}
unless
node_cpu_seconds_total{mode =~ "idle|user|system|steal|nice"}

4.6 集合运算符实践

实例1:查询实例的⽹络接收流量"并且"⽹络发送流量,每秒传输超过200Mb/s

#模拟接收和发送流量⽐较⾼:"需要在同⼀节点"模拟服务端和客户端,执⾏如下命令
# 1、模拟服务端:iperf -s -p 9999 
# 2、模拟客户端:iperf -c 192.168.137.131 -p 9999 -b 300M -t 60 
# 表达式
rate(node_network_transmit_bytes_total[1m]) * 8 /1024/1024 > 200
and
rate(node_network_receive_bytes_total[1m]) * 8 /1024/1024 > 200

实例2:查询当前磁盘,可⽤空间不⾜20GB"或者"当前磁盘可⽤空间不⾜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

5 PromQL聚合操作

5.1 聚合操作介绍

聚合运算,是数据处理中的⽐较常⻅操作,例如统计公司所有⼈员的年龄,求公司整体的平均年龄,最⼤年龄,或最⼩年龄等。因此聚合操作它是从⼀组数据值中,计算出⼀个单⼀的值

以下是⼀个包含了不同城市,以及各地区天⽓温度的表格。如果想要从这些数据中提取有价值的信息,如温度的最⼤值和最⼩值,我们需要使⽤聚合运算。

整体聚合:它不不会区分数据的维度,⽽是将所有的数据作为⼀个整体来处理。这可以帮助我们了解整个数据集的总体情况。例如:

  • 整体平均温度( avg ): 计算⽅法是将所有温度值相加后,除以数量 (27 + 11 + 14 + 5 + 4 + 19 + 12 + 12 + 30 + (-1) + 15 + 5) / 12 ≈ 12.83 度。
  • 整体最⾼温度( max ): 所有区域中,记录的最⾼温度,上述表格中为30° 度。
  • 整体最低温度( min ): 所有区域中,记录的最低温度,上述数据中为-1° 度。

分组聚合:是按照数据集的⼦集进⾏分组,例如:(按照城市)这个维度进⾏分组聚合,我们就可以分别计算不同城市的⽓温分布情况;

  • ⼴州的平均温度( avg ): ⼴州所有区的平均温度是(27 + 11 + 14) / 3 = 17.33° 度。
  • 上海的最⾼温度( max ): 上海各区中的最⾼温度,为徐汇区的 30°度。
  • 武汉的最低温度( min ): 武汉各区中的最低温度,为武昌区的 -1°度。

5.2 PromQL聚合介绍

Prometheus 的聚合操作与此前刚才所描述的常规聚合在本质上是相似的,只不过它⽀持多种聚合运算函数,包括:

  • max :计算⼀组时间序列中的最⼤值。
  • min :计算⼀组时间序列中的最⼩值。
  • avg :计算时间序列的平均值。
  • sum :计算时间序列值的总和。
  • count :它不考虑时间序列的具体值,仅⽤来统计时间序列的数量。例如统计不同OS的数量,或者统计有多少个正在运⾏的Pod等等。
  • count_vaules :对每个样本的值进⾏数量统计,例如:http请求的状态码,200出现了多少次,404出现了多少次,500出现了多少次;
  • topk :
  • bottomk :

除了这些基本聚合功能外,Prometheus 也提供了分组聚合的功能,它是基于时间序列的标签进⾏分组聚合:

  • by :通过by关键字,明确指定保留哪些标签进⾏聚合,其他的标签将被忽略。
  • without :与by 相反,without 关键字⽤于指定要排除的标签,⽽剩下的标签则⽤于聚合和分组

为了加深PromQL聚合操作的理解,我们使⽤前⾯提到的城市天⽓温度数据,并通过聚合操作来展示如何获取整体的最⾼温度、最低温度以及按城市维度进⾏分组求取平均温度等。

1、下载并运⾏程序,提供天⽓温度相关的指标数据。
cp weather_exporter /usr/local/bin/
chmod +x /usr/local/bin/weather_exporter
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 --port 7001
ExecReload=/bin/kill -HUP
TimeoutStopSec=20s
Restart=always
[Install]
WantedBy=multi-user.target

systemctl daemon-reload
systemctl start weather_exporter.service

3、编辑Prometheus配置⽂件,抓取对应的指标数据
- job_name: "weather-exporter"
metrics_path: "/metrics"
scrape_interval: 1m
static_configs:
- targets: ["192.168.137.131:7001"]

4、重新加载prometheus
curl -X POST http://192.168.137.131:9090/-/reload

5.3 PromQL聚合示例

示例1:获取所有城市的整体温度总和 
sum(weather)

示例2:分别展示不同城市的最⼤温度
max(weather) by(city)

示例3:分别展示不同城市的最⼩温度 
min(weather) by(city)

示例4:仅展示"武汉"城市的平均温度
avg(weather{city="武汉"})

示例5:是⽤topk获取前三个的⾼温城市。topk的结果按温度值从⾼到低排序。
topk(3,weather)

示例6:使⽤bottomk获取排名靠前三的低温城市。bottomk的结果按温度值从低到⾼排列。
bottomk(3,weather)

示例7:统计天⽓温度的数量,按城市进⾏区分
count(weather) by(city)

示例8:统计各温度值出现的频次,按城市进⾏区分
# 表达式 count_values("value_count", <metrics_name>) by (<label>) 
count_values("status",weather) by (city)


5.4 PromQL聚合实践

实例1:查询所有节点,最近1分钟的负载,是否⾼于cpu核⼼的2倍

# 模拟10个cpu工作:stress --cpu 10 

# 获取每个节点的负载表达式
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

实例2:查询每个节点的CPU的使⽤率,指标名称:node_cpu_seconds_total

# 表达式: (1 - CPU整体空闲使⽤率) * 100 = CPU使⽤率
(1 - avg(rate(node_cpu_seconds_total{mode="idle"}[1m])) by(instance)) * 100

实例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

实例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

实例5:计算Prometheus服务器的HTTP请求成功率,指标名称: prometheus_http_requests_total

# 计算公式: 请求成功的(2xx|3xx) / 总的请求 * 100 = 请求成功率
# 表达式
sum(prometheus_http_requests_total{code =~ "2.*|3.*"}) / sum(prometheus_http_requests_total) * 100

实例6:查询请求排名前三的URL,指标名称:prometheus_http_requests_total

topk(3,prometheus_http_requests_total)
sum (topk(3,prometheus_http_requests_total) ) by (instance,handler)

6 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) :区间向量内指标样本的总个数。

Prometheus监控之PromQL入门_运算符_16

6.2 PromQL时间聚合示例

1、获取武汉城市中武昌区,最近5分钟的温度数据

weather{city="武汉",dist="武昌区"}[5m]

2、获取武汉城市中武昌区,最近5分钟温度的最⼤值

max_over_time(weather{city="武汉",dist="武昌区"}[5m])

3、获取武汉城市中武昌区,最近5分钟温度的最⼩值

min_over_time(weather{city="武汉",dist="武昌区"}[5m])

4、获取武汉城市中武昌区,最近5分钟温度的平均值

avg_over_time(weather{city="武汉",dist="武昌区"}[5m])

5、获取武汉城市中武昌区,当前数据总共来⾃多少个样本

count_over_time(weather{city="武汉",dist="武昌区"}[5m])

6.3PromQL时间聚合实践

实例1:查询最近1分钟内 tcp_timewait 连接数的最⼤值,并检查是否超过node_tcp_connection_states{state="time_wait"}

#安装httpd,ab命令属于httpd
# 模拟⼤量tcp_timewait:ab -n 1000 -c 2 http://192.168.137.131:9090/ 

# 表达式
max_over_time(node_tcp_connection_states{state="time_wait"}[1m]) > 1000

实例2:查询最近1分钟内 tcp_established 连接数的最⼤值,并检查是否超过100个,指标名称:node_tcp_connection_states{state="established"}

# 模拟established:
# 服务端(node01):nc -lk 2345 
# 客户端(node02):for i in {1..1000}; do nc 192.168.137.131 2345 >/dev/null 2>&1 & done

# 表达式
max_over_time(node_tcp_connection_states{state="established"}[1m]) > 100

实例3:查询⽹站平均请求延迟1分钟⼤于3s的站点,指标名称: probe_duration_seconds(需要blackbox)

# 表达式
avg_over_time(probe_duration_seconds[1m]) > 3

实例4:查询MySQL服务器在最近1分钟内平均运⾏线程数超过50的。指标名称: mysql_global_status_threads_running

# 模拟MySQL线程数
for i in {1..120} ; do 
  mysql -uroot -p123456 -e "SELECT SLEEP(60);" & 
done 

# 表达式
avg_over_time(mysql_global_status_threads_running[1m]) > 50

实例5:查询以监控MySQL服务器过去1分钟内的线程当前打开的最⼤连接数。如果这个数值超过了服务器配置的最⼤连接数的80%则触发告警。指标名称

mysql_global_status_threads_connected (表示当前打开的连接数) 

mysql_global_variables_max_connections (表示配置允许的最⼤连接数)

# 模拟MySQL连接数
for i in {1..120} ; do 
  mysql -uroot -p123456 -e "SELECT SLEEP(60);" & 
done 

# 表达式
max_over_time(mysql_global_status_threads_connected[1m]) / mysql_global_variables_max_connections * 100 > 80

7 PromQL向量匹配

7.1 PromQL向量匹配介绍

在Prometheus中,执⾏"向量与向量之间的运算"时,需要遵循向量匹配的规则。这意味着两个向量必须具有"相同的标签",且对应的"标签值也必须完全相同",这才能进⾏运算。如果有任何⼀个标签或标签值不匹配,那么此次的运算将不会执⾏。这种匹配规则也被称为"向量的⼀对⼀匹配"。

例如,下⾯两个时间序列可以成功进⾏⼀对⼀匹配,⽽后可以正常执⾏各种运算:

http_requests_total{job="webserver", instance="192.168.137.131:9100"}

http_requests_duration_seconds{job="webserver", instance="192.168.137.131:9100"}

因为它们的标签以及标签值完全⼀致,所以它们可以直接进⾏运算操作。

7.2 PromQL一对一向量匹配

但是在实际监控场景中,我们会经常遇到"标签不完全相同"的两个向量,但它们任然需要进⾏运算。

http_requests_total{job="webserver", instance="192.168.137.131:9100"} 3200 :表示该实例的HTTP请求总数。

http_requests_status_total{job="webserver", instan ce="192.168.137.131:9100", method="GET"} 500 :表示该实例中使⽤GET⽅法的HTTP请求总数。

假设我们想要计算使⽤GET⽅法的请求总数,占总请求数的⽐例是多少。理想的计算公式是: GET⽅法的请求总数 / 总的请求数 * 100 = GET请求所占的⽐例。但由于两个向量的标签不完全相同(⼀个有method标签,⼀个没有),因此⽆法进⾏直接进⾏计算

为了解决这个问题,我们可以借助PromQL的向量匹配选项:

基于标签的匹配(on):指定基于哪些标签进⾏匹配。只有当指定的"标签及其值"在两个向量中都相同,向量之间才能进⾏运算。

忽略标签的匹配(ignoring):指定忽略某些标签,也就是在运算时不考虑这些标签,只要其他标签以及标签的值相同,向量之间就可以进⾏运算。

⽅式1:使⽤on关键字匹配特定标签,明明确指定仅基于job和instance标签进⾏匹配

http_requests_status_total{method="GET"} /
on (instance,job)
http_requests_total

⽅式2:使⽤ignoring关键字忽略特定标签,忽略不希望参与匹配的method标签

http_requests_status_total{method="GET"} /
ignoring (method)
http_requests_total

7.3 PromQL一对多向量匹配

在实际监控中,我们还会遇到需要进⾏"⼀对多向量匹配"的情况,即"⼀个时间序列中的数据点"需要与"另⼀个时间序列中的多个数据点"进⾏匹配运算。

举个例⼦:假设我们有如下两个指标:

# 第⼀个时间序列:记录了不同HTTP⽅法和状态码的错误请求总数。
http_requests_error_total{job="webserver", method="GET",code="500"} 220 
http_requests_error_total{job="webserver", method="GET",code="404"} 130 
http_requests_error_total{job="webserver", method="PUT",code="501"} 3 
http_requests_error_total{job="webserver", method="POST",code="500"} 34 
http_requests_error_total{job="webserver", method="POST",code="502"} 48 
# 第⼆个时间序列:记录了每种HTTP⽅法的请求总数。
http_requests_instance_total{job="webserver",method="GET"} 600 
http_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、标签不⼀致:

具体问题:两个时间序列的标签集合不完全⼀致,http_requests_error_total 包含code标签,⽽ http_requests_instance_total 不包含。解决⽅法:使⽤ignoring(code)来忽略code标签,从⽽使得两个时间序列

在没有code标签的情况下可以匹配。

2、⼀对多匹配:

具体问题:http_requests_error_total 中的每个数据点,都需要与http_requests_instance_total中的总请求数相除。

解决办法:必须明确左侧还是右侧为多的⼀边,因此我们可以使⽤group_left 或group_right 来指明哪个是"多",然后进⾏匹配。

因此完整的PromQL查询如下:

  1. 使⽤ignoring(code)忽略左侧查询( http_requests_error_total )中的code标签。
  2. 使⽤ group_left 修饰符来确保它能够与标签较少的右侧进⾏匹配。
  3. 将匹配后的结果相除,并乘以100得到百分⽐
http_requests_error_total /
ignoring (code)
group_left
http_requests_instance_total

Prometheus监控之PromQL入门_标量_17

7.4 PromQL向量匹配实践

实例1:查询每个实例CPU的各个模式使⽤的时间占"总CPU的时间"⽐例是多少,也是就占多少百分⽐。

  1. 获取每个实例各个模式占⽤CPU的时间,按照(instance、mode)进⾏分组并求和;
  2. 获取每个实例总占⽤CPU时间,按照(instance)进⾏分组求和;
  3. 将每种模式所使⽤的CPU时间 / CPU总的时间 * 100 = 每种模式占总CPU时间的百分⽐;
sum by (instance, mode) (node_cpu_seconds_total)
/ ignoring (mode) 
group_left
sum by (instance) (node_cpu_seconds_total) * 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)
/ ignoring (mode) 
group_left
sum(node_cpu_seconds_total) by (instance,cpu) * 100