Prometheus Remote-Write 1.0 规范

  • 版本:1.0
  • 状态:已发布
  • 日期:2023 年 4 月

本文档旨在定义和标准化现有、被广泛且有机采用的协议的 API、线格式、协议和语义,而不是提出任何新内容。

remote write 规范旨在记录 Prometheus 和兼容 Prometheus remote-write 的代理将数据发送到 Prometheus 或兼容 Prometheus remote-write 的接收器时的标准。

本文档中的关键词 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY" 和 "OPTIONAL" 的解释遵循 RFC 2119 

注意本规范的 2.0 版本也已发布,请参见 此处

简介

背景

远程写入协议旨在实现样本从发送者可靠地实时传输到接收者,且不丢失。

远程写入协议设计为无状态的;严格来说,没有消息间的通信。因此,该协议不被视为“流式”。为了实现流式效果,应通过同一连接发送多个消息,例如使用 HTTP/1.1 或 HTTP/2。“Fancy”技术(如 gRPC)已被考虑,但在当时并未被广泛采用,并且难以在 AWS EC2 ELB 等负载均衡器后面向互联网暴露 gRPC 服务。

远程写入协议提供了批量处理的机会,例如在一次请求中发送不同系列(series)的多个样本。虽然协议支持,但预计不会常见地在同一个请求中发送同一系列的多个样本。

远程写入协议并非 intended for use by applications to push metrics to Prometheus remote-write-compatible receivers。其 intended use is that a Prometheus remote-write-compatible sender scrapes instrumented applications or exporters and sends remote write messages to a server。

测试套件可以在 https://github.com/prometheus/compliance/tree/main/remotewrite/sender  找到。

术语表

为本文档之目的,必须遵守以下定义

  • "Sender" 是发送 Prometheus 远程写入数据的实体。
  • "Receiver" 是接收 Prometheus 远程写入数据的实体。
  • "Sample" 是一个 (timestamp, value) 对。
  • "Label" 是一个 (key, value) 对。
  • "Series" 是由一组唯一的标签标识的样本列表。

定义

协议

远程写入协议 MUST 由具有以下签名的 RPC 组成

func Send(WriteRequest)

message WriteRequest {
  repeated TimeSeries timeseries = 1;
  // Cortex uses this field to determine the source of the write request.
  // We reserve it to avoid any compatibility issues.
  reserved  2;

  // Prometheus uses this field to send metadata, but this is
  // omitted from v1 of the spec as it is experimental.
  reserved  3;
}

message TimeSeries {
  repeated Label labels   = 1;
  repeated Sample samples = 2;
}

message Label {
  string name  = 1;
  string value = 2;
}

message Sample {
  double value    = 1;
  int64 timestamp = 2;
}

远程写入发送者必须将 Write Request 编码到 HTTP POST 请求的正文中,并通过 HTTP 发送给接收者,目标 URL 路径由接收者提供。接收者 MAY 指定任何 HTTP URL 路径来接收指标。

时间戳必须是自 Unix 纪元以来的 int64 毫秒数。值必须是 float64。

必须在 HTTP 请求中发送以下标头

  • Content-Encoding: snappy
  • Content-Type: application/x-protobuf
  • User-Agent: <发送者的名称和版本>
  • X-Prometheus-Remote-Write-Version: 0.1.0

客户端 MAY 允许用户发送自定义 HTTP 标头;它们 MUST NOT 允许用户以发送保留标头的方式对其进行配置。有关更多信息,请参见 https://github.com/prometheus/prometheus/pull/8416 

HTTP POST 请求正文中的远程写入请求必须使用 Google 的 Snappy  进行压缩。必须使用 block 格式 - 绝对不能使用 framed 格式。

远程写入请求必须使用 Google Protobuf 3 进行编码,并且必须使用上面定义的 schema。请注意,Prometheus 实现  使用了 gogoproto 优化  - 对于使用 Go 语言以外的语言编写的接收者,gogoproto 类型 MAY 被替换为行级等效项。

