必须立即定位并切断高基数指标源头。先通过日志和内存监控确认oom由基数爆炸引发,再用prometheus查询识别高基数指标及标签,最后通过relabel_configs丢弃或归一化标签,并启用max-series-per-metric等参数硬限防护。

当Prometheus因监控指标基数爆炸(Cardinality Explosion)持续吃光内存、触发OOM Killer强制杀进程时,必须立即定位高基数源头并切断数据写入路径,不能等它把节点内存耗尽再处理。
确认是否为基数爆炸引发的OOM
登录Prometheus所在节点,执行kubectl logs prometheus-k8s-0 -n monitoring | grep -i "out of memory\|oom",若日志末尾出现level=error msg="Scrape failed" err="context deadline exceeded"或reason=OOMKilled,且kubectl top pod -n monitoring显示该Pod内存使用长期高于8Gi(未设limits时会持续上涨),基本可锁定为基数问题而非瞬时抓取超时。
这一步不能跳过:如果Pod刚重启不久就再次OOM,说明问题在数据源头,不是临时抖动。
快速识别高基数指标和标签
在Prometheus Web UI的/graph页面中,依次执行以下查询:
① 查总活跃序列数:prometheus_tsdb_head_series,观察值是否稳定在50万以上且持续爬升;
② 查各指标序列数量:count by (__name__) ({__name__=~".+"}),重点关注返回值超过1万的指标,如http_request_duration_seconds_bucket、http_requests_total、go_goroutines;
③ 对高嫌疑指标查其爆炸性标签值数量:count by (url) (http_requests_total) 或 count by (path) (http_request_duration_seconds_bucket),若单个标签值数量达数千甚至上万,就是罪魁祸首;
【注意:不要直接在生产环境跑label_values(__name__, "url")这类全量枚举,可能拖垮Prometheus自身】
从scrape配置端做实时拦截
方法一:用relabel_configs硬性丢弃高风险标签
编辑Prometheus配置文件中对应job的relabel_configs段,在scrape_config下添加:
- source_labels: [url] action: labeldrop- source_labels: [user_id] action: labeldrop- source_labels: [request_id] action: labeldrop
方法二:对必须保留的路径做正则归一化
将动态ID路径统一替换为占位符,避免每条请求生成新序列:
- source_labels: [path] regex: "/api/v1/users/[0-9]+/posts/[0-9]+" replacement: "/api/v1/users/:id/posts/:post_id" target_label: path
这一步生效后,/api/v1/users/123/posts/456和/api/v1/users/789/posts/012将被合并为同一条时间序列。
启用硬性防护策略止血
在Prometheus启动命令中加入以下参数,防止某次配置遗漏导致再次失控:
--storage.tsdb.max-series-per-metric=50000:单个指标最多允许5万条序列,超限后直接拒绝写入并记录warn日志;
--scrape.sample-limit=100000:v2.15+版本支持,单次抓取样本数硬限10万,超出部分静默丢弃;
--web.enable-admin-api:开启管理API,后续可调用/api/v1/admin/tsdb/delete_series清理已确认无用的系列(需配合时间范围与匹配器)。
【关键前提:max-series-per-metric必须配合--storage.tsdb.retention.time=14d等合理保留策略,否则旧序列不释放,新限制形同虚设】











