Prometheus 的几个常见问题和错误用法

巴辉特 2025-02-26 10:40:31

夜莺有十几个交流群,经常会在群里看到一些奇奇怪怪的问题,有些问题是由于对 Prometheus 的理解不够深入导致的,有些问题是由于错误的用法导致的。本文总结了一些常见的问题和错误用法,希望能帮助大家更好地使用 Prometheus。

Prometheus 中昨天的峰值今天查看就看不到了

这个问题是因为 Prometheus 查询时 step 参数设置不合理导致的,具体可以参考前两天的文章《Prometheus 昨天的峰值今天就看不到了,这监控不准啊》,不再赘述。

Grafana 中的图表,rate 函数写死了时间范围

这个问题是因为 rate 函数的时间范围写死了,导致图表的时间范围变化时,rate 函数的时间范围不变,从而导致图表的数据出现断点,比如下面的 promql:

rate(node_cpu_seconds_total{mode="idle"}[5m]) * 100

这个 promql 使用的时间范围是 5m,如果查看最近 1h 的数据或者 2h 的数据看起来都是正常的,但是如果要查看 7d 或 30d 的数据,Grafana 自动计算的 step 远大于 5m,导致 rate 函数的时间范围内经常没有数据,导致图表出现断点。

最佳实践是使用 $__rate_interval 变量,这个变量会根据用户所选时间范围自动计算 rate 函数的时间范围,通常来讲是 4 倍的 step 值,确保 rate 函数的时间范围内总能找到数据点。

如果是使用夜莺的仪表盘,也是一样的道理,为了降低用户迁移成本,夜莺的仪表盘也会使用 $__rate_interval 变量,和 Grafana 保持一致。

标签值不可枚举,导致高基数问题

在 Prometheus 生态中,每个指标的唯一标识就是一堆 labels(指标名也是一个特殊的 label,其 label key 是 __name__),如果某个指标,其 labels 不同,就会被当做两个指标来处理,比如下面的指标:

http_requests_total{method="GET", handler="/user/1"} 100
http_requests_total{method="GET", handler="/user/2"} 200

method 表示 HTTP 请求的方法,这个是可以枚举的,一般就是 GET、POST、PUT、DELETE 等,但是上例中的 handler 是一个 URL 路径,其中的数字表示 userid,这个是不可枚举的,因为 user 的量会越来越大,导致 handler 标签的 label value 越来越多,导致指标数量越来越多,最终影响 Prometheus 性能,这就是高基数问题。

对于现成的各类 Exporter(尤其是 star 数量较高的),通常不会有这个问题,因为写 Exporter 代码的人,通常是了解高基数的危害的。高基数问题通常出自那些自行使用 Prometheus SDK 埋点的新手。

仅就上例 handler 这个 label 而言,最佳实践是把 urlpath 中的 userid 替换成变量表示,而非直接使用具体的数字,比如:

http_requests_total{method="GET", handler="/user/:userid"} 300

通常来讲,userid、orderid、phone、ip、email、traceid、requestid 等信息如果放到 label value 里,大概率会引发高基数问题。这些高基数需求,可以尝试换个存储,比如使用 ClickHouse 这类 OLAP 数据库来做统计分析。

重复上报数据导致问题

大家应该都知道 rate 函数用于 Counter 类型的数据。有时我们可能会看到某个 rate 的结果巨大无比,比如网络流量、IO Bytes 等出现一些巨大无比的值。

这个问题通常是由于数据重复上报导致的,比如我在夜莺的即时查询中使用 Table 视图查看某个 Counter 的 range query 结果如下:

这里可以看到数据上报的时间差稳定在 15s,说明只有一个采集器在上报数据,是正常的。此时我又重复启动了一个采集器,再次查询,结果如下:

从时间戳的差值上来看,间隔已经不是原本预期的 15s 了,而是一会是一个值,一会又是另一个值,交叉出现,很规律。这就是典型的数据重复上报。

对于我这个测试场景而言,重复上报数据问题不大,因为我是在一个机器上启动了两个采集器,但是,如果是在不同机器上启动了两个采集器(其 hostname 相同)就麻烦了,一个机器可能启动了较长时间,其 Counter 值已经很大了,另一个机器启动时间较短,其 Counter 值较小,这样就会导致 rate 函数的结果巨大无比,大家想想是为啥?

注意 without 和 by

如果你使用 Node-Exporter 采集机器监控数据,那你一定知道 node_cpu_seconds_total 这个指标,这个指标表示 CPU 时间,单位是秒,Counter 类型,我查一个例子给你看看:

这个指标有 4 个 label:

  • cpu:CPU 编号,从 0 开始
  • instance:Exporter 的地址
  • job:Job 名称
  • mode:CPU 时间类型,比如 idle、system、user 等,我这里只查询了 idle

OK,现在我想计算整机 CPU 利用率,应该怎么做?典型的做法如下:

avg(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100

但是这样一来,结果中只有 instance 标签了,我还想要 job 标签怎么办?OK,那就加上 job 标签:

avg(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance, job) * 100

但是有些公司会打上更多自定义标签,比如 env、region 等,上面的 promql 会把 env、region 等抹掉,不通用,这时候就可以使用 without 关键字:

avg without (mode,cpu) ( 1 - rate(node_cpu_seconds_total{mode="idle"}[5m]) ) * 100

这样一来,只有 mode 和 cpu 两个 label 被抹掉,其他 label 都保留了。

总结

还有哪些坑?欢迎大家留言补充哈。

标签: Prometheus
快猫星云 联系方式 快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云 联系方式
快猫星云
OpenSource
开源版
Flashcat
Flashcat