Prometheus 远程写入规范

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

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

远程写入规范旨在记录 Prometheus 和 Prometheus 远程写入兼容代理如何将数据发送到 Prometheus 或 Prometheus 远程写入兼容接收器的标准。

本文档中关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY” 和 “OPTIONAL” 必须按照 RFC 2119 中的描述进行解释。

注意: 此规范有 2.0 版本可用,请参阅此处

介绍

背景

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

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

远程写入协议包含批处理的机会,例如,在单个请求中为不同的序列发送多个样本。虽然协议中支持,但预计在同一请求中为同一序列发送多个样本的情况并不常见。

远程写入协议不适用于应用程序将指标推送到 Prometheus 远程写入兼容接收器。其目的是让 Prometheus 远程写入兼容发送器抓取已进行 Instrumentation 的应用程序或 exporters,并将远程写入消息发送到服务器。

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

术语表

为了本文档的目的,必须遵循以下定义

  • “发送者”是发送 Prometheus 远程写入数据的实体。
  • “接收者”是接收 Prometheus 远程写入数据的实体。
  • “样本”是 (时间戳, 值) 对。
  • “标签”是 (键, 值) 对。
  • “序列”是由一组唯一标签标识的样本列表。

定义

协议

远程写入协议必须包含具有以下签名的 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;
}

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

时间戳必须是 int64 类型,以 Unix 纪元以来的毫秒数计算。值必须是 float64 类型。

以下标头必须随 HTTP 请求一起发送

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

客户端可以允许用户发送自定义 HTTP 标头;它们绝不能允许用户以发送保留标头的方式配置它们。更多信息请参阅 https://github.com/prometheus/prometheus/pull/8416

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

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

来自远程写入接收器的响应正文应为空;客户端必须忽略响应正文。响应正文被保留以供将来使用。

向后和向前兼容性

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

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

  • 从 proto 中删除字段将意味着主版本号的提升。
  • 添加(可选)字段将是次版本号的提升。

协商

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

标签

完整的标签集必须随每个样本一起发送。此外,与样本关联的标签集

  • 应包含 __name__ 标签。
  • 绝不能包含重复的标签名称。
  • 标签名称必须按字典顺序排序。
  • 绝不能包含任何空的标签名称或值。

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

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

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

以 "__" 开头的标签名称保留供系统使用,不应使用,请参阅 Prometheus 数据模型

远程写入接收器可以摄取写入请求中包含的有效样本,即使该请求也包含无效样本。对于包含任何无效样本的写入请求,接收器必须返回 HTTP 400 状态代码(“Bad Request”)。接收器应在响应正文中提供人类可读的错误消息。发送者绝不能尝试解释错误消息,而应按原样记录它。

排序

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

重试与退避

Prometheus 远程写入兼容发送者必须在收到 HTTP 5xx 响应时重试写入请求,并且必须使用退避算法以防止服务器过载。对于 HTTP 2xx 和 4xx 响应(429 除外),它们绝不能重试写入请求。对于 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 远程写入兼容接收器不是该系统的设计目标,应在单独的文档中探讨。

标签 每个序列可以包含一个 “job” 和/或 “instance” 标签,因为这些标签通常由发送者中的服务发现添加。这些不是强制性的。

未来计划

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

事务性 Prometheus 的目标是实现“事务性” - 即永远不会将部分抓取的目标暴露给查询。我们打算对远程写入做同样的事情 - 例如,将来我们希望将远程写入与抓取“对齐”,也许使得单个抓取的所有样本、元数据和 exemplars 都在一个远程写入请求中发送。这还有待设计。

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

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

兼容的发送者和接收者

该规范旨在描述以下组件如何交互(截至 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 代理”和“不能写入的代理”。

本文档是开源的。请通过提交问题或拉取请求帮助改进它。