こんにちは!オブザーバビリティの世界へようこそ。
今日は、現代のクラウドネイティブ環境において不可欠なモニタリングツールであるPrometheusについてお話しします。
Prometheusを使い始めたばかりの皆さんは、「メトリクスがグラフで見えた!楽しい!」と感動している頃かもしれません。しかし、監視対象のサーバーやポッドが100、1000と増えていくと、Prometheus自身が悲鳴を上げ始めます。
- 「PrometheusのPodがOOMKilled(メモリ不足)で頻繁に落ちる…」
- 「CPU使用率が100%に張り付いてダッシュボードが開かない…」
- 「ディスクI/Oが限界を迎えてメトリクスが欠落している…」
こうした課題は、規模が大きくなった現場で100%必ずぶつかる壁です。
今回は、Prometheusの基礎的なセットアップから、高負荷な本番環境にも耐えうる「パフォーマンスチューニングの神髄」まで、ステップバイステップで親身に解説していきますね。
これをマスターすれば、あなたの運用監視基盤は劇的に安定し、毎日の運用作業やトラブルシューティングが格段に楽になりますよ!
—
ステップ1:Prometheusの役割と最小構成での「HelloWorld」
まずは、Prometheusの基本的な役割をおさらいしながら、実際に動かしてみましょう。
Prometheusの役割(Pull型スクレイピング)
多くの監視ツールはAgentがサーバーにデータを送る「Push型」ですが、Prometheusは自分からターゲットのHTTPエンドポイントを見に行ってメトリクスを回収する「Pull型」を採用しています。
では、実際に最小の構成でPrometheusを動かしてみましょう。
1-1. 最小の `prometheus.yml` の作成
まずは設定ファイルを作成します。今回はPrometheus自身が吐き出しているメトリクスを、自分自身で取得(スクレイピング)する「HelloWorld」構成です。
prometheus.yml
global:
scrape_interval: 15s # デフォルトの収集間隔(15秒ごと)
evaluation_interval: 15s # アラート抑止・評価の間隔
scrape_configs:
# ジョブ名の定義(識別子)
- job_name: “prometheus_self_monitoring”
# 実際にメトリクスを取得しに行くターゲット
static_configs:
- targets: [“localhost:9090”]
1-2. Dockerでサクッと起動する
ターミナルで以下のコマンドを実行し、Prometheusを立ち上げます。
docker run -d \
–name prometheus \
-p 9090:9090 \
-v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
ブラウザで `http://localhost:9090` にアクセスしてください。PrometheusのUIが表示されるはずです。
検索窓に `prometheus_http_requests_total` と入力して「Execute」を押してみましょう。数字やグラフが表示されたら、おめでとうございます!これがPrometheusの第一歩(HelloWorld)です。
—
ステップ2:スケール時に激突する「3大ボトルネック」の解剖
Prometheusが順調に動き出しても、監視対象が増えるにつれてリソースを激しく消費し始めます。ここでPrometheus内部で何が起きているのか、3大ボトルネックを解剖しましょう。
【Prometheus内部のリソース消費構造】
[Target (HTTP)] –(Scrape)–> [ Memory (Head Block) ] –(Flush)–> [ Disk (TSDB Blocks) ]
|
(PromQL Eval)
v
[ CPU ]
1. CPUのボトルネック
- 原因: スクレイピングしたテキスト形式データのパース(解析)処理、および複雑なPromQL(クエリ)の実行やルール評価。
- 現象: スクレイピング対象の急増や、重いPromQLクエリを短時間に連打することでCPU使用率が100%に達します。
2. メモリ(RAM)のボトルネック(最大の敵!)
- 原因: Prometheusは直近2時間分のデータを「Head Block」としてメモリ上にすべて保持します。
- 恐怖の現象「高カーディナリティ(High Cardinality)」:
メトリクス名に付与される「ラベルの組み合わせ」が爆発的に増えると、メモリ消費量が跳ね上がります。例えば、ラベルに `user_id` や `ip_address` のような無制限な値を含めると、数G〜数十GBのメモリがあっという間に食い尽くされ、OOM Killed(強制終了)を引き起こします。
3. ディスクI/Oのボトルネック
- 原因: データの損失を防ぐためのWAL(Write-Ahead Log)への書き込みと、2時間ごとにメモリからディスクへデータを固めて書き出す「Compaction(圧縮・結合)」処理。
- 現象: ディスク書き込み速度(IOPS)が追いつかないと、TSDB(時系列データベース)が破損したり、データに抜け漏れが発生したりします。
—
ステップ3:実践チューニング① `scrape_interval` と `scrape_timeout` の黄金律
さて、ここからがプロのチューニングテクニックです!
まずは最も直感的でありながら、強力な効果を発揮する `scrape_interval`(収集間隔)と `scrape_timeout`(タイムアウト)の最適化です。
設定の黄金律
1. 一律15秒にしない: すべてのメトリクスを15秒間隔で取得する必要はありません。Webサービスのレスポンスタイムは15秒間隔が欲しくても、バッチ処理やNodeのディスク残量は1分〜5分(`60s`〜`300s`)で十分です。
2. `scrape_timeout` は `scrape_interval` 以下にする: 絶対に `scrape_timeout > scrape_interval` にしてはいけません。スクレイピングが重なった際にキューが詰まり、CPUとメモリを急激に圧迫します。
チューニング後の設定例
global:
scrape_interval: 1m # デフォルトは1分に引き上げる
scrape_timeout: 10s # タイムアウトは10秒でバッサリ切る
scrape_configs:
# 高頻度で追いたいミッションクリティカルなサービス
- job_name: “payment_api”
scrape_interval: 10s # ここだけ例外的に短く設定
scrape_timeout: 8s
static_configs:
- targets: [“api.internal:8080”]
# 変化の少ないインフラのノード監視
- job_name: “node_exporter”
scrape_interval: 1m # 1分で十分!これだけでDB・CPU負荷が4分の1に
scrape_timeout: 10s
static_configs:
- targets: [“node1.internal:9100”, “node2.internal:9100”]
—
ステップ4:実践チューニング② `relabel_configs` による「不要メトリクスのドロップ」
Prometheusのメモリ使用量を極限まで削減する最強の武器、それが Relabeling(リラベリング) です。
exporter(特に Kubernetes や cAdvisor、Node Exporter)はデフォルトで大量のメトリクスを出力します。しかし、実際のアラートやダッシュボードで使っているのは、そのうちの20%程度だったりします。
不要なメトリクスをPrometheusのメモリに取り込む「前」に捨ててしまうことで、コストを激減させましょう。
実践:`metric_relabel_configs` を使った間引き戦略
設定ファイル内の `metric_relabel_configs` を使用します。
scrape_configs:
- job_name: “kubernetes-cadvisor”
kubernetes_sd_configs:
- role: container
# スクレイピング直後に不要なメトリクスをドロップする設定
metric_relabel_configs:
# 1. 不要な高カーディナリティメトリクスを丸ごと捨てる
- source_labels: [__name__]
# container_tasks_state や container_memory_failures_total をドロップ
regex: “(container_tasks_state|container_memory_failures_total|container_fs_reads_merged_total)”
action: drop
# 2. 必要なメトリクス「だけ」を許可( Keep )するアプローチ(さらに強固)
# – source_labels: [__name__]
# regex: “(node_cpu_seconds_total|node_memory_MemTotal_bytes)”
# action: keep
# 3. メトリクスから不要な「高カーディナリティ・ラベル」だけを消し去る
- regex: “(pod_uid|container_id)”
action: labeldrop
> 先輩からのワンポイントアドバイス💡
> `action: drop` や `action: labeldrop` を使うと、メモリ消費量が半分以下になることも珍しくありません。「使っていないメトリクスは、取り込まない」のが大原則です!
—
ステップ5:実践チューニング③ フェデレーション(Federation)によるスケールアウト
1台のPrometheusでいくらチューニングしても限界が訪れたとき、次に検討するのがフェデレーション(階層化・分散構成)です。
これは「現場のデータ集約用Prometheus(子)」を複数配置し、それらから集約した重要メトリクスだけを「全体統括Prometheus(親)」が引き上げる仕組みです。
[ エッジ/DC A ] ──> [ 子Prometheus A ] ──(集約データのみ)──┐
├──> [ 親Prometheus (大元) ]
[ エッジ/DC B ] ──> [ 子Prometheus B ] ──(集約データのみ)──┘
設定例:親Prometheusが子Prometheusからメトリクスを引く
親Prometheusの `prometheus.yml`:
scrape_configs:
- job_name: ‘federate-datacenter-a’
scrape_interval: 15s
honor_labels: true # 子側で付与されたラベルをそのまま維持する
metrics_path: ‘/federate’
params:
‘match[]’:
# 子から「何を持ってくるか」をPromQLで絞り込む(これが超重要!)
- ‘{job=”kubernetes-service-endpoints”}’ # 重要なサービスメトリクスのみ
- ‘{__name__=~”job:.”}’ # 事前集約(Recording Rules)済みのメトリクス
static_configs:
- targets:
- ‘child-prometheus-a.internal:9090’
このように、子側で `Recording Rules`(事前計算)を行っておき、計算済みの「軽いメトリクス」だけを親が回収するように設計すると、巨大なシステムでもビクともしない超高負荷耐性の監視基盤が完成します!
—
まとめ:システムを守る監視基盤自身が、倒れないために
お疲れ様でした!今回はPrometheusのパフォーマンスチューニングについて、本質的なアプローチを解説しました。
今回学んだ要点を振り返ってみましょう。
1. ボトルネックの正体を知る: 特に「メモリ(高カーディナリティ)」が最大のリスク。
2. 収集間隔の最適化: 一律15秒をやめ、`scrape_interval` と `timeout` を適切に使い分ける。
3. 不要データのドロップ: `metric_relabel_configs` で不要なメトリクスやラベルは取り込む前に捨てる。
4. フェデレーションでスケール: 1台に限界が来たら、役割分担をして階層化する。
監視ツールは、本番環境のトラブル時に「最後の砦」として最も頼りになる存在でなければなりません。その監視基盤自身が負荷で倒れてしまっては本末転倒ですよね。
今日紹介したテクニックを少しずつご自身の環境に適用してみてください。Prometheusの負荷が劇的に下がり、ダッシュボードの表示が爆速になるのを実感できるはずです。
これをマスターすれば、毎日の運用監視作業が劇的に楽になりますよ!応援しています!