本文档旨在定义和标准化现有广泛且有机采用的协议的 API、线格式、协议和语义,而不是提出任何新内容。
远程写入规范旨在记录 Prometheus 和与 Prometheus 远程写入兼容的代理如何将数据发送到 Prometheus 或与 Prometheus 远程写入兼容的接收器的标准。
本文档中的关键词“必须”、“一定不能”、“必需”、“应”、“不应该”、“应该”、“不应”、“推荐”、“可以”和“可选”的解释方式如RFC 2119中所述。
远程写入协议旨在使能够可靠地实时传播样本从发送方到接收方,而不会丢失。
远程写入协议旨在无状态;严格来说没有消息间通信。因此,该协议不被认为是“流式”。为了实现流式效果,应使用例如 HTTP/1.1 或 HTTP/2 在同一连接上发送多个消息。“花哨”的技术(如 gRPC)已被考虑,但在当时并未被广泛采用,并且很难将 gRPC 服务公开到 AWS EC2 ELB 等负载均衡器后面的互联网上。
远程写入协议包含批处理的机会,例如在单个请求中发送不同序列的多个样本。预计不会在同一个请求中通常发送同一个序列的多个样本,尽管协议中支持这样做。
远程写入协议并非旨在供应用程序用于将指标推送到与 Prometheus 远程写入兼容的接收器。它旨在使与 Prometheus 远程写入兼容的发件人抓取已检测的应用程序或导出器并将远程写入消息发送到服务器。
可以在https://github.com/prometheus/compliance/tree/main/remote_write_sender找到测试套件。
出于本文档的目的,必须遵循以下定义
远程写入协议必须由具有以下签名的 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 进行编码,并且必须使用上面定义的模式。请注意Prometheus 实现使用gogoproto 优化 - 对于用 Go 以外的语言编写的接收器,可以将 gogoproto 类型替换为行级等效项。
来自远程写入接收器的响应主体应为空;客户端必须忽略响应主体。响应主体保留供将来使用。
该协议遵循语义版本控制 2.0:任何 1.x 兼容的接收器都必须能够读取任何 1.x 兼容的发件人,依此类推。重大/向后不兼容的更改将导致规范的 2.x 版本。
proto 格式本身在某些方面是向前/向后兼容的
协商
必须将完整的标签集与每个样本一起发送。此外,与样本关联的标签集
__name__
标签。发送方必须仅发送有效的指标名称、标签名称和标签值
[a-zA-Z_:]([a-zA-Z0-9_:])*
。[a-zA-Z_]([a-zA-Z0-9_])*
。接收方可以对标签的数量和长度施加限制,但这将是接收器特定的,并且不在本文档的范围之内。
以“__”开头的标签名称保留供系统使用,不应使用,请参阅Prometheus 数据模型。
远程写入接收方可以在其他方面包含无效样本的写入请求中摄取有效样本。接收方必须针对包含任何无效样本的写入请求返回 HTTP 400 状态代码(“错误请求”)。接收方应在响应主体中提供人类可读的错误消息。发送方一定不能尝试解释错误消息,而应按原样记录它。
与 Prometheus 远程写入兼容的发件人必须按时间戳顺序发送任何给定序列的样本。与 Prometheus 远程写入兼容的发件人可以并行发送不同序列的多个请求。
与 Prometheus 远程写入兼容的发件人必须在 HTTP 5xx 响应上重试写入请求,并且必须使用回退算法来防止服务器过载。它们一定不能在 HTTP 2xx 和 4xx 响应上重试写入请求,除非是 429。它们可以在 HTTP 429 响应上重试,如果服务器无法跟上,这可能会导致发送方“落后”。这样做是为了确保在出现服务器端错误时不会丢失数据,并且在出现客户端错误时会取得进展。
与 Prometheus 远程写入兼容的接收方必须在写入成功时响应 HTTP 2xx 状态代码。它们必须在写入失败时响应 HTTP 状态代码 5xx,并且应重试。它们必须在请求无效、永远无法成功并且不应重试时响应 HTTP 状态代码 4xx。
与 Prometheus 远程写入兼容的发件人必须在不再追加时间序列时发送陈旧标记。
陈旧标记必须由特殊 NaN 值 0x7ff0000000000002 发出信号。此值一定不能用于其他目的。
通常,发送方可以使用以下技术检测何时不再追加时间序列
本文档不打算解释完全兼容 Prometheus 的监控系统所需的所有功能。特别是,以下领域不在规范第一个版本的范围内
“up”指标 “up”指标的定义和语义超出了远程写入协议的范围,应单独记录。
HTTP路径 HTTP 处理程序的路径可以是任何内容 - 并且**必须**由发送方提供。通常,我们期望在配置中指定整个 URL。
持久性 建议兼容 Prometheus 远程写入的发送方应在接收方出现故障时持久地缓冲样本数据。
身份验证和加密 由于远程写入使用 HTTP,因此我们将身份验证和加密视为传输层问题。发送方和接收方应支持所有常用的方法(基本身份验证、TLS 等),并且可以自由添加可能自定义的身份验证选项。不应假设在 Prometheus 远程写入发送方和最终代理中支持自定义身份验证,但我们将努力在可行的情况下支持常见和广泛使用的身份验证协议。
远程读取 这是一个已经经历了一些迭代的单独接口,并且使用不太广泛。
分片 Prometheus 中用于远程写入并行的当前分片方案在很大程度上是实现细节,并且不是规范的一部分。当发送方确实实现并行化时,**必须**保留每个系列样本的顺序。
回填 规范没有对可以推送的系列的旧程度设置限制,但是可能存在服务器/特定于实现的约束。
限制 关于标签数量和长度、批处理大小等的限制不在本文档的范围内,但是预计实现将施加合理的限制。
基于推送的 Prometheus 应用程序将指标推送到兼容 Prometheus 远程写入的接收方不是该系统的设计目标,应在单独的文档中探讨。
标签 每个系列**可以**包含“job”和/或“instance”标签,因为这些标签通常由发送方中的服务发现添加。这些不是强制性的。
本节包含推测性计划,这些计划不被视为协议规范的一部分,但出于完整性考虑在此提及。
事务性 Prometheus 旨在实现“事务性”——即永远不会将部分抓取的目标公开给查询。我们打算对远程写入执行相同的操作——例如,将来我们希望将远程写入与抓取“对齐”,也许这样,单个抓取的所有样本、元数据和示例都将发送在一个远程写入请求中。这有待设计。
元数据和示例 按照上述内容,我们还将元数据(类型信息、帮助文本)和示例与抓取的样本一起发送。我们计划将这些打包在一个远程写入请求中——未来版本的规范可能会坚持这一点。Prometheus 目前对发送元数据和示例有实验性支持。
优化 我们希望研究各种优化方法,以通过消除标签名称和值的重复来减小消息大小。
该规范旨在描述以下组件如何交互(截至 2023 年 4 月)
为什么不使用 gRPC? 很有趣的是,我们最初使用了 gRPC,但在 2016 年切换到 HTTP 之上的 Protos,因为很难让他们通过 ELB:https://github.com/prometheus/prometheus/issues/1982
为什么不使用流式 protobuf 消息? 如果使用持久性 HTTP/1.1 连接,它们非常接近流式……当然,必须重新发送标头,但确实比新的 TCP 设置更便宜。
为什么我们要按顺序发送样本? 按顺序约束来自我们用于 Prometheus 中时间序列数据的编码,其实现是仅追加的。可以删除此约束,例如通过缓冲样本并在编码之前重新排序它们。我们可以在未来版本的协议中对此进行调查。
如何在按顺序约束的情况下并行化请求? 样本**必须**按给定系列的顺序排列。只要远程写入请求针对不同的系列,就可以并行发送。在 Prometheus 中,我们按其标签将样本分片到单独的队列中,然后每个队列中的写入按顺序发生。这保证了同一系列的样本按顺序传递,但不同系列的样本并行发送——并且可能在不同系列之间“乱序”。
我们认为这是必要的,因为即使接收方可以支持乱序样本,我们也不能让代理发送乱序样本,因为它们永远无法发送到 Prometheus、Cortex 和 Thanos。我们这样做是为了确保生态系统的完整性,并防止将社区混淆/分支成“可以写入 Prometheus 的 Prometheus 代理”和那些不能写入的代理。
本文档为开源。请通过提交问题或拉取请求帮助改进它。