Prometheus 远程写入 1.0 规范

  • 版本: 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 远程写入兼容发送器抓取已仪器化的应用程序或导出器,并将远程写入消息发送到服务器。

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

术语表

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

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

定义

协议

远程写入协议必须由具有以下签名的 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 路径来接收指标。

时间戳必须是自 Unix 纪元以来的毫秒数,类型为 int64。值必须是 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 编码,并且必须使用上面定义的 schema。请注意,Prometheus 实现 使用了 gogoproto 优化——对于使用 Golang 以外的语言编写的接收器,gogoproto 类型可以替换为行级等效项。

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

向后和向前兼容性

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

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

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

协商

  • 发送器必须在头信息中发送版本号。
  • 接收器可以在响应头("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 的目标是实现“事务性”——即永不向查询暴露部分抓取的目标。我们打算对远程写入也这样做——例如,将来我们希望将远程写入与抓取“对齐”,或许将单个抓取的所有样本、元数据和示例都在一个远程写入请求中发送。这仍有待设计。

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

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

兼容的发送器和接收器

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

常见问题

为什么没有使用 gRPC? 有趣的是,我们最初使用了 gRPC,但在 2016 年,由于难以通过 ELB,我们转而使用基于 HTTP 的 Protobuf:https://github.com/prometheus/prometheus/issues/1982

为什么不使用流式 Protobuf 消息? 如果使用持久的 HTTP/1.1 连接,它们非常接近流式传输……当然,头信息必须重新发送,但这确实比建立新的 TCP 连接成本更低。

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

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

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

本页内容