很多刚接触 Prometheus 的小伙伴在优化监控时,都会遇到一个令人困惑的"玄学"问题:
"我明明在配置文件里写了要丢弃
go_gc_duration_seconds这个指标,为什么重载配置后,它依然活蹦乱跳地出现在数据库里?"
检查配置语法,没错;重启服务,没用。
原因只有一个:你放错了位置。
你把针对 指标名称(__name__) 的过滤规则,写在了 relabel_configs 里,而它其实应该待在 metric_relabel_configs 里。
今天这篇博文,我们就来彻底搞懂这两个配置块的区别,帮你避开这个 90% 的新手都会踩的坑。
🕵️♂️ 核心误区:把"事后清洗"当成了"事前拦截"
Prometheus 的抓取过程并不是"一蹴而就"的,它分为两个截然不同的阶段。理解这两个阶段,是掌握 Relabeling 的关键。
阶段一:目标发现与准备 (relabel_configs)
- 时间点 :Prometheus 刚刚通过 K8s、DNS 或静态列表发现了一个目标(比如
192.168.1.5:9100),正准备发起 HTTP 请求之前。 - 手里有什么 :此时,Prometheus 手里只有目标的元数据(地址、端口、Pod 名字、节点标签等)。
- 手里没有什么 :它还没有拿到任何具体的指标数据! 它根本不知道这个目标会暴露
http_requests_total还是go_gc_duration_seconds。 - 结论 :在这个阶段,
__name__(指标名)这个标签甚至还不存在。你试图根据一个不存在的值去过滤,自然是无效的。
阶段二:数据解析与入库 (metric_relabel_configs)
- 时间点 :Prometheus 已经成功连接目标,下载了
/metrics接口的所有文本数据,正在逐行解析,准备存入内存之前。 - 手里有什么 :此时,每一行数据都被解析成了样本,拥有了明确的指标名(
__name__)、标签集(Labels)和数值(Value)。 - 能做什么 :现在 Prometheus 终于知道"哦,这一行是
go_gc_duration_seconds"。你可以基于此决定:"扔掉这一行" 或者 "删掉这一行的某个标签"。 - 结论 :只有在这个阶段,基于
__name__的过滤才会生效。
❌ 错误示范:为什么你的配置不生效?
看看下面这段配置,是不是觉得很眼熟?
yaml
scrape_configs:
- job_name: 'my-java-app'
static_configs:
- targets: ['localhost:8080']
# ⚠️ 错误位置!这里还没有指标数据,__name__ 是空的
relabel_configs:
- source_labels: [__name__]
regex: 'go_.*'
action: drop
发生了什么?
- Prometheus 发现
localhost:8080。 - 进入
relabel_configs阶段。它尝试读取__name__,发现是空的(或者未定义)。 - 正则匹配失败(或者不匹配),
action: drop不触发。 - Prometheus 继续发起 HTTP 请求,抓取所有数据。
- 数据解析完成,所有
go_开头的指标被原封不动地存入数据库。
结果:过滤失败,内存继续爆炸。
✅ 正确姿势:把刀用在刀刃上
要过滤具体的指标(如 go_.*, jvm_.*),必须使用 metric_relabel_configs。
yaml
scrape_configs:
- job_name: 'my-java-app'
static_configs:
- targets: ['localhost:8080']
# ✅ 正确位置!此时指标已解析,__name__ 有值
metric_relabel_configs:
- source_labels: [__name__]
regex: 'go_.*'
action: drop
# 顺便再丢弃一些没用的 JVM 指标
- source_labels: [__name__]
regex: 'jvm_gc_pause_seconds.*'
action: drop
现在的流程:
- 抓取数据。
- 解析出一行
go_gc_duration_seconds{...} 0.05。 - 进入
metric_relabel_configs。 - 发现
__name__是go_gc_duration_seconds,匹配正则go_.*。 - 触发
action: drop,直接丢弃该行数据,不写入内存,不写入磁盘。
结果:完美过滤,资源节省。
🆚 深度对比:两张表看懂区别
为了让你以后不再混淆,请保存这张对比表:
| 特性 | relabel_configs |
metric_relabel_configs |
|---|---|---|
| 执行时机 | 抓取前 (Before Scrape) | 抓取后,存储前 (After Scrape) |
| 处理对象 | Target (目标) | Sample (样本/指标行) |
__name__ 可用吗? |
❌ 不可用 (此时还没抓到数据) | ✅ 可用 (数据已解析) |
| 主要用途 | 1. 筛选要抓取的目标 (如只抓特定 Pod)2. 重命名发现标签 (如 __meta_... -> pod)3. 构造静态标签 |
1. 过滤具体指标 (Drop/Keep __name__)2. 删除高基数标签 (LabelDrop)3. 修改样本值 |
| 性能优势 | ⭐⭐⭐⭐⭐ (省去了网络请求和解析开销) | ⭐⭐⭐ (省去了存储和索引开销,但已消耗带宽) |
| 典型场景 | "我只需要抓取名为 web-server 的 Pod" |
"我不需要 go_gc 指标" 或 "删掉 trace_id 标签" |
💡 进阶技巧:什么时候用 relabel_configs 更优?
虽然 relabel_configs 不能过滤 __name__,但它在筛选目标方面效率更高。
场景 :你有 1000 个 Pod,但你只想监控其中名字包含 production 的 100 个 Pod。
- 做法 A (错误) :在
metric_relabel_configs里抓取所有 1000 个 Pod 的数据,然后根据指标过滤?不行,因为这是针对 Target 的筛选,不是针对指标的。 - 做法 B (正确且高效) :在
relabel_configs中直接丢弃那 900 个不需要的 Target。
yaml
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
# 在发起任何 HTTP 请求之前,直接丢弃非 production 的 Pod
# 这样连网络带宽和 CPU 解析都省下了!
- source_labels: [__meta_kubernetes_pod_name]
regex: '.*production.*'
action: keep
最佳实践总结:
- 先粗筛 :用
relabel_configs决定**"抓谁"**(基于 Pod 名、IP、Namespace 等元数据)。 - 后精洗 :用
metric_relabel_configs决定**"存什么"**(基于__name__指标名、具体标签值)。
🔍 如何验证你的配置?
改完配置别盲目自信,用这两招验证:
-
语法检查(必做):
bashpromtool check config prometheus.yml确保没有 YAML 格式错误。
-
查询验证 :
重载配置后,去 Prometheus UI 的 Graph 页面:
- 如果你过滤了
go_.*,查询count({__name__=~"go_.*"})。如果返回0或No data,恭喜成功! - 如果你删除了标签,查询该指标并查看 Table 视图,确认该标签是否消失。
- 如果你过滤了
🎉 结语
Prometheus 的配置看似复杂,其实逻辑非常清晰:
relabel_configs是门卫,决定谁可以进门(Target)。metric_relabel_configs是安检员,决定进门的人身上哪些东西可以带进去(Metrics & Labels)。
下次再想过滤指标名时,记得找安检员 (metric_relabel_configs),别在门口(relabel_configs)白费功夫了!
希望这篇博文能帮你解决疑惑。如果觉得有用,欢迎点赞收藏,转发给那些还在为 Prometheus 内存溢出头疼的运维同事!