Exporterの作成

自身のコードにインストルメンテーションを行う場合、Prometheusクライアントライブラリでコードにインストルメンテーションを行う一般的なルールに従うべきです。別の監視またはインストルメンテーションシステムからメトリックを取得する場合、物事はそれほど単純ではありません。

このドキュメントには、Exporterやカスタムコレクターを作成する際に考慮すべき事項が含まれています。理論は、直接インストルメンテーションを行う人にとっても興味深いものとなるでしょう。

Exporterを作成していて、このドキュメントのいずれかの点について不明な場合は、IRC (#prometheus on libera) または メーリングリストでお問い合わせください。

保守性と純粋性

Exporterを作成する際に、あなたが下さなければならない主な決定は、完璧なメトリックを取得するためにどれだけの労力を惜しまないかということです。

問題となっているシステムが数個のメトリックしかなく、それらがほとんど変化しない場合、すべてを完璧にすることは簡単な選択です。これは HAProxy exporter の良い例です。

一方、システムに数百のメトリックがあり、新しいバージョンで頻繁に変化する場合にすべてを完璧にしようとすると、多大な継続的な作業を自分自身に課していることになります。 MySQL exporter はこのスペクトルのこの端にあります。

node exporter はこれらの中間であり、モジュールによって複雑さが異なります。例えば、mdadm コレクターはファイルをハンドパースし、そのコレクター専用に作成されたメトリックを公開します。そのため、メトリックを正しく取得しても問題ありません。meminfo コレクターの場合、カーネルバージョンによって結果が異なるため、有効なメトリックを作成するために必要最低限の変換を行います。

設定

アプリケーションを扱う場合、アプリケーションの場所を伝える以外に、ユーザーがカスタム設定を必要としない Exporter を目指すべきです。また、大規模なセットアップでは粒度が細かすぎたり高コストになる可能性のあるメトリックをフィルタリングする機能を提供する必要があるかもしれません。例えば、HAProxy exporter はサーバーごとの統計情報のフィルタリングを可能にしています。同様に、デフォルトで無効になっている高コストのメトリックがあるかもしれません。

他の監視システム、フレームワーク、プロトコルを扱う場合、Prometheusに適したメトリックを生成するために追加の設定やカスタマイズが必要になることがよくあります。最良のシナリオでは、監視システムがPrometheusと十分に類似したデータモデルを持っているため、メトリックの変換方法を自動的に決定できます。これは、CloudwatchSNMPcollectd で可能です。最大でも、ユーザーがどのメトリックをプルするかを選択できるようにする必要があります。

その他の場合、システムからのメトリックは完全に標準外であり、システムの利用状況や基盤となるアプリケーションに依存します。その場合、ユーザーはメトリックの変換方法を指示する必要があります。 JMX exporter はここで最悪の例であり、Graphite および StatsD exporter もラベルを抽出するための設定が必要です。

Exporterが設定なしでそのまま動作することを保証し、必要に応じて変換のための設定例をいくつか提供することが推奨されます。

YAMLは標準的なPrometheus設定フォーマットです。すべての設定はデフォルトでYAMLを使用する必要があります。

メトリック

命名

メトリック名のベストプラクティスに従ってください。

一般的に、メトリック名はPrometheusに精通しているが特定のシステムに詳しくない人が、メトリックの意味を推測できるようなものであるべきです。http_requests_total という名前のメトリックはそれほど有用ではありません。これは、入ってくるリクエストとして測定されているのか、何らかのフィルタで測定されているのか、あるいはユーザーのコードに到達したときに測定されているのか?requests_total はさらに悪く、どのような種類のリクエストなのか?

直接インストルメンテーションでは、特定のメトリックは1つのファイル内に存在する必要があります。同様に、Exporterやコレクター内では、メトリックは1つのサブシステムにのみ適用され、それに従って名前が付けられるべきです。

メトリック名は、カスタムコレクターまたはExporterを作成する場合を除き、手続き的に生成されるべきではありません。

アプリケーションのメトリック名は、一般的にExporter名でプレフィックスを付けるべきです。例: haproxy_up

メトリックは基本単位(秒、バイトなど)を使用し、より読みやすい単位への変換はグラフツールに任せる必要があります。どの単位を使用するにしても、メトリック名中の単位は使用中の単位と一致しなければなりません。同様に、パーセンテージではなく比率を公開してください。さらに良いのは、比率の2つの構成要素それぞれにカウンターを提供することです。

メトリック名は、ラベルが集約されて削除された場合に意味をなさなくなる可能性があるため、エクスポートされるラベルを含めるべきではありません。例: by_type

唯一の例外は、複数のメトリックで同じデータを異なるラベルでエクスポートしている場合です。その場合、通常はそれらを区別する最も賢明な方法です。直接インストルメンテーションの場合、これは、すべてのラベルを持つ単一のメトリックをエクスポートするとカーディナリティが高すぎると問題になる場合にのみ発生するはずです。

Prometheusのメトリックとラベルの名前はsnake_caseで記述されます。camelCasesnake_caseに変換することは望ましいですが、自動的に行うとmyTCPExampleisNaNのようなものには常に適切な結果が得られるとは限らないため、そのままにしておくのが最善な場合もあります。

公開されるメトリックにはコロンを含めてはなりません。コロンは、集約時にユーザー定義の記録ルールが使用するために予約されています。

メトリック名には[a-zA-Z0-9:_]のみが有効です。

_sum_count_bucket_total の接尾辞は、Summary、Histogram、Counterで使用されます。それらのいずれかを生成しない限り、これらの接尾辞は避けてください。

_total はカウンターの慣例です。COUNTER タイプを使用する場合は、これを使用する必要があります。

process_ および scrape_ プレフィックスは予約されています。一致するセマンティクスに従う場合は、これらのプレフィックスに独自のプレフィックスを追加しても問題ありません。たとえば、Prometheusにはスクレイプにかかった時間を示すscrape_duration_secondsがありますが、Exporter固有のメトリック、たとえば exporter自体が処理にかかった時間を示すjmx_scrape_duration_secondsを持つのが良い習慣です。プロセス統計情報でPIDにアクセスできる場合、GoとPythonの両方でこれを処理するコレクターが提供されています。この良い例は、HAProxy exporter です。

成功したリクエスト数と失敗したリクエスト数がある場合、これを公開する最良の方法は、合計リクエスト用の1つのメトリックと、失敗したリクエスト用の別のメトリックとして公開することです。これにより、失敗率を簡単に計算できます。失敗または成功ラベルを持つ1つのメトリックを使用しないでください。同様に、キャッシュのヒットまたはミスの場合も、合計用の1つのメトリックとヒット用の別のメトリックがある方が良いです。

監視を使用する人がメトリック名をコードまたはウェブで検索する可能性を考慮してください。名前が非常に確立されており、それらの名前に慣れた人々の範囲外で使用される可能性が低い場合(たとえば、SNMPやネットワークエンジニア)、そのままにしておくのが良いかもしれません。このロジックはすべての Exporter に当てはまるわけではありません。たとえば、MySQL exporter のメトリックは、DBAだけでなく、さまざまな人が使用する可能性があります。元の名前を持つHELP文字列は、元の名前を使用するのとほぼ同じ利点を提供できます。

ラベル

ラベルに関する一般的なアドバイスを読んでください。

type をラベル名として使用するのは避けてください。これは非常に一般的で、しばしば意味がありません。また、可能な限り、regionzoneclusteravailability_zoneazdatacenterdcownercustomerstageserviceenvironmentenv のように競合しやすい名前を避けるようにしてください。ただし、アプリケーションがリソースをそのように呼んでいる場合は、名前を変更して混乱を招くよりも、そのままにするのが最善です。

単一のメトリックにすべてを詰め込みたいという誘惑を避けてください。何かが1つのメトリックとして意味をなすと確信していない限り、複数のメトリックの方が安全です。

ラベル le はヒストグラムにとって特別な意味を持ち、quantile はサマリーにとって特別な意味を持ちます。これらのラベルは一般的に避けてください。

読み取り/書き込みおよび送信/受信は、ラベルではなく、別々のメトリックとして扱うのが最善です。これは通常、どちらか一方だけに関心があり、そのように使用する方が簡単だからです。

経験則として、1つのメトリックは合計または平均で意味をなす必要があります。Exporterに関してもう1つのケースがあり、それはデータが基本的に表形式であり、そうでなければユーザーがメトリック名を正規表現で処理する必要がある場合です。マザーボードの電圧センサーを考えてみてください。それらを横断して数学を行うことは無意味ですが、センサーごとに1つのメトリックを持つよりも、1つのメトリックにそれらを含めることは理にかなっています。メトリック内のすべての値は(ほぼ)常に同じ単位であるべきです。たとえば、ファン速度と電圧が混在していて、自動的に分離する方法がない場合を考えてみてください。

これはしないでください

my_metric{label="a"} 1
my_metric{label="b"} 6
my_metric{label="total"} 7

またはこれも

my_metric{label="a"} 1
my_metric{label="b"} 6
my_metric{} 7

前者は、メトリックに対してsum()を実行するユーザーのために破損し、後者は合計を破損し、扱いにくいです。たとえばGoのような一部のクライアントライブラリは、カスタムコレクターで後者を行うのを積極的に阻止しますが、すべてのクライアントライブラリは直接インストルメンテーションで後者を行うのを阻止すべきです。これらはいずれも絶対に行わず、Prometheusの集約に依存してください。

監視がこのよう総計を公開する場合、総計を削除してください。何らかの理由で(たとえば、総計に個別にカウントされないものが含まれる場合)それを保持する必要がある場合は、異なるメトリック名を使用してください。

インストルメンテーションラベルは最小限にすべきです。追加のラベルは、ユーザーがPromQLを記述する際に考慮しなければならないものが1つ増えることになります。したがって、タイムシリーズの一意性に影響を与えることなく削除できるインストルメンテーションラベルは避けてください。メトリックに関する追加情報は、情報メトリックを介して追加できます。バージョン番号の処理方法については、以下を参照してください。

ただし、ほとんどすべてのユーザーが追加情報を望むことが期待されるケースがあります。その場合、非一意なラベルを追加することが、情報メトリックの代わりとなる適切な解決策です。たとえば、mysqld_exportermysqld_perf_schema_events_statements_totaldigest ラベルは、完全なクエリパターンのハッシュであり、一意性には十分です。しかし、人間が読める digest_text ラベルなしではほとんど役に立ちません。これは、長いクエリの場合、クエリパターンの開始部分しか含まれず、一意ではないためです。そのため、人間用の digest_text ラベルと、一意性用の digest ラベルの両方があります。

ターゲットラベル、静的なスクレイプ済みラベルではない

すべてのメトリックに同じラベルを適用したいと思った場合は、停止してください。

これは通常2つのケースで発生します。

最初のケースは、ソフトウェアのバージョン番号のような、メトリックに有用なラベルがある場合です。代わりに、https://www.robustperception.io/how-to-have-labels-for-machine-roles/で説明されているアプローチを使用してください。

2番目のケースは、ラベルが実際にはターゲットラベルである場合です。これらは、アプリケーション自体からではなく、インフラストラクチャの設定から来る、リージョン、クラスタ名などです。アプリケーションがラベル分類にどのように適合するかをアプリケーションが言うのではなく、Prometheusサーバーを実行している人が設定するものであり、同じアプリケーションを監視している異なる人々がそれに異なる名前を付ける可能性があります。

したがって、これらのラベルは、使用しているサービスディスカバリを介して、Prometheusのスクレイプ設定に属します。マシンロールの概念をここにも適用しても問題ありません。なぜなら、少なくとも一部の人々がスクレイプする際には有用な情報である可能性が高いからです。

タイプ

メトリックのタイプをPrometheusのタイプに合わせるようにしてください。これは通常、カウンターとゲージを意味します。Summaryの_count_sumも比較的一般的であり、場合によってはQuantileが見られます。Histogramはまれです。それに出くわした場合は、公開フォーマットが累積値を公開することを覚えておいてください。

多くの場合、メトリックのタイプが何であるかは明らかではありません。特に、メトリックのセットを自動的に処理している場合はなおさらです。一般的にUNTYPEDは安全なデフォルトです。

カウンターは減少しないため、Decrement可能な他のインストルメンテーションシステムからのカウンタータイプのメトリック(たとえばDropwizard metrics)がある場合、それはカウンターではなくゲージです。UNTYPEDは、カウンターとして使用された場合に誤解を招く可能性があるため、おそらくそこで使用するのに最適なタイプです。

ヘルプ文字列

メトリックを変換する場合、ユーザーが元のメトリックと、その変換を引き起こしたルールを追跡できるようにすることは有益です。コレクターまたはExporterの名前、適用されたルールのID、および元のメトリックの名前と詳細をヘルプ文字列に含めることで、ユーザーにとって大きな助けとなります。

Prometheusは、1つのメトリックに異なるヘルプ文字列を持つことを好みません。複数のメトリックから1つのメトリックを作成する場合、ヘルプ文字列に含めるメトリックを1つ選択してください。

これの例として、SNMP exporter は OID を使用し、JMX exporter はサンプルの mBean 名を挿入します。HAProxy exporter は手書きの文字列を持っています。node exporter にもさまざまな例があります。

あまり有用でない統計情報の削除

一部のインストルメンテーションシステムは、最小値、最大値、標準偏差に加えて、1分、5分、15分のレート、アプリケーション開始からの平均レート(Dropwizard metricsではmeanと呼ばれる)を公開します。

これらはあまり有用ではなく、 clutter を増やすだけなので、すべて削除すべきです。Prometheusは自分でレートを計算できます。通常、公開されている平均は指数関数的に減衰しているため、より正確です。最小値または最大値がいつ計算されたかはわかりません。標準偏差は統計的に無意味であり、計算が必要になった場合は常に平方和、_sum_count を公開できます。

Quantile にも同様の問題があります。これらを削除するか、Summary に入れるかを選択できます。

ドット付き文字列

多くの監視システムにはラベルがなく、代わりに my.class.path.mymetric.labelvalue1.labelvalue2.labelvalue3 のようなことを行います。

Graphite および StatsD exporter は、小さな設定言語でこれらの変換を共有します。他の exporter も同様の実装を行うべきです。この変換は現在Goで実装されており、別のライブラリに切り出すことでメリットが得られるでしょう。

コレクター

Exporterのコレクターを実装する際は、通常の直接インストルメンテーションアプローチを使用し、スクレイプごとにメトリックを更新することは絶対にしないでください。

毎回新しいメトリクスを作成してください。Goでは、Collect() メソッドで MustNewConstMetric を使用してこれを行います。Pythonについては https://github.com/prometheus/client_python#custom-collectors を参照してください。Javaでは、collectメソッドで List<MetricFamilySamples> を生成します。例については StandardExports.java を参照してください。

その理由は2つあります。第1に、2つのスクレイプが同時に発生する可能性があり、直接インストルメンテーションは事実上ファイルレベルのグローバル変数を使用するため、競合状態が発生します。第2に、ラベル値が消滅しても、引き続きエクスポートされます。

エクスポート自体を直接インストルメンテーションで計装することは問題ありません。例えば、転送された総バイト数や、エクスポートで実行された呼び出し数などです。blackbox exporterSNMP exporter のような、単一のターゲットに紐づかないエクスポートについては、特定のターゲットのスクレイプではなく、通常の /metrics 呼び出しでのみ公開されるべきです。

スクレイプ自体に関するメトリクス

スクレイプにかかった時間や処理したレコード数など、スクレイプに関するメトリクスをエクスポートしたい場合があります。

これらは、スクレイプというイベントに関するものであるため、ゲージとして公開されるべきです。メトリクス名はエクスポート名でプレフィックスされ、例えば jmx_scrape_duration_seconds のようになります。通常、_exporter は省略され、エクスポートが単なるコレクターとしても使用できる場合は、必ず省略します。

その他のスクレイプ「メタ」メトリクスは避けるべきです。例えば、スクレイプ回数のカウンターや、スクレイプ時間のヒストグラムなどです。エクスポートがこれらのメトリクスを追跡すると、Prometheus自体の自動生成されたメトリクスが重複します。これにより、すべてのエクスポートインスタンスのストレージコストが増加します。

マシンとプロセスのメトリクス

Elasticsearchなど、多くのシステムはCPU、メモリ、ファイルシステム情報などのマシンメトリクスを公開しています。Prometheusエコシステムでnode exporterがこれらを提供しているため、このようなメトリクスは削除すべきです。

Javaの世界では、多くのインストルメンテーションフレームワークがCPUやGCなどのプロセスレベルおよびJVMレベルの統計情報を公開しています。JavaクライアントとJMXエクスポーターは、DefaultExports.java を介して、これらを推奨される形式で既に含んでいます。したがって、これらも削除すべきです。

他の言語やフレームワークでも同様です。

デプロイメント

各エクスポートは、監視対象のアプリケーションインスタンスを1つだけ監視すべきであり、できれば同じマシン上で並んで実行されるべきです。つまり、HAProxyを実行するたびに、haproxy_exporterプロセスを実行します。Mesosワーカーを搭載した各マシンには、その上にMesos exporterを実行し、マシンに両方がある場合はマスター用にもう1つ実行します。

この理論的根拠は、直接インストルメンテーションではこれを行うことになるため、他のレイアウトでできる限りそれに近づけようとしているということです。これは、すべてのサービスディスカバリがエクスポートではなくPrometheusで行われることを意味します。これにより、Prometheusは、ユーザーがblackbox exporterでサービスをプローブできるようにするために必要なターゲット情報を持っているという利点もあります。

2つの例外があります。

1つ目は、監視対象のアプリケーションの隣で実行することが完全に無意味な場合です。SNMP、blackbox、IPMIエクスポートが主な例です。IPMIおよびSNMPエクスポートは、コードを実行することが不可能ないくつかのブラックボックスデバイスであるため(ただし、もしそれらにnode exporterを実行できればより良いでしょう)、また、blackboxエクスポートは、DNS名のようなものを監視しており、実行するものがない場合です。この場合、Prometheusは引き続きサービスディスカバリを実行し、スクレイプ対象のターゲットを渡すべきです。例については、blackboxおよびSNMPエクスポートを参照してください。

このタイプのexportは、現在Go、Python、Javaクライアントライブラリでのみ記述可能であることに注意してください。

2つ目の例外は、ランダムなインスタンスからいくつかの統計情報を取得しており、どのインスタンスに接続しているかを気にしていない場合です。例えば、データに対してビジネスクエリを実行してエクスポートしたいMySQLレプリカのセットを考えてみましょう。通常のロードバランシングアプローチを使用して1つのレプリカに接続するエクスポートを実行するのが最も賢明なアプローチです。

これは、マスター選挙が行われるシステムを監視している場合には適用されません。その場合、各インスタンスを個別に監視し、Prometheus内で「マスター性」を処理する必要があります。これは、必ずしもマスターが1つだけとは限らず、Prometheusの足元でターゲットを変更すると奇妙な動作を引き起こす可能性があるためです。

スケジューリング

メトリクスは、Prometheusがスクレイプするときにのみアプリケーションからプルされるべきであり、エクスポートは独自のタイマーに基づいてスクレイプを実行してはなりません。つまり、すべてのスクレイプは同期されるべきです。

したがって、公開するメトリクスにタイムスタンプを設定しないでください。Prometheusにそれを処理させます。タイムスタンプが必要だと思われる場合は、代わりにPushgatewayが必要な可能性があります。

メトリクスの取得が特に高価な場合(1分以上かかる場合)、キャッシュすることは許容されます。これは、HELP 文字列に記載する必要があります。

Prometheusのデフォルトのスクレイプタイムアウトは10秒です。エクスポートがこれを超えることが予想される場合は、ユーザー文書で明示的に言及する必要があります。

プッシュ

一部のアプリケーションや監視システムは、StatsD、Graphite、collectdのようにメトリクスをプッシュするだけです。

ここでは2つの考慮事項があります。

まず、メトリクスはいつ期限切れにしますか?CollectdとGraphiteに接続するものは両方とも定期的にエクスポートされ、停止したときにメトリクスを公開するのを停止したいです。Collectdは有効期限を設定する時間を含んでいますが、Graphiteはそうではないため、エクスポートのフラグとなります。

StatsDは少し異なります。メトリクスではなくイベントを扱っているからです。最善のモデルは、各アプリケーションの隣に1つのエクスポートを実行し、アプリケーションが再起動したときに状態がクリアされるように再起動することです。

次に、これらの種類のシステムは、ユーザーがデルタまたは生カウンターのいずれかを送信できるようにする傾向があります。Prometheusの一般的なモデルであるため、可能な限り生カウンターに依存してください。

サービスレベルのメトリクス(例:サービスレベルのバッチジョブ)については、イベント後にエクスポートして終了するようにエクスポートにPushgatewayにプッシュさせ、自分で状態を処理しないようにすべきです。インスタンスレベルのバッチメトリクスについては、まだ明確なパターンがありません。オプションは、node exporterのtextfileコレクターを悪用するか、インメモリ状態に依存する(再起動後も持続する必要がない場合はおそらく最善)か、textfileコレクターと同様の機能を実装することです。

失敗したスクレイプ

接続しているアプリケーションが応答しない、またはその他の問題がある場合に、失敗したスクレイプのパターンは現在2つあります。

1つ目は、5xxエラーを返すことです。

2つ目は、スクレイプが成功したかどうかに応じて0または1の値を持つmyexporter_up(例:haproxy_up)変数を持つことです。

HAProxyエクスポートがプロセス統計情報を提供しているように、失敗したスクレイプからまだ有用なメトリクスを取得できる場合は、後者の方が優れています。前者は、up が通常の動作をするため、ユーザーが対処しやすいですが、エクスポートがダウンしているのか、アプリケーションがダウンしているのかを区別できません。

ランディングページ

http://yourexporter/ にアクセスしたときに、エクスポート名と/metricsページへのリンクが表示されるシンプルなHTMLページがあると、ユーザーにとってより親切です。

ポート番号

ユーザーは同じマシンに多くのエクスポートとPrometheusコンポーネントを持っている可能性があるため、それを容易にするためにそれぞれに一意のポート番号があります。

https://github.com/prometheus/prometheus/wiki/Default-port-allocations で追跡しており、これは公開編集可能です。

エクスポートを開発する際に、次の空きポート番号を取得してください。できれば、一般公開する前に。まだリリースする準備ができていない場合は、ユーザー名とWIP(作業中)を記載しても構いません。

これは、ユーザーの生活を少し楽にするためのレジストリであり、特定のexportを開発するコミットメントではありません。内部アプリケーション用のexportについては、デフォルトのポート割り当て範囲外のポートを使用することをお勧めします。

発表

エクスポートを世界に発表する準備ができたら、メーリングリストにメールを送信し、利用可能なエクスポートのリストに追加するためにPRを送信してください。

このページの内容