はじめに
Prometheus(プロメテウス)は、SoundCloud社で2012年に開発が始まり、現在はCloud Native Computing Foundation (CNCF) のプロジェクトとして管理されているオープンソースの監視およびアラートツールキットです。元々はGoogleの内部システムBorgを監視するBorgmonにインスパイアされて開発されました。
Prometheusは、特にKubernetesなどのコンテナ化された環境やマイクロサービスアーキテクチャといった、いわゆるクラウドネイティブな環境での監視において、デファクトスタンダードとしての地位を確立しています。その理由は、多次元データモデル、強力なクエリ言語 (PromQL)、Pull型のメトリクス収集モデル、優れたサービスディスカバリ機能などが、動的で変化の激しい環境に非常に適しているためです。
この記事では、Prometheusの基本的な概念からアーキテクチャ、主要機能、そして実際の活用方法までを詳細に解説していきます。
Prometheusのアーキテクチャ
Prometheusのエコシステムは、いくつかのコンポーネントで構成されています。中心となるのはPrometheusサーバーですが、それ以外にも様々な役割を持つコンポーネントが存在します。
- Prometheus Server: メトリクスを収集 (Scrape) し、時系列データベース (TSDB) に保存するコアコンポーネントです。設定されたルールに基づいてアラートを生成することもできます。
- Exporters: 監視対象のシステムやサービスからメトリクスを収集し、Prometheusが取得できるHTTPエンドポイントとして公開する役割を持ちます。OS、データベース、Webサーバーなど、様々な種類のExporterが存在します。
- Client Libraries: アプリケーションコードに計装(Instrumentation)を組み込み、カスタムメトリクスを公開するためのライブラリです。Go, Java, Python, Rubyなど、多くの言語に対応しています。
- Pushgateway: 通常のPull型モデルでは収集が難しい、短命なバッチジョブなどのメトリクスをPush型で受け付けるための中間ゲートウェイです。
- Alertmanager: Prometheus Serverから送られてきたアラートを処理します。重複排除、グルーピング、ルーティング(Email, Slack, PagerDutyなどへの通知)などの機能を提供します。
- Service Discovery: 監視対象(ターゲット)を動的に発見する仕組みです。Kubernetes, Consul, EC2などのプラットフォームと連携し、設定ファイルを手動で更新することなく、変化する環境に対応できます。
- Data Visualization: Prometheus自体にも簡易的なグラフ表示機能(Expression Browser)がありますが、より高度な可視化のためにはGrafanaなどの外部ツールと連携するのが一般的です。
データの流れ:Pullモデル vs Pushモデル
Prometheusのメトリクス収集は、主にPullモデルを採用しています。これは、Prometheus Serverが設定された間隔で、各監視ターゲット(Exporterや計装されたアプリケーション)のHTTPエンドポイントにアクセスし、メトリクスを能動的に取得(Pull)する方式です。
Pullモデルの利点は以下の通りです。
- 中央集権的な制御: 監視の設定や収集間隔をPrometheus Server側で一元管理できます。
- ターゲットの状態確認: ターゲットへのアクセスが失敗した場合、ターゲット自体がダウンしていることを検知できます。
- 導入の容易さ: 監視対象側はHTTPエンドポイントを公開するだけで済みます。
一方で、ファイアウォール内のターゲットや、非常に短命なジョブ(完了したらすぐに終了してしまうため、Pullされる前に消えてしまう)など、Pullモデルが適さないケースもあります。このような場合には、Pushgatewayを利用したPushモデルが補助的に使用されます。ターゲットがPushgatewayにメトリクスを送信(Push)し、Prometheus ServerはPushgatewayをPullすることで、間接的にメトリクスを収集します。
Prometheusのデータモデル
Prometheusは、収集したすべてのデータを時系列データ (Time Series Data) として保存します。時系列データとは、タイムスタンプが付与された値のストリームであり、同じメトリクス名と、ラベル (Label) と呼ばれるキーバリューペアのセットによって一意に識別されます。このラベルによって、Prometheusは多次元データモデルを実現しています。
メトリクス名とラベル
各時系列データは、以下の要素で構成されます。
- メトリクス名 (Metric Name): 測定対象の一般的な特徴を示します (例:
http_requests_total
)。ASCII文字、数字、アンダースコア (_
)、コロン (:
) が使用可能ですが、正規表現[a-zA-Z_:][a-zA-Z0-9_:]*
に一致する必要があります。コロンは通常、ユーザー定義の記録ルール (Recording Rule) で使用されます。 - ラベル (Labels): キーバリューペア(例:
method="POST", handler="/api/users"
)で、メトリクスに次元を追加します。これにより、同じメトリクス名でも、特定の次元(例えば、POSTメソッドで/api/usersハンドラへのリクエスト)を識別できます。ラベル名にはASCII文字、数字、アンダースコアが使用可能で、正規表現[a-zA-Z_][a-zA-Z0-9_]*
に一致する必要があります。__
(アンダースコア2つ) で始まるラベル名は内部的に予約されています。ラベル値には任意のUnicode文字が使用可能です。
メトリクス名とラベルの組み合わせが、一意の時系列データを定義します。例えば、http_requests_total{method="GET", handler="/"}
と http_requests_total{method="POST", handler="/api"}
は、同じメトリクス名 http_requests_total
を持ちますが、ラベルが異なるため、別々の時系列データとして扱われます。
サンプル
各時系列データは、サンプル (Sample) の集合として保存されます。サンプルは以下の2つの要素から構成されます。
- 値 (Value): float64形式の数値。
- タイムスタンプ (Timestamp): ミリ秒単位の精度を持つUnixタイムスタンプ。
メトリクスタイプ
Prometheusは、主に以下の4つのメトリクスタイプを定義しています。これは主に、クライアントライブラリやExporterが提供するメトリクスの意味合いや用途を示すためのものです。
タイプ | 説明 | ユースケース例 |
---|---|---|
Counter (カウンター) | 単調増加する累積値。リセットされることはありますが、減少することはありません。通常、リクエスト数、完了したタスク数、エラー数などを数えるのに使われます。 | HTTPリクエストの総数 (http_requests_total ), CPU使用時間の累積秒数 (node_cpu_seconds_total ) |
Gauge (ゲージ) | 任意に上下する単一の数値。現在の値を示すのに使われます。 | 現在のメモリ使用量 (process_resident_memory_bytes ), キューに入っているジョブの数 (queue_length ), 温度 (temperature_celsius ) |
Histogram (ヒストグラム) | 観測値を設定されたバケット(区間)に分類し、その度数をカウントします。観測値の合計 (sum) と総数 (count) も同時に公開します。リクエストのレイテンシやレスポンスサイズなど、値の分布を把握したい場合に適しています。histogram_quantile() 関数を使ってパーセンタイル値を計算できます。 | HTTPリクエストのレイテンシ分布 (http_request_duration_seconds_bucket ), レスポンスサイズの分布 |
Summary (サマリー) | ヒストグラムと同様に観測値の分布を測定しますが、クライアント側で設定された分位数(Quantile, φ-quantile)を直接計算して公開します。観測値の合計 (sum) と総数 (count) も公開します。ヒストグラムよりも計算コストが高いですが、正確な分位数が必要な場合に利用されます。 | サービスレベル目標 (SLO) で求められる99パーセンタイルのレイテンシなど。 |
PromQL (Prometheus Query Language)
PromQLは、Prometheusに格納された時系列データをリアルタイムに選択し、集計するための強力なクエリ言語です。グラフの描画やアラートルールの定義に使用されます。
基本構文
最も基本的なPromQLクエリは、メトリクス名そのものです。
http_requests_total
これにより、http_requests_total
という名前を持つすべての時系列データの最新のサンプル値(Instant Vector)が返されます。
セレクタとマッチャー
ラベルを使用してデータをフィルタリングするには、{}
内にラベルマッチャーを指定します。
http_requests_total{job="apiserver", handler="/api/v1"}
これは、job
ラベルが "apiserver"
であり、かつ handler
ラベルが "/api/v1"
である時系列データを選択します。
以下のマッチング演算子が利用可能です。
=
: 指定された文字列と完全に一致するラベルを選択します。!=
: 指定された文字列と等しくないラベルを選択します。=~
: 指定された正規表現に一致するラベルを選択します。!~
: 指定された正規表現に一致しないラベルを選択します。
http_requests_total{environment=~"production|staging"} # environmentが "production" または "staging"
http_requests_total{method!="GET"} # methodが "GET" ではない
レンジベクタ (Range Vector)
特定の期間のデータを取得するには、時間範囲セレクタ [ ]
を使用します。これによりレンジベクタが返されます。
http_requests_total{job="apiserver"}[5m] # 過去5分間のデータ
時間単位には、s
(秒), m
(分), h
(時), d
(日), w
(週), y
(年) があります。
関数と演算子
PromQLには多数の関数と演算子が用意されており、データの加工や集計が可能です。
rate(v range-vector)
: レンジベクタ内の時系列データについて、1秒あたりの平均増加率を計算します。主にカウンタータイプのメトリクスに使用されます。rate(http_requests_total{job="apiserver"}[5m]) # 過去5分間の1秒あたりの平均リクエスト数
increase(v range-vector)
: レンジベクタ内の指定された期間における増加量を計算します。カウンターの増加分を知りたいときに便利です。increase(http_requests_total{job="apiserver"}[1h]) # 過去1時間の総リクエスト増加数
sum(v instant-vector)
: インスタントベクタ内のすべての要素の合計値を計算します。sum(rate(http_requests_total[5m])) # 全ての時系列データのrateの合計
avg(v instant-vector)
: インスタントベクタ内のすべての要素の平均値を計算します。topk(k scalar, v instant-vector)
: 値が大きい方からk個の要素を返します。bottomk(k scalar, v instant-vector)
: 値が小さい方からk個の要素を返します。histogram_quantile(φ scalar, b instant-vector)
: ヒストグラムからφ分位数(0 ≤ φ ≤ 1)を計算します。histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) # 95パーセンタイルレイテンシ
- 算術演算子 (
+, -, *, /, %, ^
): ベクトル間の演算を行います。 - 比較演算子 (
==, !=, >, <, >=, <=
): ベクトル間の比較を行います。 - 論理演算子 (
and, or, unless
): ベクトル間の論理演算を行います。
集計 (Aggregation)
sum
, avg
, min
, max
, count
などの集計演算子を使用して、複数の時系列データを集約できます。by
または without
句を使って、集計時に保持するラベルを指定できます。
sum by (job) (rate(http_requests_total[5m])) # jobラベルごとにrateの合計を計算
avg without (instance) (node_load1) # instanceラベルを除外して平均負荷を計算
オフセット修飾子 (Offset Modifier)
offset
キーワードを使うと、過去のある時点のデータを取得できます。
sum(rate(http_requests_total[5m] offset 1h)) # 1時間前の時点での過去5分間のrateの合計
Exporterによるメトリクス収集
多くのシステムやサービスは、デフォルトではPrometheus形式のメトリクスを公開していません。そこでExporterの出番となります。Exporterは、既存のシステム(OS、データベース、メッセージキュー、ハードウェアなど)からメトリクスを収集し、Prometheusがスクレイプ可能なHTTPエンドポイント(通常は /metrics
パス)で公開する専用のソフトウェアです。
Exporterは、監視対象のシステム上で個別のプロセスとして実行されるか、場合によっては既存のサービスに組み込まれていることもあります。
公式・コミュニティ Exporter
Prometheusコミュニティによって、非常に多くのExporterが開発・公開されています。代表的なものには以下のようなものがあります。
Exporter | 説明 |
---|---|
Node Exporter (official) | Linuxやその他の*nix系OSのハードウェアおよびOS関連メトリクス(CPU、メモリ、ディスク、ネットワークなど)を公開します。最も広く利用されているExporterの一つです。 |
Blackbox Exporter (official) | エンドポイント(HTTP, HTTPS, DNS, TCP, ICMPなど)を外部から監視(ブラックボックス監視)し、その応答時間やステータスなどをメトリクスとして公開します。サービスが外部から利用可能かどうかの確認に役立ちます。 |
MySQLd Exporter (official) | MySQLサーバーの状態やパフォーマンスに関するメトリクスを公開します。 |
PostgreSQL Exporter | PostgreSQLデータベースのメトリクスを公開します。 |
Redis Exporter | Redisサーバーのメトリクスを公開します。 |
JMX Exporter | JMX (Java Management Extensions) を介してJVMベースのアプリケーション(Kafka, Cassandraなど)のメトリクスを公開します。 |
HAProxy Exporter (official) | HAProxyロードバランサーの統計情報を公開します。 |
Nginx VTS Exporter | NginxのVirtual Host Traffic Status (VTS) モジュールからメトリクスを収集します。 |
Consul Exporter (official) | Consulクラスタのヘルスチェックやサービス情報をメトリクスとして公開します。 |
その他にも、データベース、メッセージングシステム、ストレージ、ハードウェア、API、ログなど、多種多様なExporterが存在します。公式のExporters and integrationsページにリストがあります。
また、一部のソフトウェア(例: etcd, Kubernetes, Grafana, RabbitMQ, Envoy)は、Exporterを必要とせず、ネイティブでPrometheus形式のメトリクスを公開しています。
カスタムExporterの作成
監視したいシステムに対応するExporterが存在しない場合は、自分でカスタムExporterを作成することも可能です。Prometheusは多くの言語に対応したクライアントライブラリを提供しているため、比較的容易に開発できます。基本的には、監視対象システムからデータを取得し、それをPrometheusのメトリクス形式(テキストベース)に変換してHTTPサーバーで公開する、という処理を実装します。
Alertmanagerによるアラート通知
Prometheusは監視対象のメトリクスを収集・保存するだけでなく、そのデータに基づいてアラートを発生させることができます。Prometheus Server自体はアラートルールを評価し、条件に一致した場合にアラートを「Firing(発火中)」状態にしますが、実際の通知処理(重複排除、グルーピング、ルーティング、抑制など)はAlertmanagerという別のコンポーネントが担当します。
アラートルールの定義
アラートルールは、Prometheusの設定ファイル (prometheus.yml
) または別のルールファイルで定義されます。ルールはPromQL式を用いて記述され、特定の条件が満たされた場合にアラートが発火します。
以下はアラートルールの例です。
groups:
- name: node_alerts rules: - alert: HighCpuUsage expr: avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) < 0.2 for: 10m labels: severity: warning annotations: summary: "High CPU usage on instance {{ $labels.instance }}" description: "{{ $labels.instance }} has been using more than 80% CPU for the last 10 minutes." - alert: InstanceDown expr: up == 0 for: 5m labels: severity: critical annotations: summary: "Instance {{ $labels.instance }} down" description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
この例では、以下の2つのルールが定義されています。
- HighCpuUsage: インスタンスごとのCPUアイドル率の5分間の平均が20%未満(つまり使用率が80%超)の状態が10分間続いた場合に
warning
レベルのアラートを発火します。 - InstanceDown:
up
メトリクス(Prometheusがターゲットをスクレイプできたかどうかを示す)が0(ダウン)の状態が5分間続いた場合にcritical
レベルのアラートを発火します。
expr
にはPromQL式、for
にはアラートが発火するまでの継続時間、labels
にはアラートに付与するカスタムラベル、annotations
には通知内容に含める詳細情報(タイトルや説明など)を記述します。テンプレート構文 ({{ $labels.instance }}
) を使用して、ラベルの値を通知に埋め込むことができます。
Alertmanagerの設定
Alertmanagerは、Prometheus Serverからアラートを受け取り、設定ファイル (alertmanager.yml
) に基づいて処理を行います。
route: receiver: 'default-receiver' group_by: ['alertname', 'cluster'] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: - receiver: 'slack-notifications' match_re: severity: critical|warning continue: true - receiver: 'pagerduty-notifications' match: severity: critical
receivers:
- name: 'default-receiver' # デフォルトでは何もしない (null receiver)
- name: 'slack-notifications' slack_configs: - api_url: 'https://hooks.slack.com/services/...' channel: '#alerts' send_resolved: true title: '[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .CommonLabels.alertname }} for {{ .CommonLabels.job }}' text: "{{ range .Alerts -}}\n*Alert:* {{ .Annotations.summary }}\n*Description:* {{ .Annotations.description }}\n*Details:* {{ range .Labels.SortedPairs }} {{ .Name }}={{ .Value }}{{ end }}\n{{ end }}"
- name: 'pagerduty-notifications' pagerduty_configs: - service_key: 'YOUR_PAGERDUTY_INTEGRATION_KEY'
inhibit_rules:
- source_match: severity: 'critical' target_match: severity: 'warning' equal: ['alertname', 'cluster', 'service']
主な設定項目は以下の通りです。
route
: アラートのルーティングツリーのルートを定義します。receiver
: デフォルトの通知先レシーバー。group_by
: アラートをグループ化するためのラベル。同じグループのアラートはまとめて通知されます。group_wait
: 最初のアラート発生後、グループ化のために待機する時間。group_interval
: 同じグループの新しいアラートについて通知するまでの待機時間。repeat_interval
: 一度通知したアラートを再通知するまでの間隔。routes
: サブルートのリスト。条件 (match
,match_re
) に基づいて、特定のレシーバーにルーティングします。continue: true
で、マッチした場合でも後続のサブルートの評価を続行します。
receivers
: 通知先の設定(Slack, PagerDuty, Email, Webhookなど)。通知テンプレートもここで定義できます。inhibit_rules
: 特定のアラートが発生している場合に、別のアラートを抑制(Inhibit)するためのルール。例えば、より深刻なアラート(例: クラスタ全体がダウン)が発生している場合、それに関連する個別のアラート(例: 個別インスタンスのダウン)を抑制することができます。
高可用性 (HA)
Alertmanagerはクラスタリングをサポートしており、複数のAlertmanagerインスタンスを連携させて高可用性構成を組むことができます。インスタンス間でゴシッププロトコルを用いて状態を同期し、通知の重複を防ぎます。Prometheus Serverの設定では、複数のAlertmanagerインスタンスをリストとして指定します。
Service Discovery
クラウドネイティブ環境では、コンテナや仮想マシンが頻繁に作成・削除され、IPアドレスも動的に変化します。このような環境で、監視対象(ターゲット)の設定を静的に管理するのは非常に困難です。PrometheusのService Discovery (サービスディスカバリ) 機能は、この問題を解決します。
Service Discoveryは、様々なプラットフォーム(Kubernetes, AWS EC2, Azure VM, GCP GCE, Consulなど)と連携し、利用可能なターゲットを自動的に検出し、Prometheusの監視対象リストを動的に更新します。
静的設定 (Static Config)
最も基本的な方法は、設定ファイル (prometheus.yml
) にターゲットのリストを直接記述する静的設定です。
scrape_configs: - job_name: 'node' static_configs: - targets: ['192.168.1.10:9100', 'node-exporter.example.com:9100']
これは小規模な環境や、IPアドレスが固定されているターゲットには有効ですが、動的な環境には適していません。
動的検出
Prometheusは多くの組み込みService Discoveryメカニズムを提供しています。以下はその一部です。
kubernetes_sd_configs
: Kubernetes APIサーバーと通信し、Pod, Service, Endpoint, Node, Ingressなどのリソースを発見します。ラベルセレクタなどを使って、特定の条件に合うリソースのみをターゲットにすることができます。ec2_sd_configs
: AWS EC2 APIと通信し、EC2インスタンスを発見します。タグやリージョンなどでフィルタリングできます。azure_sd_configs
: Azure APIと通信し、仮想マシンを発見します。gce_sd_configs
: Google Cloud APIと通信し、GCEインスタンスを発見します。consul_sd_configs
: ConsulのカタログAPIと通信し、登録されているサービスを発見します。dns_sd_configs
: DNSのSRVレコードやA/AAAAレコードを問い合わせてターゲットを発見します。file_sd_configs
: 指定されたファイル(JSONまたはYAML形式)からターゲットのリストを読み込みます。外部のツールやスクリプトでターゲットリストを生成し、ファイルに書き出すことで連携できます。
Kubernetes環境での設定例:
scrape_configs: - job_name: 'kubernetes-pods' kubernetes_sd_configs: - role: pod relabel_configs: # 特定のアノテーションを持つPodのみをスクレイプ対象とする例 - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] action: keep regex: true # アノテーションからスクレイプパスを取得する例 - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] action: replace target_label: __metrics_path__ regex: (.+) # ポート番号を指定する例 - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port] action: replace regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 target_label: __address__ # PodのラベルをPrometheusのラベルとして追加する例 - action: labelmap regex: __meta_kubernetes_pod_label_(.+)
この設定では、kubernetes_sd_configs
を使用してPodを発見しています。さらに、relabel_configs
を使って、発見されたターゲットに対して様々な処理を行っています。
action: keep
: 特定のラベル(この例ではprometheus.io/scrape=true
アノテーション)を持つターゲットのみを保持し、他を破棄します。action: replace
: ラベルの値を書き換えます。ここでは、アノテーションで指定されたパスやポート番号を、Prometheusがスクレイプに使用する内部ラベル (__metrics_path__
,__address__
) に設定しています。action: labelmap
: Kubernetesリソースのラベル(__meta_kubernetes_pod_label_*
)を、Prometheusのメトリクスラベルとして自動的に追加します。
Relabelingは非常に強力な機能で、Service Discoveryで取得したメタデータを利用して、ターゲットのフィルタリング、ラベルの追加・変更・削除などを柔軟に行うことができます。
高可用性とスケーラビリティ
Prometheusは単一サーバーでも高いパフォーマンスを発揮しますが、大規模環境や高い可用性が求められる環境では、いくつかの戦略を組み合わせて利用する必要があります。
高可用性 (HA) ペア
最もシンプルな高可用性構成は、全く同じ設定を持つ2台以上のPrometheusサーバーを並行して実行することです。各サーバーは同じターゲットをスクレイプし、同じアラートルールを評価します。Alertmanagerは、同じラベルセットを持つ重複したアラートを受け取ると、それらを重複排除して1つの通知にまとめてくれるため、ユーザーには単一のアラートとして届きます。
この構成はアラートの可用性を高めますが、クエリ(Grafanaなどからの参照)については、どちらか一方のサーバーに接続するか、ロードバランサーを介する必要があります。また、各サーバーは独立してデータを保持するため、片方のサーバーがダウンした場合、その期間のデータは失われます(もう一方のサーバーにはデータがあります)。
Federation (フェデレーション)
Federationは、あるPrometheusサーバーが、別のPrometheusサーバーから選択した時系列データをスクレイプする機能です。
主なユースケースは2つあります。
- 階層的フェデレーション (Hierarchical Federation): 大規模な環境(複数のデータセンターなど)でスケーラビリティを実現するために使用されます。各データセンターやクラスタごとに「ローカル」なPrometheusサーバーを配置し、詳細なメトリクス(インスタンスレベル)を収集します。そして、「グローバル」なPrometheusサーバーが、これらのローカルサーバーから集約されたメトリクス(ジョブレベルなど)を選択的にスクレイプします。これにより、グローバルな視点での監視と、ローカルな詳細レベルでの監視を両立できます。
- サービス間フェデレーション (Cross-Service Federation): 関連するメトリクスを1つのサーバーに集約するために使用されます。例えば、クラスタ全体のインフラメトリクスを収集するPrometheusサーバーと、特定のアプリケーションメトリクスを収集するPrometheusサーバーがあるとします。アプリケーション側のPrometheusサーバーが、インフラ側のサーバーから自身のサービスに関連するメトリクス(CPU使用率など)をフェデレーションで取得することで、アプリケーションメトリクスとインフラメトリクスを組み合わせて分析したり、アラートを作成したりできます。
Federationの設定は、通常のスクレイプ設定に似ていますが、/federate
エンドポイントを使用し、match[]
パラメータで収集したいメトリクスを指定します。
scrape_configs: - job_name: 'federate-dc1-prometheus' scrape_interval: 60s honor_labels: true # フェデレーション元サーバーのラベルを優先する metrics_path: /federate params: 'match[]': - '{job="mysql_exporter"}' # mysql_exporterジョブの全メトリクス - '{__name__=~"job:.*"}' # jobレベルで集約された記録ルール static_configs: - targets: ['prometheus-dc1.example.com:9090']
Federationはスケーリングの一手法ですが、ネットワーク帯域の消費や、上位サーバーでのデータ集約による詳細度の低下といったトレードオフも存在します。また、基本的にPullモデルであるため、ネットワーク問題によるデータ欠損のリスクがあります。
Remote Read/Write API
Prometheusは、ローカルストレージに加えて、外部のストレージシステムと連携するためのRemote ReadおよびRemote Write APIを提供しています。
- Remote Write: Prometheusがスクレイプしたデータを、リアルタイムに外部のストレージシステムに送信(Push)する仕組みです。これにより、Prometheusのローカルストレージの制限(デフォルトでは15日間の保持期間、ディスク容量)を超えた長期保存や、よりスケーラブルなストレージバックエンドの利用が可能になります。
- Remote Write API は、Prometheusがスクレイプしたサンプルを指定されたエンドポイントにPOSTリクエストで送信します。データは効率的なプロトコルバッファ形式で送信され、再試行メカニズムも備わっていますが、リモートエンドポイントが長時間ダウンしているとデータが失われる可能性があります(デフォルトでは2時間)。
- Remote Read: Prometheusがクエリを実行する際に、ローカルストレージだけでなく、外部のストレージシステムからもデータを読み取る仕組みです。これにより、GrafanaなどのツールからPrometheusのクエリAPI (
/api/v1/query
など) を通じて、長期保存されたデータにアクセスできます。
Remote Read/Write APIに対応した代表的な外部ストレージシステムには以下のようなものがあります。
- Thanos: PrometheusのHA構成、長期保存、グローバルクエリビューを提供するオープンソースプロジェクト。SidecarコンポーネントがPrometheusサーバーのデータをオブジェクトストレージ(S3, GCSなど)にアップロードし、Queryコンポーネントが複数のPrometheusサーバーやThanos Store Gateway(オブジェクトストレージのデータを読む)からのデータを集約してクエリに応答します。
- Cortex: 水平スケーラブルでマルチテナント対応の長期保存ソリューション。
- M3DB: Uberによって開発された分散型時系列データベース。
- 各種クラウドベンダーのマネージドサービス(例: Google Cloud Managed Service for Prometheus, Amazon Managed Service for Prometheus)
Remote Writeの設定例:
remote_write: - url: "http://thanos-receiver.example.com:19291/api/v1/receive" queue_config: max_samples_per_send: 500 max_shards: 10 capacity: 10000
Remote Readの設定例:
remote_read: - url: "http://thanos-query.example.com:9090/api/v1/read" read_recent: true # ローカルストレージとリモートの両方から読む場合
Remote Writeは、Federationと比較して、基本的にすべてのデータ(フィルタリング可能)をPushするため、データ欠損のリスクが低く、よりリアルタイムに近いデータ連携が可能ですが、PrometheusサーバーのCPUやメモリ使用量が増加する傾向があります。
ThanosやCortexなどのソリューションを利用することで、Prometheus単体では難しい、真の水平スケーラビリティと長期的なデータ保持、そして複数のPrometheusインスタンスにまたがる単一のクエリインターフェース(Global Query View)を実現できます。
Prometheusのユースケースとベストプラクティス
一般的なユースケース
- インフラストラクチャ監視: Node Exporterなどを利用して、サーバーのCPU、メモリ、ディスクI/O、ネットワークトラフィックなどを監視します。
- アプリケーションパフォーマンス監視 (APM): クライアントライブラリを用いてアプリケーション内部のメトリクス(リクエストレイテンシ、エラーレート、スループット、キューの長さなど)を計装し、パフォーマンスのボトルネックや問題を特定します。
- Kubernetes監視: PrometheusはKubernetes環境の監視に非常に適しており、kube-state-metricsやcAdvisorなどからクラスターの状態、ノードリソース、Podの稼働状況などを詳細に監視できます。Prometheus Operatorを利用すると、Kubernetes上でのPrometheusのデプロイや設定管理が容易になります。
- サービスレベル目標 (SLO) / サービスレベル指標 (SLI) の追跡: PromQLを使用してSLI(例: 99パーセンタイルレイテンシ、可用性)を計算し、SLOに対する達成度を監視します。
- データベース監視: 各種データベースExporterを利用して、クエリパフォーマンス、接続数、レプリケーション遅延などを監視します。
- ネットワーク機器監視: SNMP Exporterなどを利用して、ルーターやスイッチのトラフィック、エラーレートなどを監視します。
- ブラックボックス監視: Blackbox Exporterを利用して、外部から見たサービスの可用性や応答時間を監視します。
ベストプラクティスと注意点
- メトリクスとラベルの命名規則に従う: 一貫性のある命名規則(例: 基本単位を含める、単語はアンダースコアで区切る、ラベル名は小文字スネークケース)を用いることで、PromQLクエリの作成やダッシュボードの理解が容易になります。
- 高カーディナリティラベルを避ける: ラベルの値の組み合わせ(カーディナリティ)が非常に多くなると、Prometheusのメモリ使用量やパフォーマンスに大きな影響を与えます。ユーザーID、リクエストID、セッションIDなど、ユニークな値を取りうる情報をラベルとして使用するのは避けるべきです。必要な場合は、集約ルールやRelabelingでカーディナリティを抑える工夫をします。
- Exporterの選択: 監視対象に適した、信頼性の高いExporterを選択します。公式Exporterや広くコミュニティで使われているものが推奨されます。
- 適切なスクレイプ間隔の設定: 短すぎる間隔はPrometheusサーバーとターゲット双方の負荷を高め、長すぎる間隔は変化の検出が遅れます。監視対象の性質に応じて適切な間隔(一般的には15秒〜60秒)を設定します。
- Recording Rulesを活用する: 頻繁に使用する複雑なPromQLクエリや、計算コストの高いクエリは、Recording Ruleとして定義しておくことで、事前に計算結果を別の時系列データとして保存できます。これにより、クエリのパフォーマンスが向上し、ダッシュボードの表示も高速になります。
- アラートルールは明確かつ実行可能に: アラートは、対応が必要な問題を示しているべきです。単なる情報提供や、対応不要なアラートはノイズとなり、重要なアラートを見逃す原因になります。アノテーションには、問題の概要、影響範囲、推奨される対応手順などを含めると良いでしょう。また、重要なラベル(例: アラートの原因となっているインスタンス名)を集約で消してしまわないように注意が必要です。
- 欠損メトリクスの処理: 特定のイベントが発生しないと現れないメトリクス(エラーカウントなど)は、クエリ時に値が存在しない場合があります。アプリケーション起動時に可能なラベル組み合わせで0初期化するか、PromQLの
or
演算子などを使って欠損値を扱います。 - ストレージ計画: PrometheusのローカルTSDBは、データ量や保持期間に応じてディスク容量とメモリを消費します。必要な保持期間と予想されるデータ量に基づいて、適切なストレージ容量を計画します。長期保存が必要な場合は、Remote Writeと外部ストレージの導入を検討します。
- Prometheus自体の監視 (Meta-Monitoring): Prometheusサーバー自身の健全性(CPU/メモリ使用率、ディスクI/O、クエリレイテンシ、スクレイプ成功率など)も監視することが重要です。これにより、監視システム自体の問題を早期に発見できます。
- 高可用性とスケーリング計画: 監視システムの重要度に応じて、HAペア構成、Federation、Remote Storage (Thanos/Cortex) などの導入を計画します。
- セキュリティ: PrometheusやExporterが公開するHTTPエンドポイントへのアクセス制御を適切に行います。必要に応じてTLSによる暗号化や認証を設定します。
まとめ
Prometheusは、その多次元データモデル、強力なPromQL、豊富なExporterエコシステム、動的なサービスディスカバリ機能により、特にクラウドネイティブ環境における監視の標準的なツールとなっています。
メリット:
- クラウドネイティブ環境との高い親和性 (Service Discovery, Pullモデル)
- 柔軟で強力な多次元データモデルとクエリ言語 (PromQL)
- 豊富なExporterとインテグレーション
- 活発なオープンソースコミュニティとエコシステム
- 単一バイナリで動作し、依存関係が少ない
- Alertmanagerによる高度なアラート管理
デメリット / 考慮事項:
- 単一インスタンスでのスケーラビリティには限界がある (数千万アクティブシリーズ程度)
- デフォルトでは長期的なデータ保存には向かない(ローカルTSDBの制限)
- 高カーディナリティラベルの扱いに注意が必要
- ログやトレースなど、メトリクス以外の監視データは扱えない
- 本格的なダッシュボード機能は外部ツール (Grafana) が必要
大規模環境や長期保存、高度な可用性が求められる場合は、ThanosやCortexといったエコシステムツールと組み合わせることで、これらの課題に対応できます。
Prometheusは、現代の複雑化・動的に変化するシステムを効果的に監視するための強力な基盤を提供します。その概念と機能を理解し、ベストプラクティスに従って運用することで、システムの信頼性とパフォーマンス向上に大きく貢献するでしょう。