Skip to main content

Prometheusは動いている。なのにメトリクスが無い。ServiceMonitorが『静かに』失敗する3つの理由

Prometheusは起動している。ダッシュボードも開ける。でもグラフが空白のままだ

Helmでkube-prometheus-stackを入れ、Grafanaのログインにも成功した。だがアプリケーションのカスタムメトリクスを表示しようとすると、パネルは空白のままになる。エラーログもない。アラートも鳴らない。ただ、データが存在しないだけだ。

これは Kubernetes の監視構築でよく起きる「サイレント障害」だ。kubernetes service monitor の仕組みは、Podのアノテーションベースの発見よりも柔軟な設定を提供する反面、Prometheus Operator の公式ドキュメントが示す通り、複数のレイヤーで条件が一致して初めて動く設計になっている。どこか1箇所でも噛み合わなければ、メトリクスは静かに消える。

監視というシステムの本質的な皮肉がここにある。アプリケーションの障害は監視が検知してくれる。しかし、監視自体が壊れていることは、監視では検知できない。この記事では、ServiceMonitorがメトリクスを取りこぼす3つの典型的な原因と、その切り分け手順を順番に見ていく。ちなみに、こうした監視スタックの初期設定自体を引き受けてくれる選択肢としてKuboのようなマネージドK3s環境もあるが、まずは仕組みそのものを理解しておこう。

監視ダッシュボードにメトリクスが表示されないブラックボックス状態を示す図

原因1: Service・ServiceMonitor・Prometheus CRの「三重のラベル一致」が崩れている

ServiceMonitorのselectorフィールドは、公式ドキュメントによれば「ラベルセレクターを用いてメトリクスをスクレイプするKubernetes Podオブジェクトを選択する」ためのものだ。つまりServiceMonitor自体にラベルを書けばよいわけではなく、監視対象のServiceが持つラベルと完全に一致していなければならない

さらに、kube-prometheus-stackのHelmチャートを使っている場合、話はもう一段階複雑になる。デフォルトでは、Prometheus本体は「自分と同じnamespace内にあり、かつprometheus-operatorのreleaseラベルと同じラベルタグが付与されたServiceMonitor」だけを検出する仕様になっている。つまり:

  1. ServiceMonitorのselector.matchLabels ↔ Serviceのlabels
  2. Prometheus CRのserviceMonitorSelector ↔ ServiceMonitorのlabels(release ラベル)

この2つのラベル一致が両方成立しなければ、ServiceMonitorは存在してもPrometheusから「見えない」ことになる。Helmチャート側ではserviceMonitorSelectorNilUsesHelmValuesfalseに設定することで、release固有のラベルによる絞り込みを無効化し、namespace内の全ServiceMonitorを対象にすることも可能だが、これは意図せず無関係なServiceMonitorまで拾ってしまうリスクとのトレードオフになる。

チェックすべきポイント

  • ServiceMonitorのspec.selector.matchLabelsとServiceのmetadata.labelsが完全一致しているか
  • kube-prometheus-stack環境では、ServiceMonitor自体に正しいrelease: {リリース名}ラベルが付いているか
  • Prometheus CRのspec.serviceMonitorSelectorがどのラベルを要求しているか、kubectl get prometheus -o yamlで確認したか

Service・ServiceMonitor・Prometheus CRの3層のラベル一致関係を示す図

原因2: namespaceSelectorを設定しないと「自分のnamespaceしか見えない」

アプリケーションはproductionネームスペース、監視スタックはmonitoringネームスペースに分離しているのはよくある構成だ。しかしこの構成でnamespaceSelectorを設定し忘れると、メトリクスは永遠に収集されない。

公式ドキュメントの仕様は明確だ。ServiceMonitorのAPIリファレンスによれば、namespaceSelectorが空のラベルセレクターなら全namespaceのPodを検出するが、nullラベルセレクター(未設定)の場合はServiceMonitorオブジェクトと同じnamespaceのPodのみが検出対象になる。これがデフォルト挙動であり、明示的な設定なしにクロスnamespace監視は成立しない。

この仕様を知らないと、「ServiceMonitorをmonitoring namespaceに作ったのに、production namespaceのServiceが拾われない」という現象にそのままハマる。原因1のラベル一致がすべて正しくても、このnamespaceの壁を越えていなければ意味がない。

対処法

yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app-monitor
  namespace: monitoring
  labels:
    release: kube-prometheus-stack
spec:
  namespaceSelector:
    matchNames:
      - production
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics
      interval: 30s

matchNamesで対象namespaceを明示するか、複数namespaceを横断する場合はany: trueで全namespace対象に切り替える。

namespaceSelector未設定時と設定後のnamespace間可視範囲の違いを示す図

原因3: ラベルは正しいのに、RBAC権限がなくて「見えているのに見えない」

ここまでの2つはYAMLの記述ミスで気づきやすいが、3つ目は最も見落とされやすい。ラベルもnamespaceSelectorも完璧なのに、それでもメトリクスが取れないケースだ。

原因はPrometheusのServiceAccountに紐づくRBAC権限の不足にある。KubernetesのServiceAccount公式ドキュメントが明記する通り、自動作成されるdefault ServiceAccountは「デフォルトAPI Discovery権限」以外にはほぼ権限を持たない。Prometheusが対象namespaceのService・Endpoints・Podをlist/watchできる権限を明示的に付与しなければ、スクレイピング対象として認識されることすらない。

KubernetesのRBAC公式ドキュメントによれば、権限はRoleで定義したverbsget, list, watchなど)を、RoleBindingを通じてServiceAccountに紐づける形で付与する。RoleBindingはnamespaceスコープ、ClusterRoleBindingはクラスタ全体スコープという違いも重要だ。マルチnamespace監視をする場合、必要な数だけRoleBindingを増やすか、ClusterRoleとClusterRoleBindingで一括付与するかの設計判断が発生する。

この問題が厄介なのは、エラーとして表面化しないことだ。権限不足でリソースが一覧できなくても、Prometheus自体はクラッシュしない。ただターゲットリストに現れないだけで、ログを注意深く見ない限り気づかない。

確認コマンド

bash
kubectl auth can-i list services --as=system:serviceaccount:monitoring:prometheus-kube-prometheus-prometheus -n production
kubectl auth can-i list endpoints --as=system:serviceaccount:monitoring:prometheus-kube-prometheus-prometheus -n production

noが返ってきた場合は、対象namespaceに向けたRole/RoleBindingの追加が必要になる。Kubernetes公式のRBACベストプラクティスにもある通り、最小権限の原則を守りながら、監視に必要な範囲だけを明示的に許可するのが正しい設計だ。

ServiceAccountからRBAC権限チェックまでの診断フローを示す図

実際に切り分ける手順 — TargetsページからkubectlコマンドまでのC4ステップ

原因が3つあると分かっても、実際の障害でどこから手をつけるべきかは別の問題だ。以下の順序で切り分けると迷いがない。

  1. Prometheus UIのTargetsページを開く/targets)。対象のジョブが一覧に出ているかを確認する。出ていなければ原因1か2、出ているのにDOWN状態ならエンドポイント自体の問題を疑う
  2. ServiceMonitorのselectorを確認する。kubectl get servicemonitor <name> -o yamlspec.selectorspec.namespaceSelectorを出力し、対象のServiceのラベルと突き合わせる
  3. Prometheus CRのselectorを確認する。kubectl get prometheus -n monitoring -o yamlspec.serviceMonitorSelectorを確認し、ServiceMonitor自体のラベルと一致しているか見る
  4. RBAC権限をkubectl auth can-iで確認する。ここまで一致していてもTargetsに出ない場合は、ほぼ確実にRBACが原因になる

このステップを踏めば、原因1・2・3のどこで詰まっているかが機械的に切り分けられる。逆に言えば、この4ステップを最初から手順化しておかない限り、毎回同じ原因を場当たり的に探し直すことになる。

Prometheus はCNCFの2番目の卒業プロジェクトとして、Kubernetesエコシステムのデファクトスタンダードになっているからこそ、この設定の複雑さは多くのチームが一度は通る道でもある。CNCFのKubernetes監視解説でも、複数のOSSツールセットを組み合わせる際の設定整合性の重要性が指摘されている。

序盤の切り分けを毎回自前でやるのが負担なら、KuboはPrometheus + Grafanaを標準搭載しており、ServiceMonitorのラベル整合やRBAC設定を含めて事前に動作確認済みの構成で提供される。K3sベースの軽量な構成の上に、監視スタックの初期設定をゼロから組む工程そのものを引き受ける形だ。

Targets確認からRBAC確認までの4ステップ診断プロセスを示す図

モニタリングとオブザーバビリティの違いを踏まえた設計を

そもそも監視とオブザーバビリティの違いを踏まえると、監視は「明確に定義された障害」の検出に特化したものであり、オブザーバビリティはログ・メトリクス・トレースを含むより広い可観測性の実現を指す。ServiceMonitorの設定ミスは、この監視という土台そのものが機能していない状態であり、その上に積むはずのオブザーバビリティ全体が崩れることを意味する。

AWSのオペレーショナルエクセレンスに関するドキュメントでも、オブザーバビリティの実装は運用の柱の一つとして位置づけられている。監視基盤の設定不備は、単なる技術的なミスではなく、運用全体の信頼性に関わる問題だ。

まとめ — 3つの原因を順に潰せば、監視の「静かな失敗」は防げる

Prometheus + ServiceMonitorでメトリクスが取れない場合、原因はほぼ以下の3点に集約される。

  • ラベルセレクタの不一致: Service ↔ ServiceMonitor ↔ Prometheus CRの三重整合が崩れていないか
  • namespaceSelectorの設定漏れ: デフォルトでは自namespaceしか見えない仕様を理解しているか
  • RBAC権限の不足: ServiceAccountがlist/watchできる権限を明示的に持っているか

これらはいずれも公式ドキュメントに仕様として明記されているが、複数のCRDとRBACをまたぐ設定であるがゆえに、実際の障害では見落とされやすい。毎回この3点を手動でチェックする運用から抜け出したいなら、KuboのようにPrometheus + Grafanaが標準搭載され、初期設定済みの状態で始められるマネージドK3s環境を検討する価値がある。EKSやAKSでゼロから監視スタックを組む場合と比べても、コスト効率よく本格的なKubernetes運用を実現できる。

監視基盤の構築・運用でこうした落とし穴に頭を悩ませているなら、お問い合わせから相談してみてほしい。

Related articles

← Back to all posts