Prometheus 高基数指标救星:Relabeling 机制从入门到实战

高基数(High Cardinality)指标是 Prometheus 运维中最常见的内存杀手。本文彻底讲透 relabeling 机制——它是什么、为什么能解决基数爆炸、以及如何在不影响关键监控的前提下安全丢弃或改写冗余标签。适合刚接触 Prometheus 的小白,也用真实配置示例帮你避开踩坑。

Prometheus 高基数指标救星:Relabeling 机制从入门到实战
封面图:ZuCDN · ZuCDN 原创

当你的 Prometheus 内存一路飙升、查询响应从秒级变成分钟级,你大概率遇到了高基数(High Cardinality)问题。很多新手第一次听说“基数”这个词时一脸懵——简单说,一个指标的时间序列数量由它的标签组合决定。比如 http_requests_total{method="GET", endpoint="/api", status="200"} 就是一个系列。如果 endpoint 的值来自 URL 中的用户 ID(如 /user/12345),每多一个用户就多一个系列,几百个用户瞬间变成几万个系列——这就是高基数。

Prometheus 是为高基数而设计的?其实不是。它的内存使用与时间序列数量近似线性增长,单机通常只能稳定承载几百万个活跃序列。一旦基数失控,内存和查询性能会迅速恶化。很多团队的第一反应是“加机器”,但更优雅的解法是 relabeling——在数据进入存储之前就精简掉不必要的标签。

本文不讲复杂理论,直接解释 relabeling 是什么、为什么能解决高基数、以及怎么落地。你会看到可执行的配置,包含验证和回滚步骤,避免一改就崩。

1. 高基数到底“高”在哪

容易忽略的细节

先看一个真实场景:你用了 kube-state-metrics 采集 Kubernetes 集群状态,指标 kube_pod_info 会带上 podnamespacenodeuid 等标签。如果每个 pod 都有一个唯一的 uid,那么每个新 pod 都会新增一个时间序列。假设集群有 200 个 namespace、每个 namespace 平均 50 个 pod,仅 kube_pod_info 就会产生 10000 个系列。如果还有 kube_pod_labels 这类自带自定义标签的指标,基数可能再乘 10 倍。

更危险的是那些“业务 label”,比如 user_idsession_idrequest_id。这些值每次请求都不同,如果被采集进 Prometheus,几小时内就能把存储撑爆。

高基数的后果:

  • 内存暴涨:每个系列需要大约 2-3KB 内存记录元数据和最近样本,100 万个系列就吃掉 2-3GB。
  • 查询缓慢:PromQL 的聚合操作需要遍历大量系列,CPU 和磁盘 IO 飙升。
  • 告警滞后:规则评估周期变长,重要告警可能延迟几分钟。

2. Relabeling 到底是什么

先看关键判断

Relabeling 是 Prometheus 在采集(scrape)前后对目标标签和目标指标进行改写、丢弃或重命名的一组规则。它发生在两个阶段:

  • Scrape 阶段:在抓取目标之前,针对目标的元标签(以 __ 开头的)和最终标签进行处理。常用于服务发现后,根据元信息决定是否采集、如何打标签。
  • Metric 阶段:在抓取到样本数据之后,针对指标名称和标签进行修改。这才是精简基数的主战场。

Relabeling 规则写在 scrape_configs 中的 relabel_configsmetric_relabel_configs 里。

核心动作:

  • replace:修改标签值或创建新标签(默认动作)。
  • keep:仅保留符合匹配条件的目标或指标。
  • drop:丢弃符合匹配条件的目标或指标。
  • labelmap:将匹配到的标签名映射为新的标签名(常用于保留元标签)。
  • labeldrop:删除指定标签名。
  • labelkeep:仅保留指定标签名,其余全部删除。

3. 哪几种 relabeling 能直接打掉高基数

对付高基数,常用三招:

3.1 丢弃整条指标(drop)

如果某个指标本身就带高基数标签,且它的数据你根本不需要——直接 drop 掉。比如 kube_pod_labels 中的 label_app_kubernetes_io_name 经常变化,但你只关心 namespace 级的聚合,完全可以 drop 这个指标:

metric_relabel_configs:
  - source_labels: [__name__]
    regex: 'kube_pod_labels'
    action: drop

3.2 只保留必要标签(labelkeep)

有的指标不能丢,但它的标签太多了。比如 kube_pod_infopodnamespacenode,其中 pod 是唯一的,但你可以换成保留 namespacenode,丢掉 pod

metric_relabel_configs:
  - regex: 'namespace|node'
    action: labelkeep

这样 kube_pod_info 每个 namespace 和 node 组合只保留一个系列,基数从 pod 数量降到 namespace × node 数量。

3.3 用 labelmap 拉平并保留必要的元标签

有些 exporter 会把 SD(服务发现)元信息带进指标,比如 __meta_kubernetes_pod_label_app。如果你只想保留其中几个自定义标签,可以用 labelmap 把它们映射为普通标签:

metric_relabel_configs:
  - regex: '__meta_kubernetes_pod_label_(app|version)'
    action: labelmap
    replacement: '$1'

这样 __meta_kubernetes_pod_label_app 变成 app,而其他 __meta_kubernetes_pod_label_* 不会被保留,避免引入高基数。

4. 实战:用 relabeling 救活一个内存爆掉的 Prometheus——High

假设你的 Prometheus 已经 OOM 了。先别急着重启,按以下步骤走:

4.1 识别高基数元凶

用以下 PromQL 查看指标系列数排名:

topk(10, count by (__name__)({__name__=~".+"}))

如果你现在 Prometheus 连查询都打不开,可以直接去 TSDB 状态页 /tsdb-status 查看。

4.2 一次性临时止损:强制 drop 高基数指标

在 prometheus.yml 的全局 scrape_configs 里加一个 metric_relabel_configs,先 drop 掉那个指标。比如是 kube_pod_labels

scrape_configs:
  - job_name: 'kubernetes-pods'
    kubernetes_sd_configs:
      - role: pod
    metric_relabel_configs:
      - source_labels: [__name__]
        regex: 'kube_pod_labels'
        action: drop
    # 其他配置保持不变

然后 reload Prometheus 配置:kill -HUP <pid> 或者用 API POST /-/reload。几分钟后内存应该会回落。

4.3 精细调整:保留必要标签

稳定之后,再精细化配置。比如你仍然需要 kube_pod_labels 中的 app 标签,那就用 labelkeep 只保留 app

metric_relabel_configs:
  - regex: '__name__|instance|job|app'
    action: labelkeep

注意:__name__instancejob 是 Prometheus 内置标签,如果你 labelkeep 时没写它们,这些标签会被删除——导致指标无法识别。所以务必保留至少 __name__instancejob

4.4 验证配置是否正确

使用 promtool check config prometheus.yml 检查语法。然后用 promtool test rules 测试告警规则是否还正常。更直观的方法是修改后观察 Prometheus target 页面(/targets)看 UP 状态,以及用 count by (__name__)({__name__=~"kube_pod_labels"}) 检查系列数是否降低。

5. 必须注意的风险与回滚与High

实际操作要点

Relabeling 是“外科手术”,切错了可能让关键监控失灵。

  • 误删必要标签:上面提过,注意保留 __name__jobinstance。此外,如果告警规则依赖某个 label,千万别 drop 或 labelkeep 去掉它。
  • 回滚方案:每次修改前备份 prometheus.yml。如果改动后问题扩大(比如所有 target 都 down了),立刻拿出备份文件并 reload。也可以用版本管理工具(如 git)记录每次变化。
  • 增量验证:不要一次性对 所有 job 应用同一个 metric_relabel_configs。先在一个测试 job 上试,确认 OK 再推广。
  • 防止空指标:如果你用 drop 动作,确保正则匹配精确,不要一棍子打死同类指标。例如 kube_pod_labelskube_pod_info,别写成 kube_pod.* 然后误杀。

6. 总结:relabeling 是 Prometheus 高基数场景的标配技能

实际操作要点

不要迷信“加机器能解决一切”。不加控制的高基数即使扩到 128GB 内存也照样崩。学会用 metric_relabel_configs 中的 droplabelkeeplabelmap,你可以从源头减少 80% 以上的时间序列。关键是先定位元凶,再逐步收紧策略,验证一步走一步。

对小白来说,最直接的上手路径:找到系列数最高的那个指标,在对应的 scrape job 里加一个 metric_relabel_configs,用 drop 先扔掉它(同时备份配置)。观察内存下降后,再改造成 labelkeep 只保留你真正需要的标签。

Prometheus 的 relabeling 机制并不难,难的是意识到“哪些数据可以丢”。下次再遇到内存告警,先查高基数指标,再动手 relabel——比你盲目加机器有效得多。把这些步骤跑通后,High基本就能稳定落地。

延伸阅读