PromQL教程(四)PromQL 向量匹配

巴辉特 2024-10-09 16:40:54

PromQL 教程

在 2024 年的当下,Prometheus 生态基本已成为监控领域事实上的标准,学习 Prometheus 是每个运维人员的必修课,也是每个关注服务稳定性的研发人员的必修课。PromQL 是 Prometheus 的查询语言,全称是 Prometheus Query Language,想要学习 Prometheus,PromQL 是必学知识。本文是 PromQL 系列教程的第四讲,讲解 PromQL 向量匹配。本系列其他文章:

前文已经介绍,PromQL 支持算术运算符、逻辑运算符等,默认情况下,这些运算符要求左右两侧的向量标签完全匹配。但有时候,我们需要对不完全匹配的向量进行操作,这时候就需要用到向量匹配。向量之间的操作试图在右侧的向量中为左侧向量的每个条目找到一个匹配的元素,匹配行为分为:one-to-one、many-to-one、one-to-many。

这里所谓的向量,就是指一堆 vector,即某个 PromQL selector 查询出来的结果。

on 和 ignoring 关键字

前文演示 and、or、unless 的例子,两个 vector 的标签集都是一样的,那如果有些标签略微有些差异,可以使用 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

这个例子中,我们想要计算 500 错误率,即 500 错误数 / 总请求数。但是 500 错误数和总请求数是两个不同的 metric,它们的标签集也不完全一致,但是它们都有 method 标签,所以我们可以用 ignoring 关键字,忽略掉 code 标签,只用 method 标签来做匹配。

group_left 和 group_right 关键字

这两个关键词用于 one-to-many 和 many-to-one 的匹配场景,left、right 指向高基数的那一侧的 vector。还是拿上面的 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" 的条目,右侧的 vector 中只有一个记录,但是左侧的 vector 中有两个记录,所以高基数的一侧是左侧,故而使用 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 是接入层 ingress 的,统计了很多 HTTP 请求相关的指标,我们想统计 5xx 的请求数量,希望能按 Pod 的 version 画一个饼图。这里有个难点:接入层这个 Pod 没有 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 分组,然后用 on (pod) group_left(label_version),把 kube_pod_labels 中的 label_version 信息带到左侧的 vector 中,这样就可以按 version 绘制饼图了。

具体来讲:

  • 乘号前面的部分,是一个典型的统计每秒 5xx 数量的语法,group by pod。
  • 乘以 kube_pod_labels,这个值是1,所以对整体数值没有影响,而 kube_pod_labels 有多个标签,而且和sum语句的结果vector的标签不一致,所以通过on(pod) 的语法指定只是按照pod标签来做对应关系。
  • 利用 group_left(label_version) 把 label_version 附加到了结果向量中,高基数的部分显然是 sum 的部分,所以是 group_left 而非 group_right。

在 Prometheus 里,这种用法非常常见,因为很多时候,我们需要把一些元信息带到指标数据中,然后再做一些统计。当然,你也可以在采集的时候就把这些元信息放到指标数据中,但是这样会有一些问题,比如元信息标签如果发生变化,就会导致指标时间线变化,即产生指标流失问题,且折线数据不连续。当然,好处就是简单,不用写这么复杂的 group_left 语句。

本文小结

本文讲解了 PromQL 中的向量匹配,包括 on、ignoring、group_left、group_right 四个关键字的用法。向量匹配是 PromQL 中非常重要的一个概念,掌握好向量匹配,可以让你更好地利用 PromQL 进行数据分析和监控。

快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云
OpenSource
开源版
Flashcat
Flashcat