引入实验性 info() 函数
2025年12月16日作者 Arve Knudsen
即使你是PromQL高手,在Prometheus中用元数据标签丰富指标也可能出人意料地棘手!传统上用于此的PromQL连接查询本身相当复杂,因为它必须指定要连接的标签、要连接的信息指标以及要丰富数据的标签。新的、仍处于实验阶段的info()函数,承诺提供一种更简单的方法,使标签丰富变得像将查询封装在一个函数调用中一样简单。
在Prometheus 3.0中,我们引入了info()函数,这是一种用信息指标中的标签丰富时间序列的强大新方法。info()与传统连接查询技术的特别之处在于,它省去了你指定识别标签、要连接的信息指标,以及用于丰富数据的(“数据”或“非识别”)标签的麻烦。请注意,在此特定上下文中,“识别标签”指的是用于标识相关信息指标的标签集,并与关联的非信息指标共享。它们是你在Prometheus连接查询 中进行连接时使用的标签。从概念上讲,它们可以与关系数据库中的外键 相媲美。
除了主要功能之外,info()还解决了一个多年来困扰连接查询的微妙但关键的问题:当非识别信息指标标签发生变化时,查询会失败的“流失问题”(churn problem),再加上缺少陈旧标记(OTLP摄取时就是这种情况)。
无论你是使用OpenTelemetry资源属性、Kubernetes标签还是任何其他元数据,info()函数都能让你的PromQL查询更清晰、更可靠、更易于理解。
问题:复杂的连接和流失问题
让我们先看看目前为止我们必须做些什么。假设你正在通过OpenTelemetry监控HTTP请求持续时间,并希望按Kubernetes集群对其进行细分。你将指标推送到Prometheus的OTLP端点。你的指标包含job和instance标签,但集群名称位于单独的target_info指标中,作为k8s_cluster_name标签。传统方法如下所示:
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. 复杂性:你需要知道
- 哪个信息指标包含你的标签(
target_info) - 哪些标签是用于连接的“识别”标签(
job,instance) - 你想要添加哪些数据标签(
k8s_cluster_name) - 适当的PromQL连接语法(
on,group_left)
这需要专家级的PromQL知识,并使查询更难阅读和维护。
2. 流失问题(关键问题)
这里有一个微妙但严重的问题:当Kubernetes容器中的OTel资源属性发生变化,而识别资源属性保持不变时,会发生什么?例如,资源属性可以是k8s.pod.labels.app.kubernetes.io/version。那么相应的target_info标签k8s_pod_labels_app_kubernetes_io_version会发生变化,Prometheus会看到一个全新的target_info时间序列。
由于OTLP端点不会将旧的target_info序列标记为陈旧,因此旧系列和新系列可以同时存在长达5分钟(默认回溯时间)。在此重叠期间,你的连接查询会找到两个不同的匹配target_info时间序列,并因“多对多匹配”错误而失败。
实际上,这可能意味着当基础设施发生变化时,你的仪表盘会中断,警报也会停止触发,这可能正是你最需要可见性的时候。
Info 函数提供了一个解决方案
此前的连接查询可以转换为使用info函数,如下所示:
sum by (http_status_code, k8s_cluster_name) (
info(rate(http_server_request_duration_seconds_count[2m]))
)
是不是更容易理解了?至于解决流失问题,真正的魔法发生在幕后:info()会自动选择具有最新样本的时间序列,从而完全消除与流失相关的连接失败。请注意,这次对info()的调用会返回target_info中的所有数据标签,但这无关紧要,因为我们使用sum将它们聚合掉了。
基本语法
info(v instant-vector, [data-label-selector instant-vector])
v: 用于丰富元数据标签的即时向量data-label-selector(可选):花括号中的标签匹配器,用于过滤要包含哪些标签
在最基本的形式中,省略第二个参数,info()会添加来自target_info的所有数据标签。
info(rate(http_server_request_duration_seconds_count[2m]))
另一方面,通过第二个参数,你可以控制要从target_info中包含哪些数据标签。
info(
rate(http_server_request_duration_seconds_count[2m]),
{k8s_cluster_name=~".+"}
)
在上面的示例中,info()包含了来自target_info的k8s_cluster_name数据标签。由于选择器匹配任何非空字符串,它将包含任何k8s_cluster_name标签值。
还可以过滤要包含哪些k8s_cluster_name标签值。
info(
rate(http_server_request_duration_seconds_count[2m]),
{k8s_cluster_name="us-east-0"}
)
选择不同的信息指标
默认情况下,info()使用target_info指标。但是,你可以通过在data-label-selector中包含一个__name__匹配器来选择不同的信息指标(如build_info或node_uname_info)。
# 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=~".+"})
注意:当前实现始终将job和instance用作连接的识别标签,无论你选择哪个信息指标。这适用于大多数标准信息指标,但对于使用不同识别标签的自定义信息指标可能存在局限性。例如,一个与job和instance具有不同识别标签的信息指标是kube_pod_labels,其识别标签是:namespace和pod。未来info()的目标是知道TSDB中哪些是信息指标并自动使用所有这些指标,除非选择通过上述名称匹配器明确限制,并且知道每个信息指标的识别标签是什么。
实际应用场景
OpenTelemetry 集成
info()函数的主要驱动力是OpenTelemetry (OTel)集成。当使用Prometheus作为OTel后端时,资源属性(关于指标生产者的元数据)会自动转换为target_info指标:
service.instance.id→instance标签service.name→job标签service.namespace→ 作为job的前缀(即,<namespace>/<service.name>)- 所有其他资源属性 →
target_info上的数据标签
这意味着,只要至少包含service.instance.id或service.name资源属性,你通过OTLP发送到Prometheus的每个OTel指标都可以使用info()进行资源属性丰富。
# Add all OTel resource attributes
info(rate(http_server_request_duration_seconds_sum[5m]))
# Add only specific attributes
info(
rate(http_server_request_duration_seconds_sum[5m]),
{k8s_cluster_name=~".+", k8s_namespace_name=~".+", k8s_pod_name=~".+"}
)
构建信息
用构建时信息丰富你的指标。
# Add version and branch information to request rates
sum by (job, http_status_code, version, branch) (
info(
rate(http_server_request_duration_seconds_count[2m]),
{__name__="build_info"}
)
)
按生产者版本过滤
仅选择来自特定生产者版本的指标。
sum by (job, http_status_code, version) (
info(
rate(http_server_request_duration_seconds_count[2m]),
{__name__="build_info", version=~"2\\..+"}
)
)
前后对比:并排比较
让我们看看info()函数如何简化实际查询。
示例1:OpenTelemetry 资源属性丰富
传统方法
sum by (http_status_code, k8s_cluster_name, k8s_namespace_name, k8s_container_name) (
rate(http_server_request_duration_seconds_count[2m])
* on (job, instance) group_left (k8s_cluster_name, k8s_namespace_name, k8s_container_name)
target_info
)
使用 info()
sum by (http_status_code, k8s_cluster_name, k8s_namespace_name, k8s_container_name) (
info(rate(http_server_request_duration_seconds_count[2m]))
)
使用info,意图更加清晰:我们正在使用与Kubernetes相关的OpenTelemetry资源属性来丰富http_server_request_duration_seconds_count。
示例2:按标签值过滤
传统方法
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{k8s_cluster_name=~"us-.*"}
)
使用 info()
sum by (http_status_code, k8s_cluster_name) (
info(
rate(http_server_request_duration_seconds_count[2m]),
{k8s_cluster_name=~"us-.*"}
)
)
在这里,我们过滤只包含来自美国集群(名称以us-开头)的指标。info()版本将过滤器自然地集成到data-label-selector中。
技术优势
除了基本的UX优势外,info()函数还提供了几个技术优势:
1. 自动处理流失问题
如前所述,当存在多个版本时,info()会自动选择具有最新样本的匹配信息时间序列。这消除了在流失期间困扰传统连接查询的“多对多匹配”错误。
工作原理:当非识别信息指标标签发生变化(例如,pod被重新创建)时,会有一个短暂的时期,新旧序列可能同时存在。info()函数会简单地选择具有最新样本的那个,确保你的查询持续工作。
2. 更好的性能
info()函数比传统连接更高效:
- 只选择匹配的信息序列
- 避免不必要的标签匹配操作
- 优化查询执行路径
开始使用
info()函数是实验性的,必须通过功能标志启用。
prometheus --enable-feature=promql-experimental-functions
启用后,你可以立即开始使用它。
当前限制和未来计划
当前的实现是一个MVP(最小可行产品),旨在验证方法并收集用户反馈。该实现存在一些有意的限制:
当前限制
-
默认信息指标:默认只考虑
target_info。- 变通方法:你可以在data-label-selector中使用
__name__匹配器,例如{__name__=~"(target|build)_info"},尽管这仍然假设job和instance是识别标签。
- 变通方法:你可以在data-label-selector中使用
-
固定识别标签:总是假设
job和instance是用于连接的识别标签。- 不幸的是,这使得
info()不适用于某些场景,例如包含来自kube_pod_labels的数据标签,但这是我们未来希望解决的问题。
- 不幸的是,这使得
未来发展
这些限制是暂时的。实验状态允许我们:
- 收集实际使用反馈
- 了解哪些用例最重要
- 在确定最终API之前迭代设计
info()函数的未来版本应:
- 默认考虑所有信息指标(不只是
target_info) - 根据信息指标元数据自动理解识别标签
重要提示:由于这是一个实验性功能,其行为可能在未来的Prometheus版本中发生变化,或者该函数甚至可能根据用户反馈从PromQL中完全移除。
提供反馈
你的反馈将直接影响此功能的未来,并帮助我们确定它是否应成为PromQL的永久组成部分。可以通过我们的社区联系或者通过创建Prometheus 问题 来提供反馈。
我们鼓励你尝试info()函数并分享你的反馈:
- 它为你解决了哪些用例?
- 你还希望看到哪些额外功能?
- API如何改进?
- 你是否看到性能有所提升?
结论
实验性的info()函数代表着使PromQL更易用、更可靠的重大进步。通过简化元数据标签丰富和自动处理流失问题,它消除了Prometheus用户的两大痛点,特别是对于那些采用OpenTelemetry的用户。
了解更多
欢迎在GitHub 讨论区 上与Prometheus社区分享你的想法,或通过CNCF Slack #prometheus 频道 联系我们。
愉快的查询!