relabeling · Prometheus 出了名的强大与烧脑

relabeling 是「发现的目标」和「实际抓的目标」之间那台可编程的机器。这一章读 model/relabel 的 11 种 action、讲清那个统一的四步流水线心智模型,以及两个 relabel 阶段的关键区别——它同时也是控制基数最有力的工具。

作者 David更新于 第 11 篇(共 52 篇)

relabeling · Prometheus 出了名的强大与烧脑

服务发现吐出一堆带着 __meta_* 标签的目标,但“发现的”和“该抓的”往往不是一回事:你可能只想抓带某个注解的 pod、想把 K8s 的 namespace 变成指标标签、想丢掉某些没用的指标。干这活的是 relabeling——Prometheus 出了名的强大、也出了名的让人头疼的机制。

一台可编程的标签机器

relabeling 的本质:一条规则流水线,对每个目标(或每条指标)的标签集做变换,并可以在中途把它整个丢弃。 它是服务发现和抓取之间那台可编程的机器——正因为它可编程,Prometheus 才能适配千奇百怪的环境,而核心一行不用改。

代价是烧脑:它是声明式的、正则驱动的、多条规则顺序执行、且有两个不同的阶段。几乎每个用 Prometheus 的人都在这儿栽过跟头。但只要抓住一个统一的四步心智模型,它就不难了。

统一的四步心智模型

看 Config 结构(真实源码 model/relabel/relabel.go:86),字段本身就说明了流程:

type Config struct {
    // 从哪些标签取值,按 Separator 拼接
    SourceLabels model.LabelNames `yaml:"source_labels,flow,omitempty"`
    // 拼接时用的分隔符(默认 ";")
    Separator string `yaml:"separator,omitempty"`
    // 拿拼接结果去匹配这个正则
    Regex Regexp `yaml:"regex,omitempty"`
    // hashmod 用的模数
    Modulus uint64 `yaml:"modulus,omitempty"`
    // replace 时,结果写到哪个标签
    TargetLabel string `yaml:"target_label,omitempty"`
    // 替换模板(可用 $1 $2 引用正则捕获组)
    Replacement string `yaml:"replacement,omitempty"`
    // ...Action
}

每一条 relabel 规则都是同样的四步:

图表加载中…

记住这四步,所有 relabel 规则都是它的实例化。 举个最常见的例子——“只抓带 prometheus.io/scrape: true 注解的 pod”:

- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]  # ① 取这个标签的值
  regex: "true"                                                            # ② 匹配 "true"
  action: keep                                                             # ③ 匹配则保留,否则丢弃目标

再比如“把 K8s namespace 变成指标上的 namespace 标签”:

- source_labels: [__meta_kubernetes_namespace]   # ① 取值
  action: replace                                # ③ 替换
  target_label: namespace                        # ④ 写到 namespace 标签

(没写 regex 时默认是 (.*),replacement 默认是 $1,所以就是原样拷贝。)

11 种 action

源码 model/relabel/relabel.go:46 列出了全部 action:

action干什么实战用途
replace正则替换后写入 target_label(默认 action)最常用:改写 / 拷贝标签
keep不匹配就丢弃目标只抓我想抓的
drop匹配就丢弃目标排除我不想抓的
labelmap按正则批量拷贝标签名把所有 __meta_*_label_* 一次性变成指标标签
labeldrop丢掉匹配的标签控基数:删掉没用的高基数标签
labelkeep只保留匹配的标签白名单式清理
hashmod取哈希取模写入标签分片:多个 Prometheus 各抓一部分
keepequal两个标签不相等就丢少见
dropequal两个标签相等就丢少见
lowercase / uppercase大小写转换规范化

hashmod 值得单说——它是 Prometheus 水平分片的官方解法。 第 1 章说过 Prometheus 刻意单机自足,那目标太多一台抓不完怎么办?分片:

- source_labels: [__address__]
  action: hashmod
  modulus: 4              # 分 4 片
  target_label: __tmp_shard
- source_labels: [__tmp_shard]
  regex: "0"              # 这台只抓第 0 片(其他 3 台分别配 1/2/3)
  action: keep

哈希保证同一个目标永远落在同一片,4 台 Prometheus 各抓 1/4、互不重叠也不遗漏。注意这仍然不是“分布式 Prometheus”——4 个实例彼此完全不知道对方存在,只是各自被配置成抓不同的子集。全局查询交给上层的 Thanos/Mimir。保持核心简单、复杂性推到外层,又一次。

两个阶段:relabel_configs vs metric_relabel_configs

这是最容易搞混、也最该搞清的一点:

relabeling 发生在两个完全不同的时机**,对应两个配置项——用错地方规则根本不生效。**

relabel_configsmetric_relabel_configs
时机抓取前,对目标抓取后,对每条指标
作用对象目标的标签(含 __meta_*、__address__)抓回来的每条 series 的标签
drop 的效果不抓这个目标(省网络+CPU+存储)抓了但丢掉这条指标(只省存储)
典型用途决定抓谁、设置 __address__/__metrics_path__丢掉不需要的指标、删高基数标签

关键差异:relabel_configs 里 drop 掉的目标,压根不会被访问;metric_relabel_configs 里 drop 掉的指标,已经被抓回来解析过了。 所以:

  • 想不抓某个目标 → 用 relabel_configs(便宜)。
  • 想丢掉某个指标 → 只能用 metric_relabel_configs(因为指标是抓回来才知道的)。

另外 __meta_* 和 __ 开头的标签在 relabel_configs 之后就被丢弃了——它们只是 relabel 阶段的原料,不会进入最终 series。想留下某个 __meta_* 的值,必须在 relabel_configs 里显式把它 replace 到一个正常标签上。这是新手最常见的困惑:“我明明看到 SD 有这个标签,为什么指标上没有?”——因为你没把它拷贝出来。

实战:relabeling 是控基数最有力的工具

metric_relabel_configs 是你面对基数爆炸时的急救手段**——尤其当你改不了那个应用的代码时(第三方组件、云厂商 exporter)。** 两个高频救命姿势:

metric_relabel_configs:
  # ① 丢掉整个没用的高基数指标
  - source_labels: [__name__]
    regex: "some_useless_high_cardinality_metric.*"
    action: drop

  # ② 保留指标,但删掉那个高基数标签(如带 UUID 的 path)
  - regex: "user_id|request_id|session_id"
    action: labeldrop

第 ② 招尤其实用:很多 exporter 会吐出带 ID 的标签,让 series 数爆炸,但指标本身有用。labeldrop 把那个标签删掉,剩下的 series 会自动合并(标签少了,原本不同的 series 变成同一条)。

但要注意一个陷阱:labeldrop 后如果多条 series 塌缩成同一条,会产生“重复样本”错误(同一时刻同一 series 两个值)。所以删标签前要确认:删掉它之后,剩下的标签组合仍然唯一。如果不唯一,你需要的其实是在源头聚合,而不是 relabel。

最好的基数控制永远在埋点那一刻(第 2 部分讲),relabel 是改不了源头时的补救。

小结与下一章

relabeling 是服务发现与抓取之间那台可编程的标签机器——它让 Prometheus 能适配千奇百怪的环境而核心不改。抓住统一的四步模型就不烧脑:① source_labels 取值按 separator 拼接 → ② 匹配 regex → ③ 按 action 决策 → ④ 写 target_label 或丢弃。11 种 action(model/relabel/relabel.go:46)里最常用 replace(默认)/keep/drop/labelmap/labeldrop,而 hashmod 是官方的水平分片解法(多实例各抓一片,彼此仍互不知情)。两个阶段务必分清:relabel_configs(抓取前、对目标,drop 就不访问)vs metric_relabel_configs(抓取后、对每条指标,drop 只省存储);__meta_* 标签在第一阶段后即丢弃,想留必须显式 replace 出来。实战上 metric_relabel_configs 是基数急救手段(drop 整个指标 / labeldrop 删高基数标签),但注意标签塌缩会引发重复样本——根治永远在埋点那一刻。

目标定了、标签改好了,真正的抓取循环怎么跑?下一章收尾第 1 部分。👉 抓取循环。

参考资料

  • [1] Prometheus 源码 model/relabel/relabel.go(Config:86、11 种 Action:46、Process 实现)。
  • [2] Prometheus 官方文档:Configuration — <relabel_config>、metric_relabel_configs。
  • [3] 关联服务发现的 __meta_* 标签、基数控制。

本页目录