Prometheus 高基数治理:把 120 万 series 压到 18 万
一、现象:内存 3 天涨到 28G
一套 6 节点 Kubernetes 集群的 Prometheus 在升级业务后出现三个症状:进程 RSS 三天从 6G 涨到 28G;rate(http_request_duration_seconds_count[5m]) 查询 P99 从 300ms 恶化到 11s;TSDB 压缩任务持续超过 40 分钟,导致 prometheus_tsdb_compaction_chunk_range_seconds 报警。
排查第一步永远不是加内存,而是数 series。
prometheus_tsdb_head_series # 当前活跃 series 数
scrape_samples_scraped # 每个 target 抓到的样本数
实测 head_series 达到 121 万,其中 68% 来自 3 个业务 target。
二、定位:谁在贡献基数
按 job 拆解基数,一条 PromQL 就能定位元凶:
topk(10, sum by (job) ({__name__=~".+", job=~".+"}))
更精确的写法是统计每个 job 下不同 label 组合的数量:
topk(10, count by (job) (group by (job, __name__, instance, path, status) ({__name__=~".+"})))
本次结果:java-app 贡献 41 万,gateway 贡献 27 万,batch-worker 贡献 14 万。根因分三类——URL 路径带 ID、错误信息进 label、客户端自造高基数标签。
三、治理四招
1. relabel 丢弃无用指标
先在抓取层砍掉业务方「顺手埋点」的指标,用 metric_relabel_configs 白名单式保留:
metric_relabel_configs:
- source_labels: [__name__]
regex: '(jvm_.*|http_request_duration_seconds.*|process_.*|node_.*)'
action: keep
- source_labels: [path]
regex: '/order/[0-9]+'
target_label: path
replacement: '/order/:id'
action: replace
关键在第二条:把动态路径正则归一化,这一步单独就砍掉 19 万 series。
2. 干掉「错误信息当 label」
典型反例是 error_msg="java.net.SocketTimeoutException: Read timed out after 3000ms at ..."。错误消息每行都不同,等于每条错误创建一个新 series。
正确做法是只留错误类型,把明细打到日志:
- source_labels: [error_msg]
regex: '.*(Timeout|Refused|Reset|Denied).*'
target_label: error_type
replacement: '$1'
action: replace
- regex: 'error_msg'
action: labeldrop
3. histogram 分桶改造
默认 prometheus.DefBuckets 有 12 个桶,一个接口就产生 12 个 series;接口维度 × 实例维度 × 方法维度相乘后基数暴涨。改造原则是桶数够用即止,按 SLO 设边界:
// 原来 12 桶,改为 7 桶
DefBuckets = []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5}
对延迟差异大的接口按业务分组使用 HistogramOpts.Buckets,别一套桶走天下。这一项降低 11 万 series。
4. recording rule 预聚合
看板上高频查询的 rate(xxx[5m]) 系列,提前算成聚合指标,把「逐条查询」变成「查一张表」:
groups:
- name: api-aggregation
interval: 30s
rules:
- record: job:http_request_duration_seconds:p95
expr: histogram_quantile(0.95, sum by (job, le) (rate(http_request_duration_seconds_bucket[5m])))
- record: job:http_requests:rate5m
expr: sum by (job) (rate(http_request_requests_total[5m]))
查询耗时从 11s 降到 180ms,同时 TSDB 只需要保留聚合后的低基数结果。
四、效果与固化
| 指标 | 治理前 | 治理后 |
|---|---|---|
| 活跃 series | 121 万 | 18 万 |
| RSS 内存 | 28 GB | 7.2 GB |
| rate 查询 P99 | 11 s | 180 ms |
| 压缩耗时 | 40 min+ | 6 min |
最后把「基数」纳入上线准入:CI 里跑一次 promtool check metrics,业务新增指标必须过基数评审;同时给 scrape_samples_scraped 配阈值告警,单 target 超过 5000 样本立即通知。
基数是设计问题,不是容量问题。加内存只能推迟爆掉的时间,砍基数才能根治。
- 点赞
- 收藏
- 关注作者
评论(0)