約5000万ドキュメントを抱えるElasticsearchクラスターをプロダクションで6ヶ月運用して、ひとつ高い授業料を払って学んだことがある:CPUとRAMの監視だけでは不十分だということだ。Grafanaでクラスターが”グリーン”になっていても、クエリレイテンシーは静かに増加していることがある。さらに悪いのは、JVMヒープが90%まで上昇しても誰も気づかず、ノードがOOM killされて初めて発覚するケースだ。
自分のPrometheus + Grafanaは現在15台のサーバーを監視しており、このセットアップのおかげでユーザーから苦情が来る前に多くの問題を検知できている。しかし、スタックにElasticsearchを追加した際は、試行錯誤に2週間近くかかった。この記事は、最初から読んでおきたかった内容だ。
ElasticsearchにはなぜElasticsearch固有の監視が必要か
CPU、RAM、ディスクI/Oといったシステムメトリクスはあくまでも表層に過ぎない。本当に複雑な問題はクラスターの内部にある:
- Shard allocation:クラスターは動作しているが未割り当てシャードが存在する場合、データが危険な状態にある
- クエリレイテンシー:検索p99が50msから2sに上昇していたら、インデックスの即時最適化が必要なサインだ
- JVM GCプレッシャー:GCポーズが1秒を超えると、その間すべてのリクエストがフリーズする
- バルクキューの拒否:インデックスキューが満杯になると、明確なエラーログなしにリクエストが拒否される
- ディスクウォーターマーク:ディスク空き容量が < 5% になるとElasticsearchは自動的に書き込みをブロックする。早期発見しなければデータ損失につながる
アプリケーションレベルの監視がなければ、障害のデバッグは暗闇の中を手探りするようなものだ。
3つの一般的なアプローチの比較
1. X-Pack Stack Monitoring(ネイティブ)
ElasticsearchはKibana Stack Monitoringを通じた監視機能を組み込みで備えている。メトリクスはクラスター自身または別の監視専用クラスターに保存される。
- セットアップが簡単:
elasticsearch.ymlに数行追加するだけで完了 - Kibanaのインターフェースが洗練されており、クラスターヘルスの概要が最初から確認できる
- ただし:メトリクスの保存にクラスター自身のリソースを消費する。また、Elasticがライセンスを変更した後にフォークされたOpenSearchでは、同等の機能が利用できない
2. Metricbeat
Elasticのエージェントが各ノード上で動作し、メトリクスを収集してElasticsearchまたはLogstashに送信する。
- 豊富なモジュールがあり、設定が簡単
- Elasticスタックとの密結合 — すでにPrometheusを使用している場合は統合できない
- 各ノードにエージェントをデプロイし保守する必要がある — 10ノード以上のクラスターでは運用コストが無視できない
3. Prometheus Exporter(elasticsearch_exporter)
ExporterはPrometheus形式でメトリクスを公開するスタンドアロンサービスとして動作し、Prometheusが定期的にスクレイピングする。
- 既存のPrometheus + Grafanaスタックと完全に統合できる
- 単一のExporterでクラスター全体を監視できる — ノードごとのインストールが不要
- ElasticsearchとOpenSearchの両方をサポート
- Grafana Labsにコミュニティダッシュボードが用意されている
メリット・デメリットの分析 — そしてPrometheus Exporterを選んだ理由
ステージング環境で3つのアプローチを2週間テストした結果は明確だった:
X-Pack Monitoringはチームがライセンス付きのElasticスタックにオールインしている場合に適している。しかし、自分のコードベースではライセンスの自由度が高いOpenSearchを使用しているため、このオプションは早々に除外された。
Metricbeatはすでにログ転送にBeatsを使用している場合には合理的だ。しかし、新しいノードごとにエージェントのデプロイパイプラインを追加するオーバーヘッドは、小規模チームにとって避けたい負担だった。
Prometheus Exporterが選ばれたのはシンプルな理由からだ:Prometheus + Grafanaがすでに15台のサーバーで稼働しており、Exporterを1つ追加するだけで済む。既存のアーキテクチャに手を加える必要がない。OpenSearchに対しても同様に動作する — URIを変更するだけで使える。
ステップバイステップの導入手順
ステップ1:elasticsearch_exporterのインストール
prometheus-community/elasticsearch_exporter プロジェクトはDockerとバイナリの両方をサポートしている。最も手軽な方法:
docker run -d \
--name elasticsearch-exporter \
-p 9114:9114 \
--restart unless-stopped \
quay.io/prometheuscommunity/elasticsearch-exporter:latest \
--es.uri=http://elasticsearch:9200 \
--es.all \
--es.indices \
--es.shards
クラスターに認証がある場合(OpenSearch SecurityプラグインまたはElasticsearch xACL):
docker run -d \
--name elasticsearch-exporter \
-p 9114:9114 \
--restart unless-stopped \
-e ES_USERNAME=monitoring_user \
-e ES_PASSWORD=your_password \
quay.io/prometheuscommunity/elasticsearch-exporter:latest \
--es.uri=https://elasticsearch:9200 \
--es.all \
--es.indices \
--es.clusterinfo.interval=5m
Exporterが起動しメトリクスを公開していることを確認:
curl http://localhost:9114/metrics | grep elasticsearch_cluster_health
elasticsearch_cluster_health_status{color="green"} 1 のような出力が表示されれば、ExporterがクラスターへのOK接続に成功している。
ステップ2:Prometheusスクレイプジョブの設定
prometheus.yml にジョブを追加:
scrape_configs:
- job_name: 'elasticsearch'
scrape_interval: 30s
scrape_timeout: 25s
static_configs:
- targets:
- 'elasticsearch-exporter:9114'
labels:
cluster: 'prod-es-cluster'
env: 'production'
再起動せずにPrometheusをリロード:
curl -X POST http://localhost:9090/-/reload
Prometheus UIにアクセスし、elasticsearch_cluster_health_status をクエリしてデータが正常に収集されていることを確認する。
ステップ3:GrafanaダッシュボードのインポートOK
Grafana LabsのダッシュボードID 14191 は、elasticsearch_exporterに対してコミュニティで最もよく使われているものだ:
- Grafana → Dashboards → Import
- ID
14191を入力 → Load - Prometheusデータソースを選択 → Import
ダッシュボードには最初から含まれている:クラスターヘルスステータス、ノード数、JVMヒープ使用率、インデックスレート、検索レート、GCコレクション時間、ノードごとのディスク使用量。
ステップ4:重要メトリクスのアラート設定
以下は自分がプロダクションで直接使用しているアラートルールセットだ。Prometheusルールファイルに追加する:
groups:
- name: elasticsearch_alerts
rules:
# クラスターステータスが green ではない
- alert: ElasticsearchClusterRed
expr: elasticsearch_cluster_health_status{color="red"} == 1
for: 1m
labels:
severity: critical
- alert: ElasticsearchClusterYellow
expr: elasticsearch_cluster_health_status{color="yellow"} == 1
for: 5m
labels:
severity: warning
# 未割り当てシャード
- alert: ElasticsearchUnassignedShards
expr: elasticsearch_cluster_health_unassigned_shards > 0
for: 5m
labels:
severity: warning
# JVM Heap > 85%
- alert: ElasticsearchHighJVMHeap
expr: |
elasticsearch_jvm_memory_used_bytes{area="heap"} /
elasticsearch_jvm_memory_max_bytes{area="heap"} > 0.85
for: 3m
labels:
severity: critical
# ディスク残量が少ない(空き容量 < 20%)
- alert: ElasticsearchLowDiskSpace
expr: |
elasticsearch_filesystem_data_available_bytes /
elasticsearch_filesystem_data_size_bytes < 0.20
for: 5m
labels:
severity: warning
ステップ5:最小権限の監視用ユーザー作成
ExporterにOK管理者アカウントを使用しない — 専用ユーザーを作成する:
# Elasticsearch - APIでロールとユーザーを作成
curl -X PUT "http://localhost:9200/_security/role/monitoring_role" \
-H 'Content-Type: application/json' \
-u elastic:password \
-d '{
"cluster": ["monitor"],
"indices": [{"names": ["*"], "privileges": ["monitor", "view_index_metadata"]}]
}'
curl -X PUT "http://localhost:9200/_security/user/monitoring_user" \
-H 'Content-Type: application/json' \
-u elastic:password \
-d '{"password": "strong_password", "roles": ["monitoring_role"]}'
OpenSearchの場合は、OpenSearch Security REST APIまたはダッシュボードUIで同様の手順で行う。
6ヶ月のプロダクション運用から学んだこと
scrape_interval 30s はほとんどのユースケースで十分だ。デバッグのために粒度を上げたい場合は15sに下げることもできるが、ExporterがES APIを呼び出す頻度が増え、クラスターのリソースをより多く消費する。
--es.shards フラグは大規模クラスターでタイムアウトを引き起こす可能性がある。800シャードを持つクラスターでこのフラグを初めて有効にした時、Exporterが頻繁にタイムアウトした。解決策:--es.timeout=30s を追加するか、シャードレベルの詳細監視が不要であればフラグを無効にする。
ダッシュボード14191はセキュリティ監査ログや異常検知ジョブのステータスなど、OpenSearch固有のメトリクスをカバーしていない。OpenSearchの拡張機能を使用している場合は、生のメトリクスから独自にパネルを構築する必要がある。
この方法の本当の価値を実感したのは、午前3時にAlertmanagerからTelegramにメッセージが届いた時だ:ノード2のJVMヒープが88%に達していた。日本のユーザーが朝のシフトに入るまでのちょうど30分間で、チームはノードを再起動してヒープ設定を更新することができた。あのアラートがなければ、確実にダウンタイムが発生し、朝6時の不愉快な電話を受けることになっていたはずだ。
この方法で最も気に入っているのは、新しいツールを学ぶ必要がない点だ。チームがすでにPrometheusとGrafanaを使いこなしていれば、1〜2時間でElasticsearchの完全な監視体制を構築できる — シャード割り当て、JVMヒープ、ディスクウォーターマーク、そしてアラートまで。
