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 表示。禁止在其他情况下使用此值。
通常,发送器可以使用以下技术检测何时不再向时间序列追加数据
- 通过服务发现,检测到暴露该时间序列的目标已消失
- 发现在连续抓取之间,目标不再暴露该时间序列
- 未能抓取最初暴露该时间序列的目标
- 跟踪记录规则和告警规则的配置和评估
范围之外
本文档无意解释完全兼容 Prometheus 的监控系统所需的所有功能。特别是,以下领域超出本规范第一个版本的范围
“up”指标 “up”指标的定义和语义超出了远程写入协议的范围,应单独记录。
HTTP 路径 HTTP 处理程序的路径可以是任何值——并且必须由发送器提供。通常我们期望在配置中指定完整的 URL。
持久性 建议 Prometheus 远程写入兼容发送器应在接收器中断时持久缓冲样本数据。
认证与加密 由于远程写入使用 HTTP,我们将认证与加密视为传输层问题。发送器和接收器应支持所有常见(如基本认证、TLS 等)和潜在的自定义认证选项。不应假定 Prometheus 远程写入发送器和最终代理支持自定义认证,但我们将努力在可行的情况下支持常见和广泛使用的认证协议。
远程读取 这是一个独立的接口,已经经历了一些迭代,并且使用不如远程写入广泛。
分片 Prometheus 中用于远程写入并行化的当前分片方案很大程度上是实现细节,不属于规范的一部分。当发送器实现并行化时,它们必须保持每个时间序列的样本顺序。
回填 规范没有限制时间序列可以回溯多长时间,但服务器/实现可能会存在特定限制。
限制 标签的数量和长度、批处理大小等限制超出了本文档的范围,但预计实现将施加合理的限制。
基于推送的 Prometheus 应用程序将指标推送到 Prometheus 远程写入兼容接收器并非本系统的设计目标,应在单独的文档中探讨。
标签 每个时间序列可以包含“job”和/或“instance”标签,因为这些通常由发送器中的服务发现添加。它们并非强制性的。
未来计划
本节包含投机性计划,这些计划不被视为协议规范的一部分,但在此提及以供参考。
事务性 Prometheus 的目标是实现“事务性”——即永不向查询暴露部分抓取的目标。我们打算对远程写入也这样做——例如,将来我们希望将远程写入与抓取“对齐”,或许将单个抓取的所有样本、元数据和示例都在一个远程写入请求中发送。这仍有待设计。
元数据和示例 与上述一致,我们还随抓取到的样本发送元数据(类型信息、帮助文本)和示例。我们计划将其打包在一个远程写入请求中——本规范的未来版本可能会强制要求这一点。Prometheus 目前对发送元数据和示例有实验性支持。
优化 我们希望研究各种优化措施,通过消除标签名和值的重复来减少消息大小。
相关
兼容的发送器和接收器
本规范旨在描述以下组件如何交互(截至 2023 年 4 月)
- Prometheus(作为“发送器”和“接收器”)
- Avalanche(作为“发送器”)- 用于 Prometheus 指标的负载测试工具。
- Cortex(作为“接收器”)
- Elastic Agent(作为“接收器”)
- Grafana Agent(作为“发送器”和“接收器”)
- GreptimeDB(作为“接收器”)
- InfluxData 的 Telegraf 代理。(作为发送器,以及作为接收器)
- M3(作为“接收器”)
- Mimir(作为“接收器”)
- OpenTelemetry Collector(作为“发送器”,最终也将作为“接收器”)
- Thanos(作为“接收器”)
- Vector(作为“发送器”和“接收器”)
- VictoriaMetrics(作为“接收器”)
常见问题
为什么没有使用 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 的代理”。