PromQL,让你轻松实现监控可视化!快来了解一下吧!

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: Prometheus 中的一些关键设计,比如注重标准和生态、监控目标动态发现机制、PromQL等。

Prometheus 中的一些关键设计,比如注重标准和生态、监控目标动态发现机制、PromQL等。


PromQL 是 Prometheus 的查询语言,使用灵活方便,但很多人不知道如何更好利用它,发挥不出优势。


PromQL主要用于时序数据的查询和二次计算场景。


1 时序数据


可理解成一个以时间为轴的矩阵,如下案例有三个时间序列,在时间轴对应不同值:


^

│     . . . . . . . . . .   node_load1{host="host01",zone="bj"}

│     . . . . . . . . . .   node_load1{host="host02",zone="sh"}

│     . . . . . . . . . .   node_load1{host="host11",zone="sh"}

v

<------- 时间 ---------->


每一个点称为一个


1.1 样本(sample)

1.1.1 组成

指标(metric):metric name和描述当前样本特征的labelsets

时间戳(timestamp):ms的时间戳

值(value):该时间样本的值

PromQL就是对这样一批样本数据做查询、计算。


2 应用场景

时序数据的查询和二次计算。


PromQL第一个核心价值

2.1 筛选

查询靠的查询选择器


查询选择器

每个监控图表的渲染或者每条告警规则的处理,都只是针对有限的几条数据,所以 PromQL第一个需求就是过滤。


假设我有两个需求:


查询上海所有机器1min的负载

查询所有以host0为前缀的机器1min的负载

# = 做 zone 的匹配过滤

node_load1{zone="sh"}

# =~ 做 host 的正则过滤

node_load1{host=~"host0.*"}


大括号里写过滤条件,主要是针对标签过滤,操作符除了等于号和正则匹配之外,还有不等于 != 和正则非 !~。


metric name可以写到大括号,比如我想【同时查看上海机器的 load1、load5、load15三指标】,可对 __name__,即 metric 名字做正则过滤:


{__name__=~"node_load.*", zone="sh"}


上面例子给出的3条PromQL都叫即时查询(Instant Query),返回内容叫即时向量( Instant Vector)。


即时查询返回的是当前最新值,如 10:00发起的查询,返回的就是 10:00 这时刻对应数据。但监控数据是周期性上报,并非每时每刻都有数据上报,10:00时可能恰无数据进来,此时 Prometheus 就会往前看,看看9:59、9:58、9:57等时间点有没有上报数据。


最多应该往前看多久?

由Prometheus启动参数 --query.lookback-delta 控制,默认 5 min。从监控角度,建议调短,改成 1 min --query.lookback-delta=1m?


某人使用 Telegraf 做 HTTP 探测,配置了一个告警规则: response_code 连续 3 min != 200 才告警。实际上只有一个数据点的 response_code!=200,过了3min还是报警。


主要原因

Telegraf 的 HTTP 探测,会默认把 status code 放到标签,导致标签非稳态结构(这行为不好,最好是把这类标签直接丢弃或使用categraf、blackbox_exporter做采集器),平时 code=200,出问题时 code=500,在 Prometheus 生态里,标签变了就是新的时间序列

跟 query.lookback-delta 有关,虽只有一个点异常,即code=500的这个时间序列只有一个点,但告警规则每次执行查询时,都是查到这个异常点,连续5min都如此。所以就满足规则里连续3min才告警的条件。这就是为何建议把 --query.lookback-delta 调短

除了即时查询,PromQL还有范围查询(Range Query),返回内容叫 Range Vector。


{__name__=~"node_load.*", zone="sh"}[1m]


范围查询就是多加个时间范围1min。即时查询每个指标返回一个点,范围查询会返回多个点。假设数据10秒钟采集一次,1分钟有6个点,都会返回。


Prometheus 官方文档 介绍各函数时,都会讲解函数参数,标明 Range Vector or Instant Vector。


PromQL的另一个核心价值


2.2 计算

有算术、比较、逻辑、聚合运算符等。


算术运算符

就常用的加减乘除、取模之类的符号。


# 计算内存可用率,就是内存可用量除以内存总量,又希望按照百分比呈现,所以最后乘以100

mem_available{app="clickhouse"} / mem_total{app="clickhouse"} * 100

# 计算北京区网口出向的速率,原始数据的单位是byte,网络流量单位一般用bit,所以乘以8

irate(net_sent_bytes_total{zone="beijing"}[1m]) * 8


比较运算符

比较运算符就是大于、小于、等于、不等于之类的,简单但意义重大, 告警规则的逻辑就是靠比较运算符支撑的。


mem_available{app="clickhouse"} / mem_total{app="clickhouse"} * 100 < 20

irate(net_sent_bytes_total{zone="beijing"}[1m]) * 8 / 1024 / 1024 > 700

带比较运算符的 PromQL 就是告警规则的核心,如内存可用率的告警,在 Prometheus 这样配置:


groups:

- name: host

 rules:

 - alert: MemUtil

  # 指定了查询用的 PromQL

   expr: mem_available{app="clickhouse"} / mem_total{app="clickhouse"} * 100 < 20

   # 偶尔一次低于 20% 不是啥大事,只有连续1min每次查询都低于20%才告警,这就是 `for: 1m` 意义

   for: 1m

   labels:

     severity: warn

   annotations:

     summary: Mem available less than 20%, host:{{ $labels.ident }}


告警引擎会根据用户的配置,周期执行查询:


查不到就说明一切正常,没有机器的内存可用率低于20%

查到了,说明触发了告警,查到几条就触发几条告警

逻辑运算符

and、or和unless,用于 instant-vector 间的运算。and 是求交集,or是求并集,unless是求差集。


and使用场景,关于磁盘使用率,有的分区大如16T,有的分区小如50G,只用磁盘使用率做告警就不合理,如 disk_used_percent{app="clickhouse"} > 70 表示磁盘使用率大于70%就告警。对于小盘,这策略合理,但对大盘,70%使用率表示还有很多空间,就不合理。这时我们希望给这个策略加个限制,只有小于200G的硬盘在使用率超过70%时才告警,就能用 and:


disk_used_percent{app="clickhouse"} > 70 and disk_total{app="clickhouse"}/1024/1024/1024 < 200


向量匹配

向量间的操作想要在右侧的向量中,为左侧向量的每个条目找到一个匹配的元素,匹配行为分为:one-to-one、many-to-one、one-to-many。刚才介绍的磁盘使用率的例子,就是典型的 one-to-one 类型,左右两侧的指标,除了指标名,其余标签都是一样的,非常容易找到对应关系。但是有时候,我们希望用 and 求交集,但是两侧向量标签不同,怎么办呢?


此时我们可以使用关键字 on 和 ignoring 来限制用于做匹配的标签集。


mysql_slave_status_slave_sql_running == 0

and ON (instance)

mysql_slave_status_master_server_id > 0



这个PromQL想表达的意思是如果这个MySQL实例是个slave(master_server_id>0),就检查其slave_sql_running的值,如果slave_sql_running==0,就表示slave sql线程没有在运行。


但mysql_slave_status_slave_sql_running和mysql_slave_status_master_server_id这两个metric的标签,可能并非完全一致。不过好在二者都有个instance标签,且相同的instance标签的数据从语义上来看就表示一个实例的多个指标数据,那我们就可以用关键字on来指定只使用instance标签做匹配,忽略其他标签。


与on相反的是关键字ignoring,顾名思义,ignoring是忽略掉某些标签,用剩下的标签来做匹配。我们拿 Prometheus 文档中的例子来说明。


## example series

method_code:http_errors:rate5m{method="get", code="500"}  24

method_code:http_errors:rate5m{method="get", code="404"}  30

method_code:http_errors:rate5m{method="put", code="501"}  3

method_code:http_errors:rate5m{method="post", code="500"} 6

method_code:http_errors:rate5m{method="post", code="404"} 21

method:http_requests:rate5m{method="get"}  600

method:http_requests:rate5m{method="del"}  34

method:http_requests:rate5m{method="post"} 120

## promql

method_code:http_errors:rate5m{code="500"}

/ ignoring(code)

method:http_requests:rate5m

## result

{method="get"}  0.04            //  24 / 600

{method="post"} 0.05            //   6 / 120




例子里都是 one-to-one 的对应关系,这个好理解。难理解的是 one-to-many 和 many-to-one,这种情况下,做指标运算时就要借助关键字 group_left 和 group_right 了。left、right 指向高基数那一侧的向量。还是用上面method_code:http_errors:rate5m和method:http_requests:rate5m 这两个指标来举例,你可以看一下使用 group_left 的PromQL和输出的结果。


## promql

method_code:http_errors:rate5m

/ ignoring(code) group_left

method:http_requests:rate5m

## result

{method="get", code="500"}  0.04            //  24 / 600

{method="get", code="404"}  0.05            //  30 / 600

{method="post", code="500"} 0.05            //   6 / 120

{method="post", code="404"} 0.175           //  21 / 120


比如针对 method="get" 的条目,右侧的向量中只有一个记录,但是左侧的向量中有两个记录,所以高基数的一侧是左侧,故而使用 group_left。


这里我再举一个例子,来说明 group_left、group_right 的一个常见用法。比如我们使用 kube-state-metrics 来采集 Kubernetes 各个对象的指标数据,其中针对 pod 有个指标是 kube_pod_labels,该指标会把 pod 的一些信息放到标签里,指标值是1,相当于一个元信息。


kube_pod_labels{

[...]

 label_name="frontdoor",

 label_version="1.0.1",

 label_team="blue"

 namespace="default",

 pod="frontdoor-xxxxxxxxx-xxxxxx",

} = 1



假设某个 Pod 是接入层的,统计了很多 HTTP 请求相关的指标,我们想统计 5xx 的请求数量,希望能按 Pod 的 version 画一个饼图。这里有个难点:接入层的请求类指标没有 version 标签,version 信息只出现在 kube_pod_labels 里,怎么让二者联动?


sum(

 rate(http_request_count{code=~"^(?:5..)$"}[5m])) by (pod)

*

on (pod) group_left(label_version) kube_pod_labels


我们把这个 PromQL掰开揉碎,乘号前面的部分,是一个统计每秒 5xx 数量的典型语法,按照 pod 维度做分组统计。


然后乘以 kube_pod_labels,这个值是1。任何值乘以1都是原来的值,所以对整体数值没有影响,而 kube_pod_labels 有多个标签,而且和 sum 语句的结果向量的标签不一致,所以通过 on(pod) 语法来指定只按照 pod 标签来建立对应关系。


最后,利用 group_left(label_version),把 label_version 附加到了结果向量里,高基数的部分显然是 sum 的部分,所以使用 group_left 而非 group_right。


聚合运算

针对单指标的多个 series,还有聚合需求。如查看100台机器的平均内存可用率或排序,取数值最小的10台。


这种需求使用 PromQL 内置聚合函数。


# 求取 clickhouse 的机器的平均内存可用率

avg(mem_available_percent{app="clickhouse"})

# 把 clickhouse 的机器的内存可用率排个序,取最小的两条记录

bottomk(2, mem_available_percent{app="clickhouse"})

分组统计

分别统计 clickhouse 和 canal 的机器内存可用率,使用 by 指定分组统计的维度(与 by 相反的without)。


avg(mem_available_percent{app=~"clickhouse|canal"}) by (app)


这些聚合运算可理解为 纵向拟合。100 台机器的内存可用率,在折线图上有100条线,如想把这100条线拟合成一条线,就相当于把每个时刻的100个点拟合成1个点。咋让100个点变成1个点?求平均值或最大值之类,所以有了这些聚合运算符。


横向拟合

即 <aggregation>_over_time 等函数。这些函数接收范围向量,因为范围向量是一个时段内有多个值, <aggregation> 就是对这多个值做运算。


# [2m]:获取这个指标最近 2 分钟的所有数据点。若15秒采集一个点,2min就是8个点

# max_over_time:对这8个点求最大值,相当于对各个时间序列做横向拟合

max_over_time(target_up[2m])


3 容易误解的函数

increase 函数

字面意求取一个增量,接收一个 range-vector,range-vector 显然会返回多个 value+timestamp 的组合。直观理解就是,直接把时间范围内的最后一个值减去第一个值,不就可以得到增量?No!


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-14wDpCXz-1682918864743)(images/623851/5fe64d3408bba9b26b88f556865b2983.png)]


promql: net_bytes_recv{interface="eth0"}[1m] @ 1661570908

965304237246 @1661570850

965307953982 @1661570860

965311949925 @1661570870

965315732812 @1661570880

965319998347 @1661570890

965323899880 @1661570900

promql: increase(net_bytes_recv{interface="eth0"}[1m]) @1661570909

23595160.8

监控数据10s上报一次,所以虽然两次 PromQL 查询时间不同:


一次 1661570908

一次 1661570909,但查询的原始数据内容一样,就是 1661570850~1661570900 几个时间点对应数据。

直观理解,在这几个时间点对应的数据上求取 increase,无非就是最后一个值减去第一个值,即965323899880-965304237246=19662634。但实际23595160.8,差别大。


实际上,increase 这个 PromQL 发起请求的时间是1661570909,时间范围是[1m],相当于告诉Prometheus,我要查询1661570849(由1661570909-60得出)~1661570909之间的 increase 数值。但原始监控数据并没有 1661570849、1661570909 这两个时刻的数值,咋办?Prometheus只能基于现有数据做外推,即使用最后一个点数值 - 第一个点的数值的结果,除以时间差,再乘60。


( 965323899880.0 − 965304237246.0 ) d i v ( 1661570900.0 − 1661570850.0 ) t i m e s 60 = 23595160.8 (965323899880.0-965304237246.0)\\div(1661570900.0-1661570850.0)\\times60=23595160.8

(965323899880.0−965304237246.0)

div(1661570900.0−1661570850.0)

times60=23595160.8


这样最终就得到1分钟的 increase 值,是小数。


rate 函数

increase是求取的时间段内增量,且有数据外推


rate函数则求取每s的变化率,也有数据外推,increase结果除以 range-vector 的时间段大小=rate值。


rate(net_bytes_recv{interface="eth0"}[1m])

== bool

increase(net_bytes_recv{interface="eth0"}[1m])/60.0


== 后跟个bool,表示希望返回一个 bool 值,如果是 True 就会返回 1,如果是 False 就返回 0。我们观察结果后发现,这个表达式永远都会返回 1,即等号前后的两个 PromQL 语义上是相同的。


rate 函数求取的变化率,相对平滑。因为是拿时间范围内的最后一个值和第一个值做数据外推,一些毛刺现象就会被平滑掉。如果想要得到更敏感的数据,我们可以使用 irate 函数。irate 是拿时间范围内的最后两个值来做计算,变化就会更剧烈,我们拿网卡入向流量这个指标来做个对比。


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjA7oHHy-1682918864746)(images/623851/68ac7b49091b60970b9a8f8a106500c3.png)]


蓝色的变化得更剧烈的线是用 irate 函数计算得到的,而紫色的相对平滑的线是用 rate 函数计算得到的,对比还是很强烈的。


4 总结

PromQL核心价值:


筛选


靠查询选择器,查询分为即时查询和范围查询


计算


有算术、比较、逻辑、聚合运算符,还有向量匹配逻辑


1111.png


5 FAQ

Prometheus 中提供了一个函数叫 absent,用于做数据缺失告警,使用得也很广泛,但是坑也挺大。这里我留给你一个问题:如果我想对 100 台机器的 node_load1 做数据缺失告警,应该如何配置?这个需求用 absent 解决合适吗?你能否给出 absent 的最佳使用场景?


对于100台机器的node_load1数据缺失告警需求,使用absent函数不太适合,原因在于:


Absent函数是用于监控某个指标是否消失(即不存在),而非该指标数据是否有缺失。如果只是某一个节点出现了一段时间的缺失,Absent将会误报为不存在。


在涉及到多个节点的情况下,每个节点可能由于各种原因导致其不能向Prometheus发送监控数据,从而触发错误的告警。所以要针对每个节点单独设置告警,即设置一个请求每个节点数据的查询语句,同时确保查询结果正常,并且对于每个节点的告警条件进行区分。


对于 Absent 函数最佳使用场景,它可以过滤掉一些无效的告警,比如对于一些稀有的事件或者异常数据点,当这些事件或者数据出现时我们就需要告警,但是如果出现的很少,我们就会被一堆“false”positive告警搞得心烦意乱。此时, Absent 函数就派上了用场,可以排除掉这些稀有数据并避免告警的的误判,提高告警的可靠性和准确性。

目录
相关文章
|
6月前
|
存储 Prometheus 监控
【监控】grafana图表使用快速上手
【监控】grafana图表使用快速上手
210 0
|
数据可视化 测试技术 数据安全/隐私保护
通过可观测可视化Grafana版进行数据可视化展示与分析
使用可观测可视化Grafana版进行数据可视化展示与分析。
|
数据可视化 开发者 索引
基于 Kibana Lens 进行数据可视化,灵活分析航班信息
在今天的练习中,我们将使用 Kibana 自带的数据来进行一些可视化的展示。希望对刚开始使用 Kibana 的用户有所帮助
6704 1
基于 Kibana Lens 进行数据可视化,灵活分析航班信息
|
数据可视化 数据库 开发者
课时1:基于Kibana Lens进行数据可视化,灵活分析航班信息
课时1:基于Kibana Lens进行数据可视化,灵活分析航班信息
|
存储 数据可视化 Linux
基于Kibana Lens进行数据可视化,灵活分析航班信息
通过本教程,您将通过简单的拖放操作,体验Kibana Lens灵活的数据可视化功能,在仪表板中创建条形图、表格、圆环图和树状图等可视化视图。
|
监控 数据可视化
kuma可视化监控
kuma可视化监控
12888 4
|
Prometheus Cloud Native 开发者
PromQL 使用基础|学习笔记(二)
快速学习 PromQL 使用基础
419 0
PromQL 使用基础|学习笔记(二)
|
存储 Prometheus 监控
PromQL 使用基础|学习笔记(一)
快速学习 PromQL 使用基础
398 0
PromQL 使用基础|学习笔记(一)
|
存储 数据采集 Prometheus
PromQL 使用基础|学习笔记(四)
快速学习 PromQL 使用基础
252 0
PromQL 使用基础|学习笔记(四)
|
存储 Prometheus 监控
PromQL 使用基础|学习笔记(三)
快速学习 PromQL 使用基础
614 0
PromQL 使用基础|学习笔记(三)

热门文章

最新文章