Prometheus Remote-Write 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のような「高度な」技術も検討されましたが、当時は広く採用されておらず、AWS EC2 ELBのようなロードバランサーの背後でgRPCサービスをインターネットに公開することは困難でした。
リモート書き込みプロトコルには、複数のシリーズの複数のサンプルを1つのリクエストで送信するなど、バッチ処理の機会が含まれています。同じシリーズの複数のサンプルが同じリクエストで一般的に送信されることは想定されていませんが、プロトコルではこれをサポートしています。
リモート書き込みプロトコルは、アプリケーションがメトリクスをPrometheusリモート書き込み互換レシーバーにプッシュするために使用されることを意図していません。Prometheusリモート書き込み互換の送信者は、計装されたアプリケーションまたはエクスポーターをスクレイプし、リモート書き込みメッセージをサーバーに送信することを意図しています。
テストスイートはhttps://github.com/prometheus/compliance/tree/main/remotewrite/senderにあります。
用語集
このドキュメントの目的のために、以下の定義に従う必要があります。
- 「送信者」とは、Prometheus Remote Write データを送信するものです。
- 「受信者」とは、Prometheus Remote Write データを受信するものです。
- 「サンプル」とは、(タイムスタンプ、値)のペアです。
- 「ラベル」とは、(キー、値)のペアです。
- 「シリーズ」とは、一意のラベルセットによって識別されるサンプルのリストです。
定義
プロトコル
リモート書き込みプロトコルは、以下のシグネチャを持つ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 リクエストのボディに書き込みリクエストをエンコードし、指定された URL パスで HTTP 経由で受信者に送信しなければなりません。受信者は、メトリクスを受信するために任意の 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 の最適化 を使用していることに注意してください。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 Remote Write 互換の送信者は、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を使用するため、認証と暗号化はトランスポート層の問題であると考えます。送信者と受信者は、通常のすべての方法(Basic認証、TLSなど)をサポートすべきであり、カスタム認証オプションを追加することも自由です。Prometheusリモート書き込み送信者および最終的なエージェントにおけるカスタム認証のサポートは想定すべきではありませんが、実現可能な限り、一般的で広く使用されている認証プロトコルをサポートするよう努力します。
リモート読み取り これは、すでにいくつかのイテレーションを経ており、使用頻度の低い別のインターフェースです。
シャーディング Prometheusにおけるリモート書き込みの並列化に関する現在のシャーディングスキームは、実装の詳細であり、仕様の一部ではありません。送信者が並列化を実装する場合、シリーズごとのサンプル順序を保持しなければなりません。
バックフィル この仕様では、プッシュできるシリーズの古さには制限を設けていませんが、サーバー/実装固有の制約が存在する場合があります。
制限 ラベルの数と長さ、バッチサイズなどの制限は、このドキュメントの範囲外ですが、実装では合理的な制限が課せられると予想されます。
プッシュベースのPrometheus アプリケーションがメトリクスをPrometheusリモート書き込み互換の受信者にプッシュすることは、このシステムの設計目標ではありませんでした。これは別のドキュメントで検討されるべきです。
ラベル 各シリーズには「ジョブ」および/または「インスタンス」ラベルが含まれる場合があります。これらは通常、送信者でサービスディスカバリによって追加されます。これらは必須ではありません。
将来の計画
このセクションには、プロトコル仕様の一部とは見なされない投機的な計画が含まれていますが、完全性のためにここに記載されています。
トランザクション性 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 上の Proto に切り替えました。https://github.com/prometheus/prometheus/issues/1982
なぜストリーミングプロトコルメッセージではないのですか? 永続的な HTTP/1.1 接続を使用する場合、ストリーミングに近い動作をします。もちろんヘッダーは再送信される必要がありますが、新しい TCP セットアップよりも費用はかかりません。
なぜサンプルを順番に送信する必要があるのですか? 順序制約は、Prometheusで時系列データに使用しているエンコーディング(追記専用で実装されています)に由来します。この制約は、例えばサンプルをバッファリングし、エンコーディング前に並べ替えることで削除できます。プロトコルの将来のバージョンで、この点について調査する可能性があります。
順序制約がある場合、どのようにリクエストを並列化できますか? サンプルは、特定の系列に対して順序通りである必要があります。リモート書き込みリクエストは、異なる系列に対するものであれば並列に送信できます。Prometheusでは、サンプルをラベルでシャードし、別々のキューに入れます。その後、各キューで順次書き込みが行われます。これにより、同じ系列のサンプルは順序通りに配信されますが、異なる系列のサンプルは並列に、そして異なる系列間では「順不同」で送信される可能性があります。
これは必要であると考えています。なぜなら、たとえ受信側が順不同のサンプルをサポートできたとしても、エージェントが順不同で送信するわけにはいかないからです。そうすれば、Prometheus、Cortex、Thanosに送信できなくなります。私たちはエコシステムの整合性を確保し、コミュニティを「Prometheusに書き込めるPrometheusエージェント」とそうでないものとに混乱させたり、分裂させたりするのを防ぐためにこれを行っています。