将原生直方图发送到 Mimir
注意
原生直方图是 Grafana Mimir 的实验性功能。
Prometheus 原生直方图是 Prometheus 生态系统中的一种数据类型,可以生成、存储和查询高分辨率的观测值直方图。
原生直方图与经典 Prometheus 直方图在许多方面有所不同
- 原生直方图 bucket 边界由一个取决于原生直方图的 scale(分辨率)的公式计算得出,而不是用户定义的。该计算会产生指数级增长的 bucket 边界。有关详细信息,请参阅Bucket 边界计算。
- 如果观测值产生的 bucket 过多,原生直方图 bucket 边界可能会动态变化(变宽)。有关详细信息,请参阅限制 Bucket 数量。
- 原生直方图 bucket 计数器仅计算 bucket 边界内的观测值,而经典直方图 bucket 仅具有一个称为
le
的上限,并计算 bucket 内及所有更低 bucket 中的所有观测值(累积)。 - 原生直方图指标实例仅需要一个时间序列,因为 bucket、观测值总和和观测值计数存储在一个称为
native histogram
的数据类型中,而不是使用float
数据类型存储在单独的时间序列中。因此,没有<metric>_bucket
、<metric>_sum
和<metric>_count
序列。只有一个<metric>
时间序列。 - 通过 Prometheus 查询语言 (PromQL) 查询原生直方图使用不同的语法。有关详细信息,请参阅可视化原生直方图和函数。
有关原生直方图的介绍,请观看Prometheus 中的原生直方图演示文稿。
优点和缺点
与经典 Prometheus 直方图相比,使用原生直方图有优点也有缺点。有关更多信息和实际示例,请参阅生产环境中的 Prometheus 原生直方图视频。
优点
- 更简单的检测:您无需考虑 bucket 边界,因为它们是自动创建的。
- 实践中分辨率更高:自定义 bucket 布局通常分辨率不高。
- 原生直方图彼此兼容:它们具有自动布局,易于组合。
注意
操作可能将一个操作数降级到较低分辨率以匹配另一个操作数。
缺点
- 观测值的分布方式可能不适合指数 bucket 模式,例如以分贝为单位测量的声压,它本身就是对数形式的。
- 如果从具有特定 bucket 边界的外部表示直方图进行转换,通常与原生直方图的 bucket 边界不精确匹配,此时需要使用插值。
- 无法设置任意的 bucket 边界,例如对 SLO 定义特别重要的边界。通常,高于或低于给定阈值的观测值比例必须通过插值进行估计,而经典直方图在给定阈值处配置了 bucket 边界则可以精确计算。
上述问题可以通过高分辨率来缓解,原生直方图可以以比经典直方图低得多的资源成本提供高分辨率。
使用 Prometheus 客户端库进行应用检测
以下示例提供了一些合理的默认值来定义新的原生直方图指标。这些示例使用了 Go 客户端库 1.16 版本和 Java 客户端库 1.0 版本。
注意
可以将原生直方图选项添加到现有经典直方图,以便同时获得经典直方图和原生直方图。请参阅从经典直方图迁移。
histogram := prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "request_latency_seconds",
Help: "Histogram of request latency in seconds",
NativeHistogramBucketFactor: 1.1,
NativeHistogramMaxBucketNumber: 100,
NativeHistogramMinResetDuration: 1*time.Hour,
})
static final Histogram requestLatency = Histogram.build()
.name("requests_latency_seconds")
.help("Histogram of request latency in seconds")
.nativeOnly()
.nativeInitialSchema(3)
.nativeMaxNumberOfBuckets(100)
.nativeResetDuration(1, TimeUnit.HOURS)
.register();
在 Go 中,NativeHistogramBucketFactor
选项设置了从一个 bucket 到下一个 bucket 相对增长的上限。值 1.1 意味着一个 bucket 的宽度最多比下一个较小的 bucket 宽 10%。目前支持的值范围从 1.0027
或 0.27% 到 65536 或 655%。有关更详细的解释,请参阅Bucket 边界计算。
系数 1.1
舍入到两位小数后的一些结果 bucket 如下
…, (0.84, 0.92], (0.92, 1], (1, 1.09], (1.09, 1.19], (1.19, 1.30], …
…, (76.1, 83], (83, 91], (91, 99], …
…, (512, 558], (558, 608], (608, 663], …
在 Java 中,使用 schema 值 3
的 .nativeInitialSchema
会产生相同的 bucket 边界。有关 Java 中支持的 schema 的更多信息,请参阅 nativeInitialSchema 的文档。
NativeHistogramMaxBucketNumber
/nativeMaxNumberOfBuckets
的值限制了观测值产生的 bucket 数量。如果接收端限制了可发送的 bucket 数量,这一点尤其有用。有关 bucket 限制的更多信息,请参阅限制 Bucket 数量。
NativeHistogramMinResetDuration
/nativeResetDuration
中的持续时间将禁止在此期间自动重置计数器。计数器重置与 bucket 限制相关,有关更多信息,请参阅限制 Bucket 数量。
使用 Prometheus 抓取和发送原生直方图
使用 Prometheus 2.47 或更高版本。
要启用从应用程序抓取原生直方图,您需要在命令行中通过 feature flag 启用原生直方图功能
prometheus --enable-feature=native-histograms
此 flag 使 Prometheus 检测并抓取原生直方图,并忽略同时定义了原生直方图的指标的经典直方图版本。没有定义原生直方图的经典直方图不受影响。
要继续抓取原生直方图指标的经典直方图版本,您需要在 scrape 作业中将
always_scrape_classic_histograms
设置为true
。注意
在 Prometheus 3.0 之前的版本中,
always_scrape_classic_histograms
设置称为scrape_classic_histograms
。请使用与您使用的 Prometheus 版本对应的设置名称。例如,要同时获取经典直方图和原生直方图,请使用以下配置
scrape_configs: - job_name: myapp always_scrape_classic_histograms: true
注意
原生直方图在应用程序的
/metrics
端点没有文本表示。因此,Prometheus 在这些情况下会协商 Protobuf 协议传输。注意
在某些情况下,Protobuf 解析会改变传统直方图的
le
标签和 summary 的quantile
标签的数字格式。通常,如果抓取目标使用 client_golang 进行检测,并且 promhttp.HandlerOpts.EnableOpenMetrics 设置为false
,就会发生这种情况。在这种情况下,整数标签值表示为quantile="1"
或le="2"
,省略了小数部分。但是,Protobuf 解析会将表示形式更改为始终包含小数部分(遵循 OpenMetrics 规范),因此上述示例在摄入 Prometheus 后变为quantile="1.0"
和le="2.0"
。这会改变指标的身份,使其与最初摄入的身份不同。有关更多信息,请参阅 Prometheus 文档中的Feature Flags Native Histograms。
要能够将原生直方图发送到兼容 Prometheus remote write 的接收端,例如 Grafana Cloud Metrics、Mimir 等,请在 remote write 配置中将
send_native_histograms
设置为true
,例如remote_write: - url: http://.../api/prom/push send_native_histograms: true
使用 Grafana Alloy 抓取和发送原生直方图
使用最新版本的Grafana Alloy。
要抓取原生直方图,请在
prometheus.scrape
组件中设置scrape_protocols
参数,将PrometheusProto
指定为第一个协商协议。scrape_protocols = ["PrometheusProto", "OpenMetricsText1.0.0", "OpenMetricsText0.0.1", "PrometheusText0.0.4"]
除了原生直方图外,要抓取经典直方图,请将
always_scrape_classic_histograms
设置为true
。注意
在 Prometheus 3.0 之前的版本中,
always_scrape_classic_histograms
设置称为scrape_classic_histograms
。请使用与您使用的 Prometheus 版本对应的设置名称。scrape_protocols = ["PrometheusProto", "OpenMetricsText1.0.0", "OpenMetricsText0.0.1", "PrometheusText0.0.4"] always_scrape_classic_histograms = true
有关更多信息,请参阅 Grafana Alloy 文档中的prometheus.scrape。
注意
在某些情况下,Protobuf 解析会改变传统直方图的
le
标签和 summary 的quantile
标签的数字格式。通常,如果抓取目标使用 client_golang 进行检测,并且 promhttp.HandlerOpts.EnableOpenMetrics 设置为false
,就会发生这种情况。在这种情况下,整数标签值表示为quantile="1"
或le="2"
,省略了小数部分。但是,Protobuf 解析会将表示形式更改为始终包含小数部分(遵循 OpenMetrics 规范),因此上述示例在摄入 Prometheus 后变为quantile="1.0"
和le="2.0"
。这会改变指标的身份,使其与最初摄入的身份不同。有关更多信息,请参阅 Prometheus 文档中的Feature Flags Native Histograms。
要将原生直方图发送到兼容 Prometheus remote write 的接收端,例如 Grafana Cloud Metrics 或 Mimir,请在
prometheus.remote_write
组件中将send_native_histograms
参数设置为true
。例如prometheus.remote_write "mimir" { endpoint { url = "http://.../api/prom/push" send_native_histograms = true } }
从经典直方图迁移
为了简化迁移过程,您可以保留经典直方图的自定义 bucket 定义,同时添加原生直方图 bucket。
将原生直方图定义添加到检测中的现有直方图。
如果现有直方图没有定义 bucket,请添加默认 bucket 以保留经典直方图。
同一指标同时定义经典直方图和原生直方图的代码示例
histogram := prometheus.NewHistogram( prometheus.HistogramOpts{ Name: "request_latency_seconds", Help: "Histogram of request latency in seconds", Buckets: prometheus.DefBuckets, // If buckets weren't already defined. NativeHistogramBucketFactor: 1.1, NativeHistogramMaxBucketNumber: 100, NativeHistogramMinResetDuration: 1*time.Hour, })
static final Histogram requestLatency = Histogram.build() .name("requests_latency_seconds") .help("Histogram of request latency in seconds") .classicUpperBounds(Histogram.Builder.DEFAULT_CLASSIC_UPPER_BOUNDS) // If upper bounds weren't already defined. .nativeInitialSchema(3) .nativeMaxNumberOfBuckets(100) .nativeResetDuration(1, TimeUnit.HOURS) .register();
让 Prometheus 或 Grafana Alloy 抓取同时定义了经典直方图和原生直方图的指标。
将原生直方图以及现有经典直方图发送到 remote write。
修改仪表盘以使用原生直方图指标。有关详细信息,请参阅可视化原生直方图。
使用以下策略之一更新仪表盘。
(推荐)使用新的原生直方图查询添加新的仪表盘。此解决方案需要查看迁移前后的不同仪表盘,直到迁移前的数据因超过保留时间而被移除。当有足够的时间为用户提供新数据时,您可以发布新仪表盘。
在仪表盘中添加仪表盘变量,以启用在经典直方图和原生直方图之间切换。Grafana 不支持选择性地启用和禁用查询(问题 79848)。作为一种变通方法,例如添加仪表盘变量
latency_metrics
,并为其分配值-1
或1
。然后,将以下两个查询添加到面板(<classic_query>) and on() (vector($latency_metrics) == 1)
(<native_query>) and on() (vector($latency_metrics) == -1)
其中
classic_query
是原始查询,native_query
是使用原生直方图查询语法并放在括号中的相同查询。Mimir 仪表盘使用此技术。例如,请参阅 Mimir 代码库中的Overview 仪表盘。此解决方案允许用户在经典直方图和原生直方图之间切换,而无需转到不同的仪表盘。
将现有经典查询替换为修改后的查询。例如,替换
<classic_query>
为
<native_query> or <classic_query>
其中
classic_query
是原始查询,native_query
是使用原生直方图查询语法的相同查询。警告
使用 PromQL 运算符
or
可能会导致意料之外的结果。例如,如果查询使用七天的时间范围,例如sum(rate(http_request_duration_seconds[7d]))
,那么一旦在查询指定结束时间之前存在两个原生直方图样本,该查询就会返回值。在这种情况下,七天内的速率是从几分钟(而不是七天)的数据计算得出的。这导致您开始抓取原生直方图时图表附近出现不准确的情况。
开始添加新记录规则和警报以使用原生直方图。此时请勿移除旧的记录规则和警报。
重要的是,至少在您的记录规则和警报中最长范围的时间段内(再加一天)持续抓取经典直方图和原生直方图。这是最短时间,但建议持续抓取这两种数据类型,直到可以验证新规则和警报为止。
例如,如果您有一个计算请求速率的警报,例如
sum(rate(http_request_duration_seconds[7d]))
,此查询会查看过去七天的数据以及 Prometheus 回溯周期。当您开始发送原生直方图时,没有完整的七天数据,因此,结果可能不可靠,无法用于警报。配置原生直方图采集后,选择以下任一方式停止采集经典直方图。
- 从检测中移除自定义 bucket 定义
Buckets
/classicUpperBounds
。在 Java 中,还要使用nativeOnly()
选项。请参阅使用 Prometheus 客户端库进行应用检测中的示例。 - 在抓取时使用 Prometheus relabeling 或 Grafana Alloy prometheus.relabel 丢弃经典直方图序列。
- 停止抓取指标的经典直方图版本。此选项适用于抓取目标的所有指标。
- 从检测中移除自定义 bucket 定义
通过删除规则或警报的经典直方图版本来清理记录规则和警报。
Bucket 边界计算
本节假设您熟悉基本代数。原生直方图 bucket 边界是根据以 2 为底的指数公式计算得出的。
原生直方图样本有三种不同类型的 bucket,对于任何观测值,该值都会计入其中一种 bucket。
- 一个零 bucket,其中包含绝对值小于或等于零阈值的观测值计数。
- 正 bucket,其中包含正观测值计数,这些观测值大于 bucket 下限且小于或等于上限。
其中 index 可以是正整数或负整数,导致边界大于 1 或小于 1。schema 可以在检测时直接从 [-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
中指定,或者它是从列表中选择的最大数字,使得
例如,对于因子 1.1
schema 与因子对照表
schema | 因子 | schema | 因子 | |
---|---|---|---|---|
-4 | 65536 | 3 | 1.0905 | |
-3 | 256 | 4 | 1.0443 | |
-2 | 16 | 5 | 1.0219 | |
-1 | 4 | 6 | 1.0109 | |
0 | 2 | 7 | 1.0054 | |
1 | 1.4142 | 8 | 1.0027 | |
2 | 1.1892 |
- 负 bucket,其中包含负观测值计数,这些观测值小于 bucket 上限且大于或等于下限。
其中 schema
如上所述选择。
限制 bucket 数量
通过 remote write 抓取或接收原生直方图的服务器可能会限制其接受的原生直方图 bucket 数量。服务器可能会拒绝或降级(降低分辨率并合并相邻 bucket)。即使不限制,存储和发送可能无限数量的 bucket 也不切实际。
Prometheus 的检测库具有自动化功能,可以在使用最大 bucket 数量选项(例如 Go 中的 NativeHistogramMaxBucketNumber
)时减少 bucket 数量。
超过设定的最大值后,将执行以下策略
首先,如果直方图的最后一次重置(或创建)距今至少是最小重置持续时间,则整个直方图将重置为其初始状态(包括经典 bucket)。这仅在设置了最小重置持续时间(Go 中的
NativeHistogramMinResetDuration
)时才有效。如果经过的时间较短,或者最小重置持续时间为零,则不执行重置。相反,零阈值会充分增加,以将 bucket 数量减少到小于或等于最大 bucket 数量,但不超过最大零阈值(Go 中的
NativeHistogramMaxZeroThreshold
)。因此,如果阈值已经达到或超过最大阈值,则此步骤不会发生任何变化。之后,如果 bucket 数量仍超过最大 bucket 数量,则会通过将所有 bucket 的宽度加倍来降低直方图的分辨率(直至相邻 bucket 之间的增长因子达到 2^(2^4) = 65536,请参阅Bucket 边界计算)。
一旦超过最小重置持续时间(自上次重置或直方图创建以来),任何增加的零阈值或降低的分辨率都会重置回其原始值。