Prometheus 重度用户经常会遇到标签 Enrichment 的问题:业务指标里缺少某个标签,需要从 target_info、build_info 这类 metadata 指标里把标签补到查询结果中。过去常见做法是写 on (...) group_left (...),语法复杂,还容易遇到标签变化带来的 many-to-many 匹配问题。
info 函数的价值,就是把这类 PromQL join 场景包装得更简单。
核心要点摘要
info函数用于给 instant vector 做标签 Enrichment,把 metadata 指标里的标签附加到查询结果上。- 它可以把复杂的
on (job, instance) group_left (...) target_info写法简化成info(...)。 info底层遇到多个匹配项时,会取最新时间戳的那一个,用来规避标签变化导致的 many-to-many 问题。- 当前文章讨论的是实验性能力,启用时需要加
--enable-feature=promql-experimental-functions。 - 当前
info函数的标识标签写死为job、instance,还不能自定义其他标识标签。
背景痛点
Prometheus 重度用户通常会遇到的一个典型痛点,是标签 Enrichment。
比如计算 HTTP QPS 的指标中缺少 k8s_cluster_name 标签,这个标签的信息在 target_info 中,此时就需要使用 join 逻辑来联动两个指标,把 target_info 中的 k8s_cluster_name 标签搞到最终的结果中。类似两个数据库 Table 之间的 join 操作。
比如:
sum by (http_status_code, k8s_cluster_name) (
rate(http_server_request_duration_seconds_count[2m])
* on (job, instance) group_left (k8s_cluster_name)
target_info
)
这个操作有两个问题:
1. 复杂
一般用户根本看不懂。毕竟没几个人认认真真学过 Promql,我之前写过 Promql 的教程:https://flashcat.cloud/tags/promql/ 如果你刚入门,那强烈建议读一读。
2. 标签变化问题
即著名的 Churn Problem 问题。延展上面的例子,target_info 中除了有 k8s_cluster_name 标签,可能还有别的标签,比如 k8s_pod_labels_app_kubernetes_io_version 标签。
如果版本发生变化,k8s_pod_labels_app_kubernetes_io_version 标签就会变,标签变化,在 Prometheus 生态里就是新的时间线。
而又因为 lookback delta(默认5分钟)的机制,导致在 5 分钟内,两个时间线会同时存在,进而导致 join 时的 many-to-many 问题!进而导致你的告警规则异常、仪表盘图表异常!
这是 Prometheus 明显的设计缺陷,但当时吧,大家觉得这个场景用得少,也没有更好的办法,就这么一直凑合用着了。直到…
社区在尝试把 OpenTelemetry 和 Prometheus 做整合,把 Prometheus 作为 OpenTelemetry 的后端存储,OpenTelemetry 的 attributes 变化有点频繁,于是,社区更加崩溃了,于是,info 函数来了。
什么是标签 Enrichment
标签 Enrichment 可以理解为“给原始指标补充查询所需的上下文标签”。
比如 HTTP 请求指标里有 http_status_code,但没有 k8s_cluster_name。如果集群名存在于 target_info 里,过去就需要通过 PromQL join 把两个指标关联起来。这样做能解决问题,但表达式长、学习成本高,而且一旦 metadata 标签变化,就容易出现匹配异常。
info 函数
先看一个直观例子,上面提到的 Promql 如果使用 info 会是这个样子:
sum by (http_status_code, k8s_cluster_name) (
info(rate(http_server_request_duration_seconds_count[2m]))
)
呀,简单多了吧。
也就是说,info 函数把“把 metadata 标签附加到业务指标上”这件事显式表达出来,用户不再需要每次都手写 group_left。
info 语法
info(v instant-vector, [data-label-selector instant-vector])
info 函数有两个参数:
- v:是一个 instant vector,info 会对这个 vector 进行标签丰富
- data-label-selector:参数可选,用来对 metadata 信息(target_info 就是 metadata 信息)的筛选
注意,xx_info 指标的标签通常很多,info 会把所有的标签全部附加到 v 上面,如果你不想看到这么多标签,就要在上层继续做聚合,比如上例中的 sum by (http_status_code, k8s_cluster_name),最终就只剩 http_status_code、k8s_cluster_name 两个标签。
data-label-selector 不好理解?举两个例子:
## example1:
info(
rate(http_server_request_duration_seconds_count[2m]),
{k8s_cluster_name=~".+"}
)
## example2:
info(
rate(http_server_request_duration_seconds_count[2m]),
{k8s_cluster_name="us-east-0"}
)
选择不同的 info 指标
上面直接使用 info 函数,没有指定是从 target_info 还是从 build_info 等 metadata 信息中提取标签,实际上,默认的 info 是写死了就从 target_info 中提取标签。
如果想从其他 xx_info 提取标签,可以使用第二个参数做过滤,把指标名写到花括号里,用 __name__ 做过滤:
# Use build_info instead of target_info
info(up, {__name__="build_info"})
# Use multiple info metrics (combines labels from both)
info(up, {__name__=~"(target|build)_info"})
# Select build_info and only include the version label
info(up, {__name__="build_info", version=~".+"})
最重要的
最重要的是 info 函数解决了 Churn 问题,info 底层处理时,遇到同时匹配多个的情况,就只取最新时间戳的那一个,以此规避 many-to-many。
这对标签频繁变化的场景很关键。否则,在 lookback delta 窗口内,新旧 metadata 时间线可能同时存在,PromQL join 就可能因为多个匹配项而失败。
启用 info 特性
在 Prometheus 进程启动的时候传个参数:
prometheus --enable-feature=promql-experimental-functions
缺陷
回顾最开始的 Promql:
sum by (http_status_code, k8s_cluster_name) (
rate(http_server_request_duration_seconds_count[2m])
* on (job, instance) group_left (k8s_cluster_name)
target_info
)
注意其中的 on (job, instance) 部分,这里的 job、instance 作为标识标签来联动两个指标。但是上文的 info 并没有提到在哪里指定标识标签啊,是的,info 函数现在是实验阶段,是写死的…就是固定的把 job、instance 作为标识标签,你想用其他的作为标识标签?不好意思,现在尚不支持…毕竟,现在这个特性只是实验阶段…
什么时候适合用 info 函数
如果你的查询符合下面几个条件,就适合评估 info 函数:
- 原始业务指标缺少某些上下文标签。
- 这些上下文标签存在于
target_info、build_info或其他xx_info指标里。 - 过去需要使用
on (...) group_left (...)做标签关联。 - 关联键刚好是
job和instance。 - 你可以接受它当前仍属于实验性特性的限制。
如果关联键不是 job、instance,或者你的生产环境不希望启用实验性 PromQL 函数,那就继续使用显式 join 更稳妥。
结论
info 函数解决的是 Prometheus 标签 Enrichment 的老痛点:复杂的 group_left 写法,以及 metadata 标签变化导致的 many-to-many 问题。
它让常见的 target_info 标签补充场景变得更短、更清晰。不过,它当前仍有明显边界:需要启用实验性函数,并且标识标签固定为 job、instance。所以它适合先在明确场景里尝试,不适合不加评估地替换所有 PromQL join。
FAQ
Q1:info 函数主要解决什么问题?
A:主要解决 PromQL 标签 Enrichment 问题,把 target_info 等 metadata 指标里的标签附加到业务指标上,同时简化 group_left 写法。
Q2:info 函数为什么能缓解 Churn Problem?
A:当多个 metadata 时间线同时匹配时,info 底层会取最新时间戳的那一个,从而规避 lookback delta 窗口内新旧标签同时存在导致的 many-to-many 问题。
Q3:info 函数当前最大的限制是什么?
A:文章讨论的能力仍是实验阶段,且标识标签固定为 job、instance,目前不能自定义其他标识标签。