本文档旨在定义和标准化现有、广泛且有机采用的协议的 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 找到。
为本文档的目的,必须遵循以下定义
远程写入协议必须包含具有以下签名的 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 版本。
proto 格式本身在某些方面是向前/向后兼容的
协商
必须随每个样本发送完整的标签集。此外,与样本关联的标签集
__name__
标签。发送者必须只发送有效的指标名称、标签名称和标签值
[a-zA-Z_:]([a-zA-Z0-9_:])*
。[a-zA-Z_]([a-zA-Z0-9_])*
。接收者可以对标签的数量和长度施加限制,但这将是接收器特定的,并且不在本文档的范围内。
以 "__" 开头的标签名称保留供系统使用,不应该 (SHOULD NOT) 使用,请参阅 Prometheus 数据模型。
远程写入接收者可以摄取写入请求中包含的有效样本,否则该请求包含无效样本。对于包含任何无效样本的写入请求,接收者必须返回 HTTP 400 状态代码(“错误请求”)。接收者应该 (SHOULD) 在响应主体中提供人类可读的错误消息。发送者禁止 (MUST NOT) 尝试解释错误消息,并且应该 (SHOULD) 按原样记录它。
兼容 Prometheus Remote Write 的发送器必须按时间戳顺序发送任何给定序列的样本。兼容 Prometheus Remote Write 的发送器可以并行发送多个不同序列的请求。
兼容 Prometheus Remote Write 的发送器必须在收到 HTTP 5xx 响应时重试写入请求,并且必须使用回退算法以防止服务器过载。它们不得重试 HTTP 2xx 和 4xx 响应的写入请求,除了 429 之外。它们可以重试 HTTP 429 响应,这可能会导致发送器在服务器无法跟上时“落后”。这样做是为了确保在发生服务器端错误时数据不会丢失,并在发生客户端错误时取得进展。
兼容 Prometheus Remote Write 的接收器必须在写入成功时返回 HTTP 2xx 状态码。当写入失败并且应该重试时,它们必须返回 HTTP 状态码 5xx。当请求无效,永远无法成功并且不应重试时,它们必须返回 HTTP 状态码 4xx。
当时间序列不再追加时,兼容 Prometheus remote write 的发送器必须发送陈旧标记。
陈旧标记必须由特殊的 NaN 值 0x7ff0000000000002 信号表示。此值不得在其他情况下使用。
通常,发送器可以使用以下技术检测到何时不再追加时间序列
本文档不打算解释完全兼容 Prometheus 的监控系统所需的所有功能。特别是,以下领域不在该规范的第一个版本范围之内
“up” 指标 “up” 指标的定义和语义超出了远程写入协议的范围,应单独记录。
HTTP 路径 HTTP 处理程序的路径可以是任何内容 - 并且必须由发送器提供。通常我们期望在配置中指定整个 URL。
持久化 建议兼容 Prometheus Remote Write 的发送器在接收器发生中断时应持久缓冲样本数据。
身份验证 & 加密 由于远程写入使用 HTTP,我们认为身份验证 & 加密是传输层问题。发送器和接收器应支持所有常见选项(基本身份验证,TLS 等),并且可以自由添加潜在的自定义身份验证选项。不应假定在 Prometheus 远程写入发送器和最终代理中支持自定义身份验证,但我们将尽力支持常见和广泛使用的身份验证协议(在可行的情况下)。
远程读取 这是一个单独的接口,已经进行了一些迭代,并且使用较少。
分片 Prometheus 中用于远程写入并行化的当前分片方案在很大程度上是一个实现细节,不属于该规范的一部分。当发送器确实实现并行化时,它们必须保留每个序列的样本顺序。
回填 该规范没有限制可以推送的序列的旧程度,但是可能存在特定于服务器/实现的约束。
限制 标签的数量和长度、批处理大小等的限制不在本文档的范围之内,但是预计实现将施加合理的限制。
基于推送的 Prometheus 将指标推送到兼容 Prometheus Remote Write 的接收器的应用程序不是该系统的设计目标,应在单独的文档中进行探讨。
标签 每个序列都可以包含“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-代理”和不能写入的代理。
此文档是 开源的。请提交问题或拉取请求以帮助改进它。