菜单
开源 RSS

将原生直方图发送到 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 版本。

注意

可以将原生直方图选项添加到现有经典直方图,以便同时获得经典直方图和原生直方图。请参阅从经典直方图迁移

Go
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,
})
java
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 或更高版本。

  1. 要启用从应用程序抓取原生直方图,您需要在命令行中通过 feature flag 启用原生直方图功能

    bash
    prometheus --enable-feature=native-histograms

    此 flag 使 Prometheus 检测并抓取原生直方图,并忽略同时定义了原生直方图的指标的经典直方图版本。没有定义原生直方图的经典直方图不受影响。

  2. 要继续抓取原生直方图指标的经典直方图版本,您需要在 scrape 作业中将 always_scrape_classic_histograms 设置为 true

    注意

    在 Prometheus 3.0 之前的版本中,always_scrape_classic_histograms 设置称为 scrape_classic_histograms。请使用与您使用的 Prometheus 版本对应的设置名称。

    例如,要同时获取经典直方图和原生直方图,请使用以下配置

    yaml
    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

  3. 要能够将原生直方图发送到兼容 Prometheus remote write 的接收端,例如 Grafana Cloud Metrics、Mimir 等,请在 remote write 配置中将 send_native_histograms 设置为 true,例如

    yaml
    remote_write:
      - url: http://.../api/prom/push
        send_native_histograms: true

使用 Grafana Alloy 抓取和发送原生直方图

使用最新版本的Grafana Alloy

  1. 要抓取原生直方图,请在 prometheus.scrape 组件中设置 scrape_protocols 参数,将 PrometheusProto 指定为第一个协商协议。

     scrape_protocols = ["PrometheusProto", "OpenMetricsText1.0.0", "OpenMetricsText0.0.1", "PrometheusText0.0.4"]
  2. 除了原生直方图外,要抓取经典直方图,请将 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

  3. 要将原生直方图发送到兼容 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。

  1. 将原生直方图定义添加到检测中的现有直方图。

  2. 如果现有直方图没有定义 bucket,请添加默认 bucket 以保留经典直方图。

    同一指标同时定义经典直方图和原生直方图的代码示例

    Go
    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,
    })
    java
    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();
  3. 让 Prometheus 或 Grafana Alloy 抓取同时定义了经典直方图和原生直方图的指标。

  4. 将原生直方图以及现有经典直方图发送到 remote write。

  5. 修改仪表盘以使用原生直方图指标。有关详细信息,请参阅可视化原生直方图

    使用以下策略之一更新仪表盘。

    • (推荐)使用新的原生直方图查询添加新的仪表盘。此解决方案需要查看迁移前后的不同仪表盘,直到迁移前的数据因超过保留时间而被移除。当有足够的时间为用户提供新数据时,您可以发布新仪表盘。

    • 在仪表盘中添加仪表盘变量,以启用在经典直方图和原生直方图之间切换。Grafana 不支持选择性地启用和禁用查询(问题 79848)。作为一种变通方法,例如添加仪表盘变量 latency_metrics,并为其分配值 -11。然后,将以下两个查询添加到面板

      (<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])),那么一旦在查询指定结束时间之前存在两个原生直方图样本,该查询就会返回值。在这种情况下,七天内的速率是从几分钟(而不是七天)的数据计算得出的。这导致您开始抓取原生直方图时图表附近出现不准确的情况。

  6. 开始添加记录规则和警报以使用原生直方图。此时请勿移除旧的记录规则和警报。

  7. 重要的是,至少在您的记录规则和警报中最长范围的时间段内(再加一天)持续抓取经典直方图和原生直方图。这是最短时间,但建议持续抓取这两种数据类型,直到可以验证新规则和警报为止。

    例如,如果您有一个计算请求速率的警报,例如 sum(rate(http_request_duration_seconds[7d])),此查询会查看过去七天的数据以及 Prometheus 回溯周期。当您开始发送原生直方图时,没有完整的七天数据,因此,结果可能不可靠,无法用于警报。

  8. 配置原生直方图采集后,选择以下任一方式停止采集经典直方图。

  9. 通过删除规则或警报的经典直方图版本来清理记录规则和警报。

Bucket 边界计算

本节假设您熟悉基本代数。原生直方图 bucket 边界是根据以 2 为底的指数公式计算得出的。

原生直方图样本有三种不同类型的 bucket,对于任何观测值,该值都会计入其中一种 bucket。

  • 一个零 bucket,其中包含绝对值小于或等于零阈值的观测值计数。

Zero threshold definition

  • 正 bucket,其中包含正观测值计数,这些观测值大于 bucket 下限且小于或等于上限。

Positive bucket definition

其中 index 可以是正整数或负整数,导致边界大于 1 或小于 1。schema 可以在检测时直接从 [-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8] 中指定,或者它是从列表中选择的最大数字,使得

Factor equation

例如,对于因子 1.1

Factor 1.1 equation

schema 与因子对照表

schema因子schema因子
-46553631.0905
-325641.0443
-21651.0219
-1461.0109
0271.0054
11.414281.0027
21.1892
  • 负 bucket,其中包含负观测值计数,这些观测值小于 bucket 上限且大于或等于下限。

Negative bucket definition

其中 schema 如上所述选择。

限制 bucket 数量

通过 remote write 抓取或接收原生直方图的服务器可能会限制其接受的原生直方图 bucket 数量。服务器可能会拒绝或降级(降低分辨率并合并相邻 bucket)。即使不限制,存储和发送可能无限数量的 bucket 也不切实际。

Prometheus 的检测库具有自动化功能,可以在使用最大 bucket 数量选项(例如 Go 中的 NativeHistogramMaxBucketNumber)时减少 bucket 数量。

超过设定的最大值后,将执行以下策略

  1. 首先,如果直方图的最后一次重置(或创建)距今至少是最小重置持续时间,则整个直方图将重置为其初始状态(包括经典 bucket)。这仅在设置了最小重置持续时间(Go 中的 NativeHistogramMinResetDuration)时才有效。

  2. 如果经过的时间较短,或者最小重置持续时间为零,则不执行重置。相反,零阈值会充分增加,以将 bucket 数量减少到小于或等于最大 bucket 数量,但不超过最大零阈值(Go 中的 NativeHistogramMaxZeroThreshold)。因此,如果阈值已经达到或超过最大阈值,则此步骤不会发生任何变化。

  3. 之后,如果 bucket 数量仍超过最大 bucket 数量,则会通过将所有 bucket 的宽度加倍来降低直方图的分辨率(直至相邻 bucket 之间的增长因子达到 2^(2^4) = 65536,请参阅Bucket 边界计算)。

  4. 一旦超过最小重置持续时间(自上次重置或直方图创建以来),任何增加的零阈值或降低的分辨率都会重置回其原始值。