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.0965304237246.0)div(1661570900.01661570850.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核心价值:

  • 筛选

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

  • 计算

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

5 FAQ

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/16053.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习系统编程No.23【信号实战】

引言&#xff1a; 北京时间&#xff1a;2023/4/23&#xff0c;最近学习状态不怎么好&#xff0c;总是犯困&#xff0c;没精力的感觉&#xff0c;可能是病没有好彻底的原因&#xff0c;也可能是我内心因为生病而认为摆烂理所应当&#xff0c;反正最后导致摆烂&#xff0c;课现在…

Postman预请求脚本、测试脚本(pre-request scripts、tests常用工作总结)

文章目录 Postman预请求脚本&#xff08;pre-request scripts工作常用总结&#xff09;Postman预请求脚本Postman测试脚本预请求脚本和测试脚本有什么区别常用工作总结登录接口返回的是Set-Cookie标头 Postman预请求脚本&#xff08;pre-request scripts工作常用总结&#xff0…

2008-2019年主要城市PITI指数

2008-2019年主要城市PITI指数 1、来源&#xff1a;附在文件内 2、时间区间&#xff1a;2008-2019年 3、具体时间分布&#xff1a;、2008、2009-2010、2011、2012、2013-2014、2014-2015、2015-2016、2016-2017、2017-2018、2018-2019、 4、范围&#xff1a;包括110个城市&a…

Afkayas.1(★)

软件运行 要输入正确的Name和Serial 查壳 一个VB程序&#xff0c;没有加壳 载入OD 直接开搜索字符串。 这里看到了错误的提示&#xff0c;“You Get It”应该就是成功的字符串了。 前面的“AKA-”应该是在什么时候拼接的字符串 去成功的字符串附近看看 这个字符串上面…

网络编程 总结三

一、并发服务器模型 【1】 循环服务器 1>一次只能处理一个客户端的请求&#xff0c;等待这个客户端退出后&#xff0c;才能处理下一个客户端 2>缺点&#xff1a;循环服务器所处理的客户端不能有耗时操作 //*****模型****** sfd socket(); bind(); listen(); while(1)…

js 操作数组内容

js 操作数组内容 数组添加元素&#xff08;更改原数组&#xff09; push和unshift会返回添加了新元素的数组长度 push从数组最后加入&#xff0c;unshift从数组最前面加入 const arr ["a", "b", "c"]; arr.push("d"); //返回4…

【高危】泛微 e-cology <10.57 存在 SQL注入漏洞(POC)(MPS-ndqt-0im5)

漏洞描述 泛微协同管理应用平台(e-cology)是一套企业大型协同管理平台。 泛微 e-cology 受影响版本存在SQL注入漏洞&#xff0c;未经授权的远程攻击者可通过发送特殊的HTTP请求来获取数据库的敏感信息。 漏洞名称GeoServer 存在 sql 注入漏洞漏洞类型SQL注入发现时间2023/4/…

解密PyTorch动态计算图:打破深度学习束缚的秘密武器

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Linux 用户管理与文件权限

Linux 是一个多用户系统&#xff0c;它允许多个用户同时登陆主机&#xff0c;并为他们分配不同的资源和工作环境进行使用。当然&#xff0c;不同的用户都有文件的私有需求&#xff0c;所以设置不同用户文件的权限管理十分重要。 01 用户与用户组 Linux 中一般将文件访问权限的…

2023有哪些适合学生的蓝牙耳机?盘点四款适合学生的无线蓝牙耳机

随着时代的发展&#xff0c;人们更青睐于能够提升生活品质的产品。蓝牙耳机因为摆脱了线的束缚&#xff0c;使用体验会更好。接下来&#xff0c;我来给大家推荐几款适合学生用的无线蓝牙耳机&#xff0c;有需要的朋友可以当个参考。 一、南卡小音舱Lite2蓝牙耳机 参考价&…

【hello Linux】进程间通信——共享内存

目录 前言&#xff1a; 1. System V共享内存 1. 共享内存的理解 2. 共享内存的使用步骤 3. 共享内存的使用 1. 共享内存的创建 查看共享内存 2. 共享内存的释放 3. 共享内存的挂接 4. 共享内存的去挂接 4. 共享内存的使用示例 1. 两进程挂接与去挂接演示&#xff1a; 2. 两进程…

高性能:负载均衡

目录 什么是负载均衡 负载均衡分类 服务端负载均衡 服务端负载均衡——软硬件分类 服务端负载均衡——OSI模型分类 客户端负载均衡 负载均衡常见算法 七层负载均衡做法 DNS解析 反向代理 什么是负载均衡 将用户请求分摊&#xff08;分流&#xff09; 到不同的服务器上…

中移链控制台对接4A平台功能验证介绍

中移链控制台具备单独的注册登录页面&#xff0c;用户可通过页面注册或者用户管理功能模块进行添加用户&#xff0c;通过个人中心功能模块进行用户信息的修改和密码修改等操作&#xff0c;因业务要求&#xff0c;需要对中移链控制台的用户账号进行集中管理&#xff0c;统一由 4…

什么是分布式任务调度?怎样实现任务调度

通常任务调度的程序是集成在应用中的&#xff0c;比如&#xff1a;优惠卷服务中包括了定时发放优惠卷的的调度程序&#xff0c;结算服务中包括了定期生成报表的任务调度程序&#xff0c;由于采用分布式架构&#xff0c;一个服务往往会部署多个冗余实例来运行我们的业务&#xf…

C S S

目录 1.样式定义方式 1.1行内样式表 1.2内部样式表 1.3外部样式表 2.注解 3.选择器 3.1标签选择器 3.2 id选择器 3.3 类选择器 3.4 派生选择器 3.5 伪类选择器 链接伪类选择器&#xff1a; 位置伪类选择器&#xff1a; ​编辑 目标伪类选择器&#xff1a; 复合选…

Winform从入门到精通(37)——FolderBrowserDialog(史上最全)

文章目录 前言1、Name2、Description3、RootFolder4、SelectedPath5、ShowNewFolderButton前言 当需要获取一个可以通过用户自由选择路径的时候,这时候就需要FolderBrowserDialog控件 1、Name 获取FolderBrowserDialog对象 2、Description 用于指示对话框的描述,如下: …

Windows forfiles命令详解,Windows按时间搜索特定类型的文件。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 forfiles 一、结果输出格式二、按时间搜索三、搜索指定类型文件四、批量删除文件 forfile…

ATTCK v12版本战术介绍——防御规避(四)

一、引言 在前几期文章中我们介绍了ATT&CK中侦察、资源开发、初始访问、执行、持久化、提权战术理论知识及实战研究、部分防御规避战术&#xff0c;本期我们为大家介绍ATT&CK 14项战术中防御规避战术第19-24种子技术&#xff0c;后续会介绍防御规避其他子技术&#xf…

【Unity URP】Rendering Debugger和可视化MipMap方案

写在前面 最近开始学习Unity性能优化&#xff0c;是结合了《Unity游戏优化》这本书和教程《Unity性能优化》第叁节——静态资源优化(3)——纹理的基础概念一起学习。在学习纹理优化部分时候遇到了问题&#xff0c;固定管线下Unity的Scene窗口有一个可视化Mipmap的渲染模式&…

PMP/高项 05-项目进度管理

项目进度管理 概念 项目进度管理&#xff08;Schedule Management) 项目进度管理又叫项目工期管理&#xff08;Duration Management)或项目的时间管理(Time Management) 是一种为管理项目按时完成项目所需的各个过程 进度管理过程 规划进度管理 定义活动 排列活动顺序 估算活…