Prometheus リモート書き込み仕様

  • バージョン: 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リモート書き込み互換送信者が、計測されたアプリケーションやエクスポーターをスクレイプし、リモート書き込みメッセージをサーバーに送信することを意図しています。

テストスイートは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リクエストのボディに書き込みリクエストをエンコードし、提供された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リモート書き込み互換送信者は、HTTP 5xx応答で書き込みリクエストを再試行するものとし、サーバーを圧倒しないようにバックオフアルゴリズムを使用するものとします。HTTP 2xxおよび4xx応答(429を除く)では、書き込みリクエストを再試行してはなりません。HTTP 429応答では再試行してもかまいませんが、これによりサーバーが追いつけない場合に送信者が「遅延する」可能性があります。これは、サーバー側のエラーがある場合にデータが失われないようにし、クライアント側のエラーがある場合に処理が進むようにするために行われます。

Prometheusリモート書き込み互換受信者は、書き込みが成功した場合、HTTP 2xxステータスコードで応答するものとします。書き込みが失敗し、再試行されるべきである場合、HTTP 5xxステータスコードで応答するものとします。リクエストが無効で、決して成功せず、再試行されるべきではない場合、HTTP 4xxステータスコードで応答するものとします。

Staleマーカー

Prometheusリモート書き込み互換送信者は、時系列データがこれ以上追加されなくなる場合、Staleマーカーを送信するものとします。

Staleマーカーは、特殊なNaN値0x7ff0000000000002によって通知されるものとします。この値は、それ以外の場合に使用してはなりません。

通常、送信者は以下の手法を使用して、時系列データがこれ以上追加されなくなるタイミングを検出できます。

  1. サービスディスカバリを使用して、シリーズを公開しているターゲットがなくなったことを検出する
  2. 連続するスクレイプ間でターゲットが時系列データを公開しなくなったことに気づく
  3. 元々時系列データを公開していたターゲットのスクレイプに失敗する
  4. 記録ルールとアラートルールの設定および評価を追跡する

対象外

このドキュメントは、完全にPrometheus互換の監視システムに必要なすべての機能を説明することを意図していません。特に、以下の領域は仕様の最初のバージョンでは対象外です。

「up」メトリック 「up」メトリックの定義とセマンティクスは、リモート書き込みプロトコルの範囲外であり、別途文書化されるべきです。

HTTPパス HTTPハンドラのパスは任意であり、送信者によって提供されるものとします。通常、URL全体が設定で指定されることを想定しています。

永続性 Prometheusリモート書き込み互換送信者は、受信者の障害が発生した場合にサンプルデータを永続的にバッファリングするべきです。

認証と暗号化 リモート書き込みはHTTPを使用するため、認証と暗号化はトランスポート層の問題と見なします。送信者と受信者は、一般的なものすべて(Basic認証、TLSなど)をサポートすべきであり、カスタム認証オプションを自由に追加できます。Prometheusリモート書き込み送信者および最終的なエージェントでのカスタム認証のサポートは想定されるべきではありませんが、可能な限り一般的で広く使用されている認証プロトコルをサポートするよう努力します。

リモート読み込み これは、すでにいくつかのイテレーションを経ており、あまり広く使用されていない別のインターフェースです。

シャーディング Prometheusにおけるリモート書き込み並列化のための現在のシャーディングスキームは、実装の詳細であり、仕様の一部ではありません。送信者が並列化を実装する場合、シリーズごとのサンプル順序を保持するものとします。

バックフィル 仕様では、どれだけ古いシリーズをプッシュできるかに制限を設けていませんが、サーバー/実装固有の制約が存在する場合があります。

制限 ラベルの数と長さ、バッチサイズなどに関する制限は、このドキュメントの範囲外ですが、実装は合理的な制限を課すことが予想されます。

プッシュベースのPrometheus アプリケーションがPrometheusリモート書き込み互換受信者にメトリックをプッシュすることは、このシステムの設計目標ではありませんでした。これは別のドキュメントで検討されるべきです。

ラベル 各シリーズには「job」および/または「instance」ラベルが含まれてもかまいません。これらは通常、送信者でのサービスディスカバリによって追加されるためです。これらは必須ではありません。

将来の計画

このセクションには、プロトコル仕様の一部とは見なされない投機的な計画が含まれていますが、完全性のためにここに記載されています。

トランザクション性 Prometheusは「トランザクション的」であることを目指しています。つまり、部分的にスクレイプされたターゲットをクエリに公開しないということです。リモート書き込みについても同様の意図があります。例えば、将来的にはリモート書き込みをスクレイプと「整合」させ、単一のスクレイプのすべてのサンプル、メタデータ、およびエクセンプラーを単一のリモート書き込みリクエストで送信できるようにしたいと考えています。これはまだ設計中です。

メタデータとエクセンプラー 上記と同様に、スクレイプされたサンプルとともにメタデータ(型情報、ヘルプテキスト)とエクセンプラーも送信します。これを単一のリモート書き込みリクエストにまとめる計画です。将来のバージョンの仕様では、これを義務付けるかもしれません。Prometheusは現在、メタデータとエクセンプラーの送信を実験的にサポートしています。

最適化 ラベル名と値の繰り返しを排除することにより、メッセージサイズを削減するための様々な最適化を調査したいと考えています。

互換性のある送信者と受信者

この仕様は、以下のコンポーネントがどのように相互作用するかを説明することを意図しています(2023年4月現在)。

FAQ

なぜgRPCを使用しなかったのですか? 面白いことに、当初はgRPCを使用していましたが、2016年当時はELBを通過させるのが難しかったため、HTTP上のProtoに切り替えました。https://github.com/prometheus/prometheus/issues/1982

なぜprotobufメッセージをストリーミングしないのですか? 永続的なHTTP/1.1接続を使用すれば、ストリーミングにかなり近い状態になります…もちろん、ヘッダーは再送信される必要がありますが、新しいTCPセットアップよりもコストはかかりません。

なぜサンプルを順番に送信するのですか? 順序制約は、Prometheusで時系列データに使用するエンコーディングに由来します。その実装は追記のみです。この制約は、例えばサンプルをバッファリングし、エンコード前に並べ替えることによって削除することが可能です。プロトコルの将来のバージョンでこれを調査することができます。

順序制約がある状況で、どのようにリクエストを並列化できますか? サンプルは、特定のシリーズに対しては順序通りである必要があります。リモート書き込みリクエストは、異なるシリーズのものであれば並列に送信できます。Prometheusでは、サンプルをラベルによって異なるキューにシャードし、各キュー内で書き込みを順次実行します。これにより、同じシリーズのサンプルは順序通りに配信されますが、異なるシリーズのサンプルは並列に送信され、異なるシリーズ間では「順不同」になる可能性があります。

受信者が順不同のサンプルをサポートできたとしても、Prometheus、Cortex、Thanosに送信できないため、エージェントが順不同で送信することはできません。エコシステムの整合性を確保し、コミュニティを「Prometheusに書き込めるPrometheusエージェント」と「そうでないもの」に混乱させたり、分裂させたりするのを防ぐためにこれを行っています。

このドキュメントはオープンソースです。問題報告やプルリクエストを提出して改善にご協力ください。