远程写入接收者的响应体 SHOULD 为空;客户端必须忽略响应体。响应体是为将来使用而保留的。

向后和向前兼容性

该协议遵循 语义化版本 2.0 :任何 1.x 兼容的接收者必须能够读取任何 1.x 兼容的发送者,依此类推。破坏性/向后不兼容的更改将导致规范的 2.x 版本。

proto 格式本身在某些方面是向前/向后兼容的

  • 从 proto 中删除字段将导致主版本号增加。
  • 添加(可选)字段将导致次版本号增加。

协商

  • 发送者必须在标头中发送版本号。
  • 接收者 MAY 在响应标头 ("X-Prometheus-Remote-Write-Version") 中返回其支持的最高版本号。
  • 希望以大于 1.x 的格式发送的发送者必须首先发送一个空的 1.x 版本,然后查看响应是否表明接收者支持其他版本。发送者 MAY 使用任何支持的版本。如果响应中没有版本标头,发送者必须仅假定 1.x 兼容性。

标签

每个样本都必须附带完整的标签集。此外,与样本关联的标签集

  • SHOULD 包含一个 `__name__` 标签。
  • MUST NOT 包含重复的标签名称。
  • MUST 按字母顺序对标签名称进行排序。
  • MUST NOT 包含任何空的标签名称或值。

发送者必须只发送有效的指标名称、标签名称和标签值

  • 指标名称必须符合正则表达式 `[a-zA-Z_:]([a-zA-Z0-9_:])*`。
  • 标签名称必须符合正则表达式 `[a-zA-Z_]([a-zA-Z0-9_])*`。
  • 标签值可以是任何 UTF-8 字符序列。

接收者 MAY 对标签的数量和长度施加限制,但这将是接收者特定的,并且超出了本文档的范围。

以 "__" 开头的标签名称是为系统保留的,SHOULD NOT 使用,请参见 Prometheus 数据模型

远程写入接收者 MAY 接受写入请求中有效的样本,即使该请求包含无效样本。接收者必须对包含任何无效样本的写入请求返回 HTTP 400 状态码(“Bad Request”)。接收者 SHOULD 在响应体中提供人类可读的错误消息。发送者 MUST NOT 尝试解释错误消息,而 SHOULD 按原样记录。

排序

Prometheus 远程写入兼容的发送者必须按时间戳顺序发送给定系列的样本。Prometheus 远程写入兼容的发送者 MAY 并行发送多个请求以处理不同系列。

重试与回退

Prometheus 远程写入兼容的发送者必须在收到 HTTP 5xx 响应时重试写入请求,并必须使用回退算法来防止服务器过载。它们必须不对 HTTP 2xx 和 4xx 响应(429 除外)进行重试。它们 MAY 重试 HTTP 429 响应,这可能导致发送者“落后”,如果服务器无法跟上。这样做是为了确保在服务器端错误时数据不会丢失,并在客户端错误时取得进展。

Prometheus 远程写入兼容的接收者在写入成功时必须响应 HTTP 2xx 状态码。当写入失败且应重试时,它们必须响应 HTTP 5xx 状态码。当请求无效、永远无法成功且不应重试时,它们必须响应 HTTP 4xx 状态码。

过时标记

Prometheus 远程写入兼容的发送者必须在时间序列不再追加时发送过时标记。

过时标记必须通过特殊 NaN 值 0x7ff0000000000002 来表示。此值不得用于其他任何目的。

通常,发送者可以使用以下技术来检测时间序列何时将不再追加

  1. 使用服务发现检测暴露该系列的目标已消失
  2. 在连续抓取之间注意到目标不再暴露时间序列
  3. 无法抓取最初暴露时间序列的目标
  4. 跟踪记录和警报规则的配置和评估

范围外

本文档无意解释实现完全兼容 Prometheus 的监控系统所需的所有功能。特别是,以下领域超出了本规范第一版的范围

“up”指标 "up" 指标的定义和语义超出了远程写入协议的范围,应单独记录。

HTTP 路径 HTTP 处理程序的路径可以是任何内容 - 并且必须由发送者提供。通常我们期望在配置中指定整个 URL。

持久化 建议 Prometheus 远程写入兼容的发送者在接收者发生故障时持久化缓冲样本数据。

身份验证和加密 由于远程写入使用 HTTP,我们认为身份验证和加密是传输层问题。发送者和接收者应支持所有常用选项(基本身份验证、TLS 等),并可自由添加自定义身份验证选项。不应假定 Prometheus 远程写入发送者和最终代理支持自定义身份验证,但我们将努力在可行的情况下支持常见且广泛使用的身份验证协议。

远程读取 这是一个单独的接口,已经经历了一些迭代,并且使用不太广泛。

分片 Prometheus 中用于远程写入并行化的当前分片方案在很大程度上是实现细节,并非规范的一部分。当发送者实现并行化时,它们必须保留每个系列的样本排序。

回填 规范对旧系列可以推送的年龄没有限制,但服务器/实现特定的限制可能存在。

限制 标签的数量和长度、批次大小等限制超出了本文档的范围,但预计实现将施加合理的限制。

基于推送的 Prometheus 应用程序将指标推送到 Prometheus 远程写入兼容的接收者并非此系统的设计目标,应在单独的文档中探讨。

标签 每个系列 MAY 包含 "job" 和/或 "instance" 标签,因为这些标签通常由发送者的服务发现添加。这些不是强制性的。

未来计划

本节包含推测性的计划,这些计划不被视为协议规范的一部分,但在此提及以求完整。

事务性 Prometheus 旨在成为“事务性”的 - 即,绝不向查询暴露部分抓取的指标。我们打算同样对待远程写入 - 例如,将来我们希望将远程写入与抓取“对齐”,也许这样,单个抓取的 {样本、元数据和示例} 都可以通过一次远程写入请求发送。这还有待设计。

元数据 和示例 与上述一致,我们也随抓取的样本一起发送元数据(类型信息、帮助文本)和示例。我们计划将此打包到单个远程写入请求中 - 规范的未来版本可能坚持这样做。Prometheus 目前对发送元数据和示例提供实验性支持。

优化 我们希望研究各种优化,通过消除标签名称和值的重复来减小消息大小。

兼容的发送者和接收者

该规范旨在描述以下组件(截至 2023 年 4 月)如何交互

常见问题

为什么你们没有使用 gRPC? 有趣的是,我们最初使用了 gRPC,但后来切换到了基于 HTTP 的 Protos,因为在 2016 年,它们很难通过 ELB:https://github.com/prometheus/prometheus/issues/1982 

为什么不使用流式 protobuf 消息? 如果您使用持久的 HTTP/1.1 连接,它们就非常接近流式传输……当然,头部需要重新发送,但是的,这比建立新的 TCP 连接要便宜。

为什么我们按顺序发送样本? 顺序约束来自我们在 Prometheus 中用于时间序列数据的编码方式,其实现是仅追加的。可以移除此约束,例如通过缓冲样本并在编码前对其进行重新排序。我们可以在协议的未来版本中对此进行研究。

在有顺序约束的情况下,我们如何并行化请求? 样本必须是给定系列的顺序。远程写入请求可以并行发送,只要它们是针对不同系列的。在 Prometheus 中,我们按标签将样本分片到不同的队列中,然后在每个队列中按顺序进行写入。这保证了同一系列的样本按顺序传递,但不同系列的样本是并行发送的——并且可能在不同系列之间“乱序”。

我们认为这是必要的,因为即使接收器可以支持乱序样本,我们也无法让代理乱序发送,因为它们将永远无法发送到 Prometheus、Cortex 和 Thanos。我们这样做是为了确保生态系统的完整性,并防止社区陷入“可以写入 Prometheus 的 Prometheus 代理”和“不能”的混淆/分裂。

本页内容