引入实验性 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端点。你的指标包含jobinstance标签,但集群名称位于单独的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_infok8s_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_infonode_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=~".+"})

注意:当前实现始终将jobinstance用作连接的识别标签,无论你选择哪个信息指标。这适用于大多数标准信息指标,但对于使用不同识别标签的自定义信息指标可能存在局限性。例如,一个与jobinstance具有不同识别标签的信息指标是kube_pod_labels,其识别标签是:namespacepod。未来info()的目标是知道TSDB中哪些是信息指标并自动使用所有这些指标,除非选择通过上述名称匹配器明确限制,并且知道每个信息指标的识别标签是什么。

实际应用场景

OpenTelemetry 集成

info()函数的主要驱动力是OpenTelemetry (OTel)集成。当使用Prometheus作为OTel后端时,资源属性(关于指标生产者的元数据)会自动转换为target_info指标:

  • service.instance.idinstance 标签
  • service.namejob 标签
  • service.namespace → 作为job的前缀(即,<namespace>/<service.name>
  • 所有其他资源属性 → target_info上的数据标签

这意味着,只要至少包含service.instance.idservice.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(最小可行产品),旨在验证方法并收集用户反馈。该实现存在一些有意的限制:

当前限制

  1. 默认信息指标:默认只考虑target_info

    • 变通方法:你可以在data-label-selector中使用__name__匹配器,例如{__name__=~"(target|build)_info"},尽管这仍然假设jobinstance是识别标签。
  2. 固定识别标签:总是假设jobinstance是用于连接的识别标签。

    • 不幸的是,这使得info()不适用于某些场景,例如包含来自kube_pod_labels的数据标签,但这是我们未来希望解决的问题。

未来发展

这些限制是暂时的。实验状态允许我们:

  • 收集实际使用反馈
  • 了解哪些用例最重要
  • 在确定最终API之前迭代设计

info()函数的未来版本应:

  • 默认考虑所有信息指标(不只是target_info
  • 根据信息指标元数据自动理解识别标签

重要提示:由于这是一个实验性功能,其行为可能在未来的Prometheus版本中发生变化,或者该函数甚至可能根据用户反馈从PromQL中完全移除。

提供反馈

你的反馈将直接影响此功能的未来,并帮助我们确定它是否应成为PromQL的永久组成部分。可以通过我们的社区联系或者通过创建Prometheus 问题 来提供反馈。

我们鼓励你尝试info()函数并分享你的反馈:

  • 它为你解决了哪些用例?
  • 你还希望看到哪些额外功能?
  • API如何改进?
  • 你是否看到性能有所提升?

结论

实验性的info()函数代表着使PromQL更易用、更可靠的重大进步。通过简化元数据标签丰富和自动处理流失问题,它消除了Prometheus用户的两大痛点,特别是对于那些采用OpenTelemetry的用户。

了解更多

欢迎在GitHub 讨论区 上与Prometheus社区分享你的想法,或通过CNCF Slack #prometheus 频道 联系我们。

愉快的查询!