この記事は、Managing high cardinality data from Prometheusの意訳です。
はじめに
Prometheusメトリクスにおける最大の課題の1つは、非常に詳細なデータの収集とそのデータの保存コストとのバランスを取ることです。詳細な属性を含むカーディナリティの高いデータは、データに対する貴重な洞察を提供し、サービス内の問題を特定するのに役立ちますが、その余分なデータには追加コストがかかります。この投稿では、Prometheusと New Relic を使用してカーディナリティの高いデータを管理し、コストを抑えながら必要な詳細なデータを取得する方法を学びます。
カーディナリティとは?
本題に入る前に少し用語を定義しておきましょう。カーディナリティという用語は、タイムスケールとデータ一意性を表現するためのメタデータがどれだけ詳細かを定義するために使用されます。カーディナリティの解像度を上げることは、より深く多角的な洞察を得ることができるため、非常に重要です。例えば、分散アプリケーションを監視する場合、ホスト名や地域などの一意の識別子を持つことが有用です。これらの属性のユニークな組み合わせは、カーディナリティの1つの単位となります。
カーディナリティが高すぎることによる課題
しかし、カーディナリティは、特にPrometheusのkubernetes_sd_config
のような、有用なカーディナリティと過剰なカーディナリティのバランスを取るために熟考された設定を必要とするソリューションを適用する場合、扱いにくいものになる可能性があります。例えば、kubernetes_sd_config
の特定の構成では、ホストIPアドレスやPodのUUIDを公開することがありますが、どちらも日々のモニタリングでは役に立たないことがよくあります。利用可能なすべてのメトリクスを取り込むことで、システム内のメトリクスデータを迅速に観察できるようになったとしても、過剰なデータを取り込んで保存するために高いコストがかかる可能性があります。
クラウドネイティブ環境を監視する場合、カーディナリティの突然のスパイクに対処しなければならないことがよくあります。これは、カーディナリティが中程度以下の指標が、突然カーディナリティの高い指標に変化することで起こります。この現象は、さまざまな偶発的な理由で発生する可能性があります。例えば、チームの誰かがuser_id
というラベルを追加したとします。Prometheusはラベルの値が変わるたびに新しい時系列を作成するため、膨大な量のデータが発生する可能性があります。
カーディナリティを調整する5つのアプローチ
このブログ記事では、Prometheus remote_write
設定を使ってNew Relicにデータを送信しながら、Prometheusサーバーが収集するメトリクスのカーディナリティを管理する方法を紹介します。具体的には以下のステップで分析していきます。
- Prometheusサーバーの既存のカーディナリティを確認する
- スクレイプジョブが持つカーディナリティを確認する
- remote_write設定から特定のジョブを除外する
- Prometheusジョブからカーディナリティを削減する
- Drop Rulesを使いNew Relic側でカーディナリティを削減する
なお、この記事を理解するにあたり、まずPrometheusサーバーの設定の基本を理解する必要があります。
Note:
本Post内で、PrometheusサーバーのURL例が記載されている箇所がありますが、皆さんの環境に合わせてのホスト名やポートNoを読み替えてアクセスしてください。
Tips:
Prometheus サーバーは再起動せずに設定をリロードできるようにしておくことができます。これは、新しい Prometheus 構成のデバッグを行う際に特に役立ちます。サーバーの起動時にコマンドラインオプション--web.enable-lifecycle
を含めておき、その後、curl -X POST localhost:9090/-/reload
を呼び出すことで構成を再ロードできます。
それでは、いよいよ本題に入ります。
1. Prometheus サーバーの既存のカーディナリティを確認する
まずはPrometheus側で現状を把握しましょう。次の PromQL クエリを使用して、さまざまな属性に基づいて情報を確認できます。
topk(10, count by (job)({job=~".+"}))
このクエリを実行すると、Prometheus はラベル ジョブによってセグメント化されたサーバーの現在のカーディナリティを表示します。ジョブが重大なカーディナリティを生成している場合は、次のクエリを使用してジョブ自体を調査できます。
topk(10, count by (__name__)({job='my-example-job'}))
このクエリは、my-example-job
というjob内のメトリクス名 (Prometheus では__name__
で表される) のカーディナリティを返します。
Prometheus remote_write
を使用して New Relic にデータを送信している場合、次の New Relic Query Language (NRQL) クエリを使用してジョブのカーディナリティを確認できます。
FROM Metric SELECT cardinality() FACET job, metricName SINCE today
NRQL は__name__
の代わりにmetricName
を使用していることに注意してください。
2. スクレイプジョブが持つカーディナリティを確認する
次に、高コストになっているジョブを特定しましょう。Prometheusでは、メトリクスに付与されている数が多いラベル名や時系列データ数の多いメトリクス名などを以下のURLで確認することができます。
http://localhost:9090/tsdb-status
前の画像を例とすると、http://localhost:9090/api/v1/label/client_id/values
にアクセスすることで、client_id
というラベルにドリルダウンすることができます。
次に、以下のクエリでどのジョブがどんなラベルを持っているかを確認することができます。
topk(10, count by (job)({<label name>=~'.+'}))
過剰なカーディナリティを生成しているラベルやジョブを特定したら、そのカーディナリティの影響を低減または制限するためのアプローチを行っていきましょう。
3. remote_write設定から特定のジョブを除外する
Prometheus remote_writeを初めて使用する場合は、「Prometheus remote_writeをセットアップして、 Prometheus データを New Relic アカウントに送信する」を参照してください。
Prometheusの特定のジョブからNew Relicにデータを送信したくない場合、scrape_configs
を更新してジョブを省略することができます。これにより、カーディナリティデータが送信され、予期せぬスパイクが発生するのを防ぐことができます。
例えば、Prometheusがデータストアにデータを送信しないようにするには、以下のwrite_relabel_configs
を設定に追加します。この例では、New Relicにデータを送信するのURLの例として使用しています。
...
remote_write:
- url: <https://metric-api.newrelic.com/prometheus/v1/write?prometheus_server=my-prom-server>
bearer_token: <your NR API key>
write_relabel_configs:
- action: 'drop'
source_labels: ['job']
regex: (job-to-omit)
...
この例は、job-to-omit
に一致するジョブを持つ全てのデータを削除する設定です。
また、keep
アクションを使用することで、残すべきジョブを指定することも可能です。
write_relabel_configs:
- action: 'keep'
source_labels: ['job']
regex: (<other job names to keep>)
...
アクションがkeep
に変更されていることに注意してください。keep
アクションは、正規表現に一致する値を持つラベルのみデータを保存します。逆に言えば一致しないラベルを含まないすべてのデータを削除します。どちらのアプローチも有効ですが、どちらか一方を設定する方が簡単な場合があります。
以下のように、PrometheusサーバーとNew Relic側でカーディナリティの違いを確認することができます。
- PromQL:
topk(10, count by (job)({job=~".+"}))
- NRQL:
FROM Metric SELECT cardinality() FACET job SINCE today
2つのクエリを比較することで、scrape jobのデータがNew Relicに送信されていないことを確認することができます。New Relic での使用状況に影響を与えることなく、新しく追加された scrape job のカーディナリティを評価することができます。
4. Prometheusジョブからカーディナリティを削減する
Prometheusでは、カーディナリティの高いラベルを削除したり、不要なデータをスクレイプしないようにすることで、ジョブのカーディナリティを下げることができます。具体的な例を見てみましょう。
ラベルをDropする
スクレイプする際にlabelkeep
とlabeldrop
を使用することで特定のラベルをドロップすることができます。これらの設定により、監視するデータのカーディナリティを調整することができます。ただし、出来上がったデータ(ディメンション)が意味のある一意に識別できることを確認する必要があります。でないとデータが混在したメトリクス値になってしまう可能性があります。例えば、2つの異なるエンティティを一意に識別するラベルを削除してしまうと、それらのエンティティのメモリやCPUなどのメトリクスが同一のデータとして扱われてしまい、矛盾した情報を収集してしまうことになり、どちらのエンティティの状態も理解できなくなってしまいます。
それでも、ラベルを削除することが有効な場合があります。以下はその例です。
scrape_configs:
- job_name: 'api-service'
...
relabel_configs:
- action: labeldrop
regex: (ip_address)
この設定は、正規表現ip_address
にマッチするラベルを対象とします。この例では、ip_address
ラベルを削除してもデータの一意性には影響しませんが、サービスのデプロイ時や再起動時にサービスのIPアドレスが再割り当てされた場合に、カーディナリティがスパイクする可能性を解消することができます。
完全にデータポイントをDropする
高いカーディナリティを管理するための1つの解決策は、そもそもscrapeジョブによって監視されるものを適切なものに調整することです。
ここでは、KubernetesクラスタのすべてのPodからメトリクスをスクレイピングしようとするPrometheusサーバーの設定例を示します。instance
というラベルにPos名が追加されているため、高いカーディナリティになります。
scrape_configs:
- job_name: 'k8s'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_name
action: replace
target_label: instance
ひとつのアプローチとして、特定のnamespaceのみをスクレイプ対象にすることでカーディナリティを減らすことができます。たとえば、ネームスペースmy-example-namespace
のPodのみ監視対象にする場合は、そのnamespaceの結果のみを監視するように設定を更新することができます。
以下の例のrelabel_configs
に追加された keep
アクションと正規表現に注目してください。
relabel_configs:
- source_labels:
- __meta_kubernetes_pod_name
action: replace
target_label: instance
- action: keep
source_labels:
- __meta_kubernetes_namespace
regex: (my-example-namespace)
5. Drop Rulesを使いNew Relic側でカーディナリティを削減する
例えば、長期保存のコストはかけずに直近は高いカーディナリティのデータを取得したいとします。New Relicでは、カーディナリティの高いデータを管理するためのオプションがいくつか用意されています。データポイントやアトリビュートを完全に削除することも、長期アグリゲートのみから削除することも可能です。長期アグリゲートから属性をドロップすることで、直近のデータを高カーディナリティデータを参照することを実現しつつ、長期保存用データのカーディナリティを削減することができます。
長期アグリゲートでは、メトリックデータを長期間クエリすることができます。New Relic では、これをリテンションと呼んでいます。メトリックデータの正確なリテンション時間は、Retention details for assorted data typesで確認することができます。長期アグリゲートから属性を削除した場合、それらの属性は 1日のカーディナリティ制限に抵触せずに30 日間はクエリ可能です。この30 日間の保持期間は、ドキュメントではraw metric data pointsとして解説されています。
New Relic のDrop Rulesを使用するには、NerdGraph を使用したデータのドロップを参照してください。
さいごに
いかがでしたか?Prometheusは非常にたくさんのメトリクスデータを簡単に収集できる一方、データの保存コストやカーディナリティがボトルネックになるケースがあります。本ポストが皆さんのカーディナティティチューニングの参考になれば幸いです。
次のステップ
New Relic でカーディナリティの高いデータの管理を開始する準備はできましたか?Prometheus remote_write統合のセットアップのドキュメントをご覧ください。
New Relic のアカウントをまだお持ちでない場合は、無料アカウントにサインアップしてください。アカウントには、100GB/月の無料データインジェスト、1人の無料フルアクセスユーザー、無制限の無料ベーシックユーザーが含まれています。
本ブログに掲載されている見解は著者に所属するものであり、必ずしも New Relic 株式会社の公式見解であるわけではありません。また、本ブログには、外部サイトにアクセスするリンクが含まれる場合があります。それらリンク先の内容について、New Relic がいかなる保証も提供することはありません。