目次
19 章

可観測性

運用クラスタの視野を作る3つの軸 — メトリクス (Prometheus + kube-state-metrics + node-exporter)、ログ (Loki)、トレース (OpenTelemetry + Tempo) — と可視化 (Grafana)、アラート (Alertmanager) の標準スタックを整理します。kube-prometheus-stack の ServiceMonitor・PrometheusRule、PromQL・LogQL の一行の例、そしてカーディナリティ・保存・アラート SNR・golden signals の運用ガードレールまでを一連の流れで扱います。

ここまで扱った 第15章 CNI 深掘り第16章 RBAC / ServiceAccount 深掘り第17章 Admission Controller第18章 CRD と Operator パターン はどれもクラスタの動作を作るコンポーネントでした。このすべてのコンポーネントがうまく回っているかを覗き込む一層がもう1つ必要です — 可観測性 です。どの Pod がメモリをどれだけ使うか、どの Service の latency が普段より高いか、1つのリクエストがマイクロサービスの間をどう流れたか。この3つの次元がメトリクス・ログ・トレースで、その上に K8s 運用の標準スタックが据えられています。

本章の終わりには 第11章 resources.requests / limits第12章 Health check第13章 オートスケーリング のシグナルが集まる一式のツールスタック が手に入ります。マニフェスト1枚でクラスタの視野を一段深くする出発点です。

可観測性の3つの軸 #

可観測性はよく 3つのデータ種類 に分けて語られます。

何か質問
Metrics時間に沿った数値の時系列「今何が起きているか」
Logsイベントのテキスト記録「そのイベントの詳しい事情は何か」
Traces1つのリクエストが複数のサービスを経る経路「なぜこのリクエストが遅かったか」

この3つは互いに補完関係です。メトリクスで異常を発見し、ログで詳しい状況を押さえ、トレースでリクエスト経路のどの区間が問題かを狭める流れが日常のデバッグパターンです。運用クラスタで3つすべてを揃えるのが標準で、各軸の K8s ツールはほぼ固まっています。第27章 kubectl デバッグパターン の診断ツリーもこの3つの軸を一緒に見る流れの上に立っています。

Metrics — Prometheus 中心の標準スタック #

K8s メトリクスの事実上の標準は Prometheus です。CNCF 卒業プロジェクトであり、K8s 自体のコンポーネント (API サーバー、kubelet、controller-manager、scheduler) がすべて Prometheus が理解できる形式でメトリクスを公開します。Prometheus のモデルは単純です。

  • Pull ベース — Prometheus が定期的に各ターゲットの /metrics エンドポイントを HTTP で取得します。
  • 時系列データベース — 取得したデータはラベルが付いた時系列として保存されます。
  • PromQL — 時系列を問い合わせる独自のクエリ言語です。

標準スタックのコンポーネント #

運用クラスタに Prometheus をインストールするとほぼ常に次のコンポーネントが一緒に入ります。

コンポーネント役割
Prometheus Serverメトリクス収集 + 保存 + 問い合わせ
kube-state-metricsK8s オブジェクト (Deployment、Pod、Node など) の状態をメトリクスとして公開
node-exporter各ノードのシステムメトリクス (CPU、メモリ、ディスク) を公開。第8章 DaemonSet でノードごとに1個
Alertmanagerアラートのルーティング、束ね、ミュート処理
Pushgateway (任意)短く生きる Job のメトリクスを push で受け取る

この束を一度にインストールしてくれる標準マニフェストが kube-prometheus-stack Helm チャートです。運用クラスタの導入の第一段階としてほぼ標準になっています。EKS 実戦セットアップでは 第25章 モニタリング・アラート でもう一度扱います。

ServiceMonitor / PodMonitor — Prometheus Operator の役割 #

Prometheus の scrape 対象を直接マニフェストで書く代わりに、Prometheus Operator が導入した CRD が2つあります。第18章 CRD と Operator パターン のモデルが可観測性スタックにもそのまま適用される例です。

servicemonitor-app.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: my-app
  namespace: my-app
  labels:
    release: prometheus  # kube-prometheus-stackのselectorと一致
spec:
  selector:
    matchLabels:
      app: my-app
  endpoints:
    - port: metrics
      interval: 30s
      path: /metrics

この ServiceMonitor を適用すると Prometheus Operator が自動的に Prometheus 設定を更新して、その Service の Pod から30秒ごとにメトリクスを取得します。マニフェストで scrape 対象を宣言する K8s-native 方式です。

PodMonitor は Service なしで Pod に直接付く変形です。この2つの CRD のおかげでアプリケーションチームは Service の隣に ServiceMonitor 1枚を一緒に書くだけでメトリクス収集が自動的に始まります。

一行の PromQL の例 #

PromQL はそれ自体が深いテーマですが、運用で最もよく使うパターンをいくつか押さえておきます。

ネームスペースのPodメモリ使用率の合計
sum(container_memory_usage_bytes{namespace="payments"}) by (pod)
過去5分間の5xx応答比率
sum(rate(http_requests_total{status=~"5.."}[5m]))
  / sum(rate(http_requests_total[5m]))
P95 latency (ヒストグラム)
histogram_quantile(0.95,
  sum(rate(http_request_duration_seconds_bucket[5m])) by (le, service)
)

rate() は時系列の毎秒の増加量を、histogram_quantile() はヒストグラムから特定の分位数を計算します。この2つの関数が PromQL 活用の70%程度を覆います。第11章 §「メモリ使用量 vs メモリ limits」で押さえた container_memory_working_set_bytes 時系列の分析がこの PromQL の上で本格的に可能になります。

Logs — Loki 中心の新しいスタック #

ログ収集の旧標準は EFK スタック (Elasticsearch + Fluentd + Kibana) でした。強力ですが重いです — Elasticsearch はすべてのログ本文をフルテキストインデックスするのでディスクとメモリが多くかかります。

Loki は Grafana Labs が作った軽量な代替です。モデルが違います — ログ本文はインデックスせず、ラベルだけをインデックスします。 検索時にはラベルで狭めた後に本文をグループ検査します。Prometheus に似たラベルモデルをログに持ち込んだわけです。

Loki スタックのコンポーネント #

コンポーネント役割
Lokiログ保存 + 問い合わせ
Promtail (または Fluent Bit)各ノードでログを読んで Loki へ送る。DaemonSet
GrafanaLogQL クエリ + 可視化

Promtail はノードの /var/log/containers/ を読んで K8s メタデータ (Pod 名、ネームスペース、コンテナ名、ラベル) を自動的にラベルとして付けて Loki へ転送します。別途のアプリケーション変更なしにすべてのコンテナの stdout / stderr がそのまま収集されます。第3章 kubectl と最初の Podkubectl logs がノードごとに散らばったログを1つの Pod 単位で見せるツールだとすれば、Loki + Promtail はその流れをクラスタ全体へ拡張した形です。

LogQL — Loki のクエリ言語 #

PromQL に似た肌合いです。

payments ネームスペースの ERROR ログ
{namespace="payments"} |= "ERROR"
特定 Pod のエラー比率 (メトリクスへ変換)
sum(rate({pod="checkout-abc123"} |= "ERROR" [5m]))

{...} でラベルフィルタ、|= で本文の substring マッチング、|~ で正規表現マッチング。rate() でメトリクスのように扱えるので Grafana ダッシュボードでメトリクスチャートと一緒に描けます。

Loki vs EFK — 選択の肌合い #

次元LokiEFK
インデックスラベルのみ全本文
ディスクコスト低い高い
フルテキスト検索グループ検査 (遅い)速い
運用負担低い高い (Elasticsearch クラスタの運用)
Grafana 統合1級可能

新規導入は Loki が標準に近いです。フルテキスト検索が核心要求なら EFK または OpenSearch がより適切ですが、K8s 運用の日常的なデバッグには Loki のラベル + グループモデルで十分です。

Traces — OpenTelemetry 中心の統合 #

分散トレーシングの旧標準は2つの系統に分かれていました — OpenTracingOpenCensus。2つのプロジェクトが合わさって作られたのが OpenTelemetry (OTel) です。今は分散トレーシング・メトリクス・ログを一緒に扱う単一標準で、CNCF でも最も活発なプロジェクトの1つです。

OpenTelemetry の核心概念は3つです。

  • Instrumentation ライブラリ — 各言語別の SDK がアプリケーションコードに挿入されて trace を作ります。自動 instrumentation ツール (Java agent など) がコード変更なしに付く場合も多いです。
  • OpenTelemetry Collector — アプリケーションが送るデータを受け取って処理・ルーティング。一般的に K8s に DaemonSet または Deployment として起動します。
  • バックエンド — 実際の trace を保存して可視化。Jaeger、Tempo、Datadog、Honeycomb など。

Trace のモデル — Span のツリー #

分散トレーシングの単位データは Span です。1つのリクエストが複数のサービスを経る間、各段階ごとに span が1個作られ、親-子関係でつながってツリーをなします。

一つのリクエストのspanツリーの例
[gateway] /api/orders POST  (200ms)
 ├─ [orders-service] create order   (180ms)
 │   ├─ [postgres] INSERT orders    (15ms)
 │   ├─ [postgres] INSERT items     (12ms)
 │   └─ [kafka] publish order.created (45ms)
 └─ [auth-service] verify token     (10ms)

このツリーを見ると200msのうちどの区間で時間が最もかかったかが一目で分かります。P99 latency が普段より高いときどのサービスが原因かを狭めるのに trace が決定的です。

Tempo — Loki と同じ方向の trace 保存所 #

Grafana Labs は trace 保存所も軽量なモデルで作りました — Tempo です。Loki がログにしたことを trace にしたわけです。インデックスを最小化してオブジェクトストレージ (S3 / GCS) に trace 本文を保存します。trace ID で直接照会するのに最適化されており、Loki・Prometheus と一緒に使うとき Grafana でメトリクス → ログ → trace へ自然に流れる流れが作られます。

Grafana — 可視化の標準 #

3つの軸のデータを1ヶ所で覗き込むツールが Grafana です。Prometheus、Loki、Tempo、Elasticsearch、CloudWatch などほぼすべてのデータソースを1つのダッシュボードに束ねられ、各パネルが自分のクエリ言語でデータを取ってきます。

運用クラスタの標準ダッシュボードセットは普通次の程度で構成されます。

  • クラスタ概要 — ノードごとの CPU・メモリ・ディスク、Pod 数、ネームスペースごとのリソース使用
  • ワークロード概要 — Deployment / StatefulSet ごとの replica 状態、再起動回数、OOMKilled
  • API サーバーのヘルス — request rate、error rate、P99 latency、etcd lag
  • 各アプリケーション — ビジネスメトリクス + 4 golden signals (latency、traffic、errors、saturation)

kube-prometheus-stack を導入するとクラスタ・ワークロード・API サーバーのダッシュボードは事前構成された状態で一緒に入ります。アプリケーションダッシュボードだけドメインに合わせて新しく作ればよいです。

Alerting — Alertmanager の役割 #

メトリクスがある条件を満たしたときアラートを送る仕事は Prometheus 自体ではなく Alertmanager が担当します。Prometheus がアラートルールを評価して発生したアラートを Alertmanager へ送り、Alertmanager がルーティング・束ね・ミュート・繰り返し処理をします。

PrometheusRule — アラート定義の CRD #

prometheusrule-high-error-rate.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: app-alerts
  namespace: my-app
  labels:
    release: prometheus
spec:
  groups:
    - name: my-app
      rules:
        - alert: HighErrorRate
          expr: |
            sum(rate(http_requests_total{status=~"5..", app="my-app"}[5m]))
              / sum(rate(http_requests_total{app="my-app"}[5m])) > 0.05
          for: 5m
          labels:
            severity: warning
            team: payments
          annotations:
            summary: "High 5xx rate on my-app ({{ $value | humanizePercentage }})"
            description: "5xx rate over the last 5 minutes is above 5%."

expr が Prometheus で評価される条件、for: 5m が「この条件が5分連続で真でなければアラート発生しない」という意味です。labelsseverityteam が Alertmanager でルーティングキーとして使われます。

Alertmanager のルーティング #

Alertmanager の設定ではラベルベースでアラートをどこへ送るかを定めます。

alertmanager.yaml — 単純化
route:
  receiver: default
  group_by: ['alertname', 'team']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - match:
        severity: critical
      receiver: pagerduty
    - match:
        team: payments
      receiver: payments-slack

receivers:
  - name: default
    slack_configs:
      - channel: '#alerts'
  - name: pagerduty
    pagerduty_configs:
      - service_key: ...
  - name: payments-slack
    slack_configs:
      - channel: '#payments-alerts'

このモデルのおかげでアラートのルーティングがコード (マニフェスト) で管理され、Slack / PagerDuty / Email のようなチャネルへ分岐します。

運用時に固めておく原則 #

可観測性スタックは一度よく揃えておけばクラスタの視野が一気に深くなりますが、誤って運用するとクラスタリソースの大部分を食いつぶすツールになります。次の4つを固めておくのがよいです。

1. メトリクスのカーディナリティ爆発に注意 #

Prometheus はラベルの組み合わせ1つにつき別の時系列を作ります。ラベルに user ID、request ID、UUID のような高カーディナリティの値を入れると時系列数が爆発し、Prometheus がメモリを速く使い切ります。運用ガイドの第一原則は ラベルに高カーディナリティの値を入れるな です。

カーディナリティ点検 — 時系列が最も多いメトリクス
topk(10, count by (__name__)({__name__=~".+"}))

このクエリで時系列数の多いメトリクスを定期的に点検するのが運用の一部です。

2. 保存期間とリモートストレージ #

Prometheus のローカルストレージはデフォルト15日保存です。それ以上保管するには リモートストレージ (Thanos、Cortex、Mimir、VictoriaMetrics) へ一緒に送らなければなりません。同様に Loki と Tempo もオブジェクトストレージ (S3 / GCS) に long-term storage を置くのが標準です。

保存期間はコストと直結します。メトリクス6ヶ月、ログ30日、trace 7日程度が一般的な出発点で、各軸の保存を別々に定められます。コスト観点でのガードレールは 第28章 コスト最適化 でもう一度扱います。

3. アラートの SNR — 多すぎるアラートは無アラートと同じ #

アラートを多く作りすぎると運用者がアラートを無視し始め、肝心の重要なアラートも見逃すようになります。アラート設計の標準原則は 「アラート1件 = 人の即時対応1回」 です。

  • Symptom-based — 原因ではなく症状にアラート。「DB 接続プールが80%埋まっている」ではなく「API の5xx 比率が5%を超える」。
  • for 期間でノイズを除去 — 短いスパイクにアラートが鳴らないように。
  • severity の系統critical は起こすべきもの、warning は明日の朝に見ればよいもの。系統が曖昧になると無視され始めます。

4. golden signals を標準に #

Google SRE 文化から出発した4 golden signals はほぼすべてのワークロード監視の出発点です。

シグナル意味
Latencyリクエスト処理時間 (P50 / P95 / P99)
Traffic毎秒のリクエスト数
Errors失敗比率
Saturationリソース飽和度 (CPU、メモリ、キュー長)

この4つをすべてのサービスに同じ形態で公開しておけばダッシュボードもアラートも標準化されます。ドメインメトリクスはその上に載せます。

HPA・probe・デバッグとの接点 #

本章の可観測性スタックは本書全般のシグナルが集まる1ヶ所になります。短く束ねておきます。

  • 第11章 resources.requests / limitscontainer_memory_working_set_bytescontainer_cpu_cfs_throttled_seconds_total 時系列が Prometheus へ入ってきます。OOMKilled と CPU throttling の事後分析は本章の PromQL の上で行います。
  • 第12章 Health check — probe 失敗イベントがメトリクス (kube_pod_container_status_ready) とログの両方へ公開されます。アラートルールの最初の一行が普通この readiness メトリクスです。
  • 第13章 オートスケーリング — HPA の入力メトリクスが metrics-server のほかに Prometheus Adapter を経る流れが本章の ServiceMonitor と直接つながります。KEDA も同じ位置です。
  • 第17章 Admission Controller — webhook latency P99 と拒否率を Alertmanager に連動させるのが本章の標準です。
  • 第27章 kubectl デバッグパターンdescribelogs で始まる診断ツリーが可観測性スタックのメトリクス・ログ時系列へ拡張される流れが自然です。

練習問題 #

  1. 自分のクラスタに kube-prometheus-stack を Helm でインストールした後、kubectl get servicemonitor -Akubectl get prometheusrule -A でどんな ServiceMonitor と PrometheusRule が事前構成されているかを確認します。そのうち1つの PrometheusRule の expr を直接読んでみて、そのアラートがどんな症状を見るかを §「アラートの SNR」の Symptom-based 原則と突き合わせて一段落で整理します。
  2. 自分のクラスタのどれか1つのワークロードに histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) を直接クエリして P95 latency を確認します。第11章 の CPU throttling 時系列 (container_cpu_cfs_throttled_seconds_total) と同じ時間帯で見て、throttling が応答遅延を大きくする様子が PromQL の上でどう見えるかを一段落で整理します。
  3. PrometheusRule 1枚を直接書いてみましょう — 「Pod の OOMKilled が5分以内に3回以上ならアラート」のようなルールです。expr にどの時系列を使うか (kube_pod_container_status_last_terminated_reason="OOMKilled") を選び、forseverity を §「運用時に固めておく原則」のガードレールに合わせて設定します。Alertmanager のルーティングでこのアラートをどのチャネルへ送るかも一段落で決定します。

一行まとめ: K8s 可観測性の標準の束はメトリクスの Prometheus + kube-state-metrics + node-exporter、ログの Loki、トレースの OpenTelemetry + Tempo、可視化の Grafana、アラートの Alertmanager だ。kube-prometheus-stack Helm チャートが導入の第一段階で、ServiceMonitor・PodMonitor・PrometheusRule の CRD が 第18章 の Operator モデルの上でマニフェスト次元の可観測性を作る。運用の4ガードレールはカーディナリティ爆発の回避・保存期間とリモートストレージ・アラート SNR・golden signals だ。

次の章 #

本章までクラスタの視野を作るツールを整理しました。次の章は そのクラスタがどう変更されるか の運用モデルを扱います。ここまでの流れは人が kubectl apply でマニフェストを適用するモデルでした。しかし複数の人・複数の環境・複数のクラスタが一緒に回る運用では、マニフェストの source of truth が人の手ではなく git になければなりません。

第20章 GitOps では ArgoCD と Flux のモデルを扱います。git のマニフェストがクラスタに自動的に同期される流れ、drift detection、sync policy、multi-cluster 運用パターン、第18章 の status subresource が ArgoCD の drift 検出とどう噛み合うかまでを一連の流れで整理して3部を締めくくります。

X