Prometheus 远程写入 1.0 规范
- 版本:1.0
- 状态:已发布
- 日期:2023 年 4 月
本文件旨在定义和标准化现有、已广泛并自然采纳的协议的 API、传输格式、协议和语义,而非提出任何新内容。
远程写入规范旨在记录 Prometheus 和兼容 Prometheus 远程写入的 Agent 如何向 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 等“花哨”技术,但当时这些技术尚未广泛采用,并且在 AWS EC2 ELB 等负载均衡器后向互联网公开 gRPC 服务具有挑战性。
远程写入协议包含批处理的机会,例如在单个请求中发送不同时间序列的多个样本。不期望同一时间序列的多个样本会经常在同一个请求中发送,尽管协议中对此提供了支持。
远程写入协议不适用于应用程序向兼容 Prometheus 远程写入的接收器推送指标。它旨在由兼容 Prometheus 远程写入的发送方抓取已埋点(instrumented)的应用程序或导出器,并将远程写入消息发送到服务器。
测试套件可在 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 路径来接收指标。
时间戳必须是自 Unix 纪元以来的毫秒数,类型为 int64。值必须是 float64。
HTTP 请求必须包含以下头部
Content-Encoding: snappyContent-Type: application/x-protobufUser-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 格式本身在某些方面是向前/向后兼容的
- 从 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 和除 429 之外的 4xx 响应时重试写入请求。它们可以对 HTTP 429 响应进行重试,这可能导致发送方在服务器无法跟上时“落后”。这样做是为了确保在出现服务器端错误时数据不丢失,并在出现客户端错误时取得进展。
兼容 Prometheus 远程写入的接收方在写入成功时必须返回 HTTP 2xx 状态码。在写入失败且应该重试时,它们必须返回 HTTP 5xx 状态码。在请求无效、永远无法成功且不应重试时,它们必须返回 HTTP 4xx 状态码。
过期标记
当时间序列不再追加时,兼容 Prometheus 远程写入的发送方必须发送过期标记。
过期标记必须由特殊的 NaN 值 0x7ff0000000000002 表示。该值绝不能用于其他情况。
通常,发送方可以使用以下技术检测时间序列何时不再追加
- 使用服务发现,检测到暴露该时间序列的目标已消失
- 注意到目标在连续抓取之间不再暴露该时间序列
- 抓取最初暴露时间序列的目标失败
- 跟踪记录规则和告警规则的配置和评估
范围外
本文件不旨在解释一个完全兼容 Prometheus 的监控系统所需的所有功能。特别是,以下领域超出了规范第一版的范围
“up”指标 “up”指标的定义和语义超出了远程写入协议的范围,应单独记录。
HTTP 路径 HTTP 处理程序的路径可以是任何内容——并且必须由发送方提供。通常,我们期望整个 URL 在配置中指定。
持久性 建议兼容 Prometheus 远程写入的发送方在接收方发生故障时,应持久化缓冲样本数据。
身份验证和加密 由于远程写入使用 HTTP,我们将身份验证和加密视为传输层问题。发送方和接收方应支持所有常见的认证方式(基本认证、TLS 等),并可以自由添加潜在的自定义认证选项。不应假定 Prometheus 远程写入发送方和最终的 Agent 支持自定义身份验证,但我们将努力在可行的情况下支持常见且广泛使用的认证协议。
远程读取 这是一个独立的接口,已经经历了一些迭代,并且使用范围较窄。
分片 Prometheus 中用于远程写入并行化的当前分片方案非常依赖于实现细节,不属于规范的一部分。当发送方实现并行化时,它们必须保留每个时间序列的样本顺序。
回填 规范并未限制可推送的时间序列的“陈旧”程度,但可能存在服务器/实现特定的约束。
限制 对标签数量和长度、批次大小等的限制超出了本文档的范围,但预计实现将施加合理的限制。
基于推送的 Prometheus 应用程序向兼容 Prometheus 远程写入的接收器推送指标并非本系统的设计目标,应在单独的文档中探讨。
标签 每个时间序列可以包含“job”和/或“instance”标签,因为这些通常由发送方中的服务发现添加。这些并非强制性。
未来计划
本节包含一些推测性计划,它们不被视为协议规范的一部分,但在此提及以求完整性。
事务性 Prometheus 旨在实现“事务性”——即永不向查询暴露部分抓取的目标。我们也打算对远程写入这样做——例如,将来我们希望“对齐”远程写入和抓取,或许可以将单个抓取的所有样本、元数据和示例(exemplars)在一个远程写入请求中发送。这仍待设计。
元数据和示例 与上述一致,我们还随抓取到的样本发送元数据(类型信息、帮助文本)和示例(exemplars)。我们计划将这些打包在一个远程写入请求中——规范的未来版本可能会强制要求这一点。Prometheus 目前对发送元数据和示例有实验性支持。
优化 我们希望研究各种优化方案,通过消除标签名称和值的重复来减少消息大小。
相关
兼容的发送方和接收方
该规范旨在描述以下组件如何交互(截至 2023 年 4 月)
- Prometheus (作为“发送方”和“接收方”)
- Avalanche (作为“发送方”)- 一个 Prometheus 指标负载测试工具。
- Cortex (作为“接收方”)
- Elastic Agent (作为“接收方”)
- Grafana Agent (作为“发送方”和“接收方”)
- GreptimeDB (作为 “接收方” )
- InfluxData 的 Telegraf 代理。(作为发送方 ,和 作为接收方 )
- M3 (作为“接收方”)
- Mimir (作为“接收方”)
- Oodle (作为“接收方”)
- OpenTelemetry Collector (作为“发送方” 并最终作为“接收方”)
- Thanos (作为“接收方”)
- Vector (作为“发送方” 和 “接收方” )
- VictoriaMetrics (作为“接收方” )
常见问题
为什么不使用 gRPC? 有趣的是,我们最初使用了 gRPC,但在 2016 年由于很难让它们通过 ELB,所以改用基于 HTTP 的 Protos:https://github.com/prometheus/prometheus/issues/1982
为什么不使用流式 protobuf 消息? 如果您使用持久化的 HTTP/1.1 连接,它们与流式传输非常接近……当然,需要重新发送头部信息,但这确实比建立新的 TCP 连接成本更低。
为什么我们按顺序发送样本? 按序约束源于 Prometheus 中时间序列数据所使用的编码,其实现方式是只追加。可以通过缓冲样本并在编码前重新排序来解除此约束。我们可以在协议的未来版本中研究这一点。
在存在顺序约束的情况下如何并行化请求? 样本必须按序 *针对给定序列*。只要远程写入请求是针对不同序列的,就可以并行发送。在 Prometheus 中,我们通过标签将样本分片到独立的队列中,然后每个队列中的写入按顺序进行。这保证了同一序列的样本按顺序交付,但不同序列的样本则并行发送——并且不同序列之间可能出现“乱序”。
我们认为这是必要的,因为即使接收方能够支持乱序样本,我们也不能让代理发送乱序样本,因为它们将永远无法发送到 Prometheus、Cortex 和 Thanos。我们这样做是为了确保生态系统的完整性,并防止社区因“能够写入 Prometheus 的 Prometheus 代理”和“不能写入 Prometheus 的 Prometheus 代理”而产生混淆或分歧。