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 サービスをインターネットに公開するのは困難でした。
リモート書き込みプロトコルには、バッチ処理の機会があります。たとえば、単一のリクエストで異なるシリーズの複数のサンプルを送信するなどです。プロトコルにはこれをサポートする機能がありますが、一般的には同じシリーズの複数のサンプルが同じリクエストで送信されることは期待されていません。
リモート書き込みプロトコルは、アプリケーションが Prometheus リモート書き込み互換レシーバーにメトリクスをプッシュするために使用することを意図していません。Prometheus リモート書き込み互換送信者が、インストルメント化されたアプリケーションまたはエクスポーターをスクレイピングし、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 リクエストの本文に Write Request をエンコードし、提供された URL パスで HTTP を介して受信者に送信する必要があります。受信者は、メトリクスを受信するために任意の 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 を使用してエンコードする必要があり、上記で定義されたスキーマを使用する必要があります。Prometheus の実装では gogoproto の最適化 が使用されていることに注意してください。Golang 以外の言語で書かれた受信者の場合、gogoproto の型は行レベルの同等物に置き換えることができます。
リモート書き込み受信者からの応答本文は空であるべきです。クライアントは応答本文を無視する必要があります。応答本文は将来の使用のために予約されています。
後方互換性と前方互換性
このプロトコルは セマンティックバージョニング 2.0 に従います。つまり、1.x 互換の受信者は 1.x 互換の送信者をすべて読み取ることができる必要があります。同様に、2.x 互換の受信者は 2.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 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 で応答する必要があります。
Stale Markers
Prometheus Remote Write 互換送信者は、タイムシリーズにそれ以上追加されなくなる場合に、stale marker を送信する必要があります。
Stale markers は、特別な NaN 値 0x7ff0000000000002 によってシグナルされる必要があります。この値は、それ以外の場合は使用してはいけません。
通常、送信者は次の手法を使用して、タイムシリーズにそれ以上追加されなくなる時期を検出できます。
- サービス検出を使用して、シリーズを公開しているターゲットがなくなったことを検出する
- successive scrapes の間にターゲットがタイムシリーズを公開しなくなったことに気づく
- 元のタイムシリーズを公開していたターゲットのスクレイピングに失敗する
- 記録ルールとアラートルールの設定と評価を追跡する
範囲外
このドキュメントは、Prometheus 互換の監視システム全体に必要なすべての機能について説明することを意図していません。特に、仕様の最初のバージョンでは、次の領域は範囲外です。
「up」メトリック 「up」メトリックの定義とセマンティクスは、リモート書き込みプロトコルの範囲外であり、別途文書化されるべきです。
HTTP パス HTTP ハンドラーのパスは任意であり、送信者が指定する必要があります。一般に、URL 全体が設定で指定されることを期待しています。
永続性 Prometheus Remote Write 互換送信者は、受信者の障害が発生した場合にサンプルデータを永続的にバッファリングすることを推奨します。
認証と暗号化 リモート書き込みは HTTP を使用するため、認証と暗号化はトランスポート層の問題と見なされます。送信者と受信者は、すべての一般的なもの(基本認証、TLS など)をサポートし、独自のカスタム認証オプションを追加できます。Prometheus リモート書き込み送信者および将来のエージェントでのカスタム認証のサポートは想定されていませんが、可能な限り、一般的で広く使用されている認証プロトコルをサポートするように努めます。
リモート読み取り これは別のインターフェースであり、すでにいくつかのイテレーションが行われており、それほど広く使用されていません。
シャーディング Prometheus でのリモート書き込み並列化の現在のシャーディングスキームは、実装の詳細であり、仕様の一部ではありません。送信者が並列処理を実装する場合、シリーズごとのサンプル順序を維持する必要があります。
バックフィル 仕様は、シリーズがプッシュされることのできる古さの上限を設けていませんが、サーバー/実装固有の制約が存在する場合があります。
制限 ラベルの数と長さ、バッチサイズなどの制限は、このドキュメントの範囲外ですが、実装が妥当な制限を課すことが期待されます。
プッシュベースの Prometheus アプリケーションが Prometheus Remote Write 互換受信者にメトリクスをプッシュすることは、このシステムの設計目標ではなく、別のドキュメントで検討されるべきです。
ラベル 各シリーズには、「job」および/または「instance」ラベルを含めることができます。これらは通常、送信者でサービス検出によって追加されます。これらは必須ではありません。
将来の計画
このセクションには、プロトコル仕様の一部とは見なされない推測的な計画が含まれていますが、完全性のためにここに記載されています。
トランザクショナリティ Prometheus は「トランザクション的」であることを目指しています。つまり、クエリに部分的にスクレイピングされたターゲットを公開しないということです。リモート書き込みでも同様のことを目指しています。たとえば、将来的には、リモート書き込みをスクレイプと「アライン」させたいと考えています。たとえば、単一のスクレイプのすべてのサンプル、メタデータ、およびエクサンプラーが単一のリモート書き込みリクエストで送信されるようにするなどです。これはまだ設計されていません。
メタデータ とエクサンプラー 上記と同様に、メタデータ(タイプ情報、ヘルプテキスト)とエクサンプラーもスクレイピングされたサンプルとともに送信します。これを単一のリモート書き込みリクエストにパッケージ化する予定です。仕様の将来のバージョンでこれが要求される可能性があります。Prometheus は現在、メタデータとエクサンプラーを送信するための実験的なサポートを提供しています。
最適化 ラベル名と値の繰り返しを排除することで、メッセージサイズを削減するためのさまざまな最適化を調査したいと考えています。
関連
互換性のある送信者と受信者
この仕様は、(2023年4月現在) 次のコンポーネントがどのように相互作用するかを説明することを意図しています。
- Prometheus (「送信者」と「受信者」の両方)
- Avalanche (「送信者」として) - ロードテストツールの Prometheus メトリクス。
- Cortex (「受信者」として)
- Elastic Agent (「受信者」として)
- Grafana Agent (「送信者」と「受信者」の両方)
- GreptimeDB (「受信者」として "receiver")
- InfluxData の Telegraf エージェント。(送信者として、および 受信者として)
- M3 (「受信者」として)
- Mimir (「受信者」として)
- OpenTelemetry Collector (「送信者」として "sender"、将来的には「受信者」としても)
- Thanos (「受信者」として)
- Vector (「送信者」として "sender" および「受信者」として "receiver")
- VictoriaMetrics (「受信者」として "receiver")
FAQ
なぜ gRPC を使用しなかったのですか? 面白いことに、当初は gRPC を使用していましたが、Protos を HTTP の上に切り替えました。2016 年には ELB を通過させるのが困難でした: https://github.com/prometheus/prometheus/issues/1982
なぜストリーミング protobuf メッセージを使用しないのですか? HTTP/1.1 の永続接続を使用する場合、ストリーミングに非常に近くなります...もちろんヘッダーは再送信する必要がありますが、新しい TCP セットアップよりもコストは低くなります。
なぜサンプルを順序どおりに送信するのですか? 順序制約は、Prometheus での時系列データのエンコード方法に由来しており、その実装は追記専用です。サンプリングをバッファリングし、エンコード前に並べ替えるなど、この制約を削除することは可能です。将来のプロトコルバージョンでこれを調査できます。
順序制約がある場合、リクエストを並列化するにはどうすればよいですか? サンプルは、指定されたシリーズについては順序どおりである必要があります。リモート書き込みリクエストは、異なるシリーズのものである限り、並列で送信できます。Prometheus では、ラベルでサンプルを個別のキューにシャードし、その後、各キューで書き込みが順番に行われます。これにより、同じシリーズのサンプルが順序どおりに配信されることが保証されますが、異なるシリーズのサンプルは並列で送信され、場合によってはシリーズ間で「順序外」になります。
受信者が順序外のサンプルをサポートできたとしても、エージェントが順序外で送信することはできないため、Prometheus、Cortex、Thanos に送信できなくなるため、これは必要であると考えています。これは、エコシステムの整合性を確保し、「Prometheus に書き込める Prometheus エージェント」と「そうでないエージェント」というコミュニティを混乱させたり、二分したりすることを防ぐためです。