目次
25 章

モニタリング · アラート

第24章まで整った myshop-api はコードから配備まで自動化されましたが、その動作が見えなければ運用は回りません。本章は EKS クラスタの可観測性スタックを載せます。kube-prometheus-stack で Prometheus · Grafana · Alertmanager を一括導入し、ServiceMonitor / PrometheusRule で myshop-api メトリックと 4 golden signals アラートを標準化し、Loki でログを、CloudWatch Container Insights で AWS 連携メトリクスと長期保管を押さえ、severity · team ルーティングで Slack / PagerDuty の on-call 体制までを整理します。

第24章 CI / CD パイプライン まで経て、myshop-api は新バージョンが入ってくる流れまで自動化されましたが、運用段階の半分は その動作を観測する作業 です。CPU · メモリ · リクエスト latency · エラー率がどこでどう変わるかが見えなければ、カナリアの自動 promote も不可能で、事故対応も遅れます。本章は EKS の上に可観測性スタックを載せる章です。

第19章 可観測性 で扱った標準スタック (Prometheus + Grafana + Loki + Alertmanager) のモデルが、本章で本格的な EKS · AWS 連携運用セットアップへと進む段階です。第19章がメトリック · ログ · トレースの三つの質感をオブジェクトの次元で押さえたとすれば、本章はその上に アラートルールセット · ルーティング · on-call 手順 の運用の質感を加えます。本章の目標は、myshop-api の 4 golden signals アラートが定着して、critical は PagerDuty で呼び出され、warning は Slack で通知される状態です。

二つの軸の組み合わせ — in-cluster Prometheus + マネージド CloudWatch #

EKS 環境の可観測性は通常、二つの軸の組み合わせで成り立ちます。

責任
In-cluster (Prometheus + Grafana + Loki)ワークロードメトリック、ビジネスメトリック、アラート、ダッシュボード
CloudWatch (Container Insights + Logs)AWS マネージドメトリック、ログの長期保管、AWS コンソール統合

二つのうち一つだけを使う方式も可能ですが、運用クラスタの標準は二つの組み合わせです。Prometheus が運用メトリックとアラートの基準ソースで、CloudWatch が長期保管と AWS 自体のリソース (RDS · ALB · EBS) メトリックの統合地点です。第21章 EKS セットアップ で RDS の enabled_cloudwatch_logs_exports で PostgreSQL ログを CloudWatch へ送った決定が、本章の二つ目の軸で自然に合流します。

AWS のマネージド Prometheus (AMP) とマネージド Grafana (AMG) が in-cluster 運用負担を減らすオプションとして定着しつつありますが、本章では最も一般的な in-cluster モデルを中心に見て、AMP への remote write オプションを併せて触れます。

kube-prometheus-stack — 一度で入る標準のまとまり #

第19章 可観測性 §「kube-prometheus-stack」で触れた標準 Helm チャートです。一つのコマンドに Prometheus + Grafana + Alertmanager + kube-state-metrics + node-exporter + Prometheus Operator の CRD がすべて入ってきます。第18章 CRD と Operator のその Operator モデルが本格的なメトリックスタックへつながる段階です。

インストール #

kube-prometheus-stack インストール
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/kube-prometheus-stack \
  -n monitoring --create-namespace \
  --values prometheus-values.yaml
prometheus-values.yaml — 核心部分
prometheus:
  prometheusSpec:
    retention: 30d
    retentionSize: "50GB"

    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 100Gi

    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false

    additionalScrapeConfigs:
      - job_name: ec2-spot-instance
        ec2_sd_configs:
          - region: ap-northeast-2

    remoteWrite:
      - url: https://aps-workspaces.ap-northeast-2.amazonaws.com/workspaces/ws-xxx/api/v1/remote_write
        sigv4:
          region: ap-northeast-2

grafana:
  adminPassword: ""
  ingress:
    enabled: true
    ingressClassName: alb
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]'
      alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:...
    hosts:
      - grafana.myshop.example.com
  persistence:
    enabled: true
    storageClassName: gp3
    size: 10Gi

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          resources:
            requests:
              storage: 10Gi
  config:
    route:
      receiver: default
    receivers:
      - name: default

核心となる三つの設定を押さえます。

  • retention: 30d + storageSpec — メトリック30日保管 + EBS 100 GB。30日以上保管するには remoteWrite で AMP や Thanos へ併せて送ります。第9章 PV / PVC / StorageClass の gp3 StorageClass が本格的な運用 PV の出所になります。
  • serviceMonitorSelectorNilUsesHelmValues: false — すべてのネームスペースの ServiceMonitor を自動認識します。myshop ネームスペースの ServiceMonitor が monitoring ネームスペースなしでも作動します。
  • remoteWrite — AWS Managed Prometheus (AMP) にメトリックを long-term 保存します。30日以上の分析が必要な場合のオプションです。

Grafana の Ingress は 第22章 アプリ配備の骨格 で作った AWS Load Balancer Controller が ALB に解決してくれます — 同じコンポーネントが myshop-api、ArgoCD、Grafana の三つの入り口をすべて担当する形です。

インストール直後の点検 #

基本のヘルスチェック
kubectl get pods -n monitoring
kubectl get servicemonitors -A
kubectl get prometheusrules -A
期待される出力
NAME                                                      READY   STATUS    RESTARTS
prometheus-grafana-xxx                                    3/3     Running   0
prometheus-kube-prometheus-operator-xxx                   1/1     Running   0
prometheus-kube-state-metrics-xxx                         1/1     Running   0
prometheus-prometheus-kube-prometheus-prometheus-0        2/2     Running   0
prometheus-prometheus-node-exporter-xxx                   1/1     Running   0
alertmanager-prometheus-kube-prometheus-alertmanager-0    2/2     Running   0

基本の PrometheusRule が約100個ほど自動で入ってきます。ノードダウン、etcd 障害、kubelet の問題のような K8s 自体のアラートがその中にあらかじめ定義されているので、クラスタの事故は別作業なしですぐにアラートになります。第14章 RBAC / NetworkPolicy / ResourceQuota で扱ったポリシー違反も、kube-state-metrics を通じて自動でメトリック化されます。

myshop-api にメトリック露出を追加 #

標準スタックがインストールされたクラスタに、myshop-api のメトリックを露出する一連の流れです。

1. アプリケーションが /metrics を露出 #

Prometheus クライアントライブラリがほぼすべての言語にあります。Python (FastAPI) なら次の一行で始まります。

myshop-api/main.py — Prometheus メトリック露出
from fastapi import FastAPI
from prometheus_fastapi_instrumentator import Instrumentator

app = FastAPI()
Instrumentator().instrument(app).expose(app)

この一行が次のメトリックを自動で露出します。

  • http_requests_total{handler, method, status} — リクエストカウンター
  • http_request_duration_seconds_bucket{handler, method} — latency ヒストグラム
  • http_request_size_bytes / http_response_size_bytes — ペイロードサイズ
  • 標準 Python runtime メトリック (GC, threads, memory)

ドメインメトリック (注文作成カウンター、決済成功率など) はその上に追加します。

ドメインメトリック追加
from prometheus_client import Counter, Histogram

orders_created = Counter(
    "myshop_orders_created_total",
    "Total orders created",
    ["status"]
)

checkout_duration = Histogram(
    "myshop_checkout_duration_seconds",
    "Checkout flow duration"
)

第19章 §「RED · USE · 4 golden signals」で触れたビジネスメトリックの質感が、本章で実際のコード一行へつながる段階です。

2. ServiceMonitor マニフェスト #

Prometheus Operator が watch する ServiceMonitor を作ります。

charts/myshop-api/templates/servicemonitor.yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: {{ include "myshop-api.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    app.kubernetes.io/name: myshop-api
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: myshop-api
  endpoints:
    - port: http
      interval: 30s
      path: /metrics

このマニフェストが適用された瞬間から、Prometheus が30秒ごとに myshop-api のすべての Pod の /metrics を取得し始めます。Grafana の Explore で http_requests_total{namespace="myshop"} でデータが見えれば、メトリック収集は正常です。第18章 の ServiceMonitor CRD が、本格的なメトリックパイプラインの一つのマニフェストとして定着する形です。

4 golden signals — アラートルールセットの骨格 #

第19章 で扱った 4 golden signals (Latency / Traffic / Errors / Saturation) を、myshop-api の PrometheusRule に書きます。

charts/myshop-api/templates/prometheusrule.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: {{ include "myshop-api.fullname" . }}
  namespace: {{ .Release.Namespace }}
  labels:
    release: prometheus
spec:
  groups:
    - name: myshop-api.golden-signals
      interval: 30s
      rules:
        # Errors — 5xx 比率
        - alert: MyshopApiHighErrorRate
          expr: |
            sum(rate(http_requests_total{app="myshop-api",status=~"5.."}[5m]))
              / sum(rate(http_requests_total{app="myshop-api"}[5m])) > 0.05
          for: 5m
          labels:
            severity: critical
            team: backend
          annotations:
            summary: "myshop-api 5xx rate > 5% ({{ "{{ $value | humanizePercentage }}" }})"
            description: "5xx 比率が 5% を超えた状態が 5分以上維持されている。"
            runbook_url: "https://runbooks.myshop.example.com/myshop-api-5xx"

        # Latency — P95
        - alert: MyshopApiHighLatencyP95
          expr: |
            histogram_quantile(0.95,
              sum by (le) (rate(http_request_duration_seconds_bucket{app="myshop-api"}[5m]))
            ) > 1.0
          for: 10m
          labels:
            severity: warning
            team: backend
          annotations:
            summary: "myshop-api P95 latency > 1s ({{ "{{ $value | printf \"%.2f\" }}" }}s)"

        # Traffic — トラフィック急減 (downstream 障害のシグナル)
        - alert: MyshopApiTrafficDrop
          expr: |
            sum(rate(http_requests_total{app="myshop-api"}[5m]))
              < 0.3 * sum(rate(http_requests_total{app="myshop-api"}[5m] offset 1h))
          for: 10m
          labels:
            severity: warning
            team: backend
          annotations:
            summary: "myshop-api traffic 急減 (過去1時間比 30% 未満)"

        # Saturation — Pod メモリ使用率
        - alert: MyshopApiPodMemoryHigh
          expr: |
            sum by (pod) (
              container_memory_working_set_bytes{namespace="myshop",pod=~"myshop-api-.*"}
            ) / sum by (pod) (
              kube_pod_container_resource_limits{namespace="myshop",pod=~"myshop-api-.*",resource="memory"}
            ) > 0.85
          for: 10m
          labels:
            severity: warning
            team: backend
          annotations:
            summary: "myshop-api Pod memory > 85% of limit"

各ルールの核心となるパターンを三つ押さえます。

  • for 期間 — 短いスパイクでアラートが鳴らないように、5 ~ 10分の持続時間を要求します。
  • severity ラベルcritical は即時呼び出し、warning は次の営業日にレビュー。Alertmanager ルーティングのキーです。
  • runbook_url — アラートを受けた人が即座にたどれる対応手順の文書です。アラート1件 = 明確な対応一つ という原則に従います。

第24章 CI / CD パイプライン の Argo Rollouts AnalysisTemplate が、本章の一つ目のルール (5xx 比率) と同じ PromQL 表現式を自動化の入力として使います。同じメトリックが人のアラートとコードの promote の決定の二箇所に同時に使われる形 が可観測性の運用価値です。

Alertmanager ルーティング — Slack と PagerDuty の分岐 #

アラートの流れは Prometheus → Alertmanager → チャネルです。Alertmanager がラベルを見てルーティングを決定します。

alertmanager.yaml — 運用ルーティング
route:
  receiver: default
  group_by: ['alertname', 'team', 'namespace']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h

  routes:
    - matchers:
        - severity = "critical"
      receiver: pagerduty-backend
      continue: true
      routes:
        - matchers:
            - severity = "critical"
            - team = "backend"
          receiver: pagerduty-backend
        - matchers:
            - severity = "critical"
            - team = "platform"
          receiver: pagerduty-platform

    - matchers:
        - severity = "warning"
      receiver: slack-warnings
      group_wait: 1m
      repeat_interval: 12h

receivers:
  - name: default
    slack_configs:
      - api_url: ${SLACK_WEBHOOK}
        channel: '#alerts'

  - name: slack-warnings
    slack_configs:
      - api_url: ${SLACK_WEBHOOK}
        channel: '#alerts-warning'
        title: '{{ "{{ .GroupLabels.alertname }}" }}'

  - name: pagerduty-backend
    pagerduty_configs:
      - service_key: ${PAGERDUTY_BACKEND_KEY}

  - name: pagerduty-platform
    pagerduty_configs:
      - service_key: ${PAGERDUTY_PLATFORM_KEY}

inhibit_rules:
  - source_matchers: [severity = "critical"]
    target_matchers: [severity = "warning"]
    equal: [alertname, namespace]

核心となるパターンが三つです。

  • severity 別の分岐 — critical は PagerDuty で呼び出し、warning は Slack で通知します。
  • team 別の分岐 — 同じ critical でも backend / platform チームを別々に呼び出します。
  • inhibit_rules — 同じ alertname の critical が鳴っているあいだ、同じ namespace の warning はまとめて無音処理します。アラート殺到を防ぐ標準パターンです。

秘密 (SLACK_WEBHOOK, PAGERDUTY_*) は 第23章 DB 連携 で扱った External Secrets で注入します。Secrets Manager の myshop/${env}/alerting/* パターンを作っておけば、ESO が自動で K8s Secret へ展開してくれます。

Loki — ログスタック追加 #

メトリックのほかにログも併せて確保するのが標準です。第19章 可観測性 §「Loki — 軽量なログスタック」で見たモデルをそのまま適用します。

Loki + Promtail Helm インストール
helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack \
  -n monitoring \
  --set promtail.enabled=true \
  --set loki.persistence.enabled=true \
  --set loki.persistence.storageClassName=gp3 \
  --set loki.persistence.size=100Gi

インストール後、Grafana に Loki データソースが自動で追加され、Explore で LogQL クエリが可能になります。

myshop-api の ERROR ログ
{namespace="myshop", app="myshop-api"} |= "ERROR"
エラー率メトリックへ換算 (Loki -> メトリックのように)
sum(rate({namespace="myshop", app="myshop-api"} |= "ERROR" [5m]))

長期保管を S3 に置くには Loki の storage backend を S3 に設定します。標準の運用セットアップです。第28章 コスト最適化 で、Loki の S3 バックエンドが EBS 比でコストの質感をどう変えるかを本格的に押さえます。

CloudWatch Container Insights — 二つ目の軸 #

同じ EKS クラスタに CloudWatch Container Insights を併せてインストールすると、AWS コンソールでクラスタ · ノード · Pod · コンテナのメトリックを即座に確認できます。運用チームが AWS コンソールに慣れているなら、日常点検の負担が減ります。

CloudWatch Container Insights — Helm
helm repo add aws-observability https://aws-observability.github.io/helm-charts
helm install amazon-cloudwatch-observability \
  aws-observability/amazon-cloudwatch-observability \
  -n amazon-cloudwatch --create-namespace \
  --set clusterName=myshop-prod \
  --set region=ap-northeast-2

このチャートが Fluent Bit を DaemonSet として立ち上げ、各ノードの stdout / stderr を CloudWatch Logs へ送り、CloudWatch Agent でメトリックも併せて収集します。第8章 StatefulSet · DaemonSet · Job の DaemonSet パターンが、本格的なログ収集エージェントの質感へつながる形です。

DaemonSet の ServiceAccount が IRSA で CloudWatch の PutLogEventsPutMetricData 権限を受け取ります — 第21章 の EBS CSI IRSA パターンと同じ方向です。

Fluent Bit の二つの出口 #

Fluent Bit がノードの /var/log/containers/ を読んで次の二箇所へルーティングするのが標準です。

Fluent Bit の二つの出口
コンテナログ
   |
   |-> Loki (in-cluster, 短期検索)
   `-> CloudWatch Logs (S3 export, 長期保管)

同じログを二箇所へ送る理由は責任が異なるためです — Loki は日常デバッグ、CloudWatch はコンプライアンス · 監査 · 長期分析。コストの面では Loki だけを使う方式が軽いですが、規制環境では CloudWatch が通常一緒に入ってきます。第26章 運用チェックリスト の監査手順で、CloudWatch のログ保管ポリシーが本格的につながります。

Grafana ダッシュボードの標準 #

運用クラスタの Grafana に入る標準ダッシュボードのまとまりを整理します。

ダッシュボード出所
Kubernetes / Compute Resources / Clusterkube-prometheus-stack 基本 (ID 7249)
Kubernetes / Compute Resources / Namespace (Workloads)基本 (ID 7250)
Kubernetes / Compute Resources / Pod基本 (ID 7251)
Kubernetes / Networking / Cluster基本 (ID 7253)
Node Exporter / Nodes基本 (ID 1860)
myshop-api 運用ダッシュボード自前作成 — golden signals + ビジネスメトリック

基本ダッシュボード5個は kube-prometheus-stack が自動で登録してくれるので、別作業なしですぐに見えます。自前ダッシュボード一つだけドメインに合わせて作れば、日常点検の視野がほぼ完成します。

自前ダッシュボードの標準パネルのまとまり #

myshop-api ダッシュボード — 9個のパネル
Row 1: Latency P50 / P95 / P99
Row 2: Request rate (ドメイン別, status 別)
Row 3: Error rate (4xx / 5xx)
Row 4: Pod CPU / メモリ使用率
Row 5: HPA 現在の replicas
Row 6: PgBouncer 活性接続 / 待機キュー
Row 7: ビジネスメトリック (orders/min, checkout success rate)
Row 8: 上位 ERROR ログ (Loki)
Row 9: 最近の配備 (annotations)

最後のパネルの「最近の配備 annotations」は、Grafana に ArgoCD または GitHub Actions のイベントを annotation として注入したものです。メトリックグラフの上に配備時点が縦線で表示され、「この latency スパイクはどの配備直後に発生したか」 が一目で見えます。第24章 の CI / CD と本章の可観測性が一つの画面で接する地点です。

on-call の流れ — runbook と共に #

アラートが鳴ること自体が終わりではありません。受けた人が5分以内にどこを見るべきかが明確でなければなりません。運用標準の流れです。

on-call アラートを受けた直後の標準5分
1. PagerDuty でアラート本文を確認 (alertname, team, severity)
2. annotation の runbook_url をクリック
3. Runbook の「1次点検」セクションをたどる — 関連 Grafana ダッシュボード / ログクエリ / kubectl コマンドを提示
4. 1次対応 (スケールアップ, 再起動, トラフィック遮断など)
5. Slack の事故チャネルに状態を共有

Runbook は別の git repo の Markdown で管理するのが標準です。アラートルールの runbook_url がその repo の一ページを指し、新しいアラートを追加するときに Runbook も一緒に PR で入ってきます。第20章 GitOps の git 単一ソースモデルが、アラート定義から運用手順の文書まで拡張される形です。第27章 kubectl デバッグパターン が、本書の次元でこの Runbook の一部の役割を担うことになります。

最初の運用一連の流れ後の点検 #

スタックをインストールして数日回してみた時点で点検する項目です。

Prometheus の時系列数 (カーディナリティ点検)
kubectl exec -n monitoring prometheus-prometheus-kube-prometheus-prometheus-0 -c prometheus -- \
  promtool tsdb analyze /prometheus/wal | head -50
アラートの発生頻度 (一度も鳴らないルール / 鳴りすぎるルール)
kubectl exec -n monitoring alertmanager-prometheus-kube-prometheus-alertmanager-0 -c alertmanager -- \
  amtool alert query --alertmanager.url=http://localhost:9093
Loki のディスク使用量
kubectl exec -n monitoring loki-0 -- df -h /data

運用一か月が過ぎると、カーディナリティの急増、アラート SNR (signal-to-noise ratio) の低下、ログディスクの圧迫が通常一度ずつ来ます。この点検を定期点検に置くのが標準で、第26章 運用チェックリスト の月次点検節に、本章の三つのコマンドがそのまま入ります。

カーディナリティ急増の落とし穴 #

最も多い運用事故は、メトリックラベルに 高カーディナリティの値 (ユーザー ID、リクエスト ID、UUID など) が入ることです。時系列数が急増して Prometheus が OOM で落ちるか、AMP コストが突然数十倍になります。ラベルには 有限の enum 値 (status code、handler 名、environment) だけを置くのが原則で、詳細な識別子はログやトレースに抜きます。

練習問題 #

  1. 本章の kube-prometheus-stack を dev EKS クラスタにインストールして、myshop-api に ServiceMonitor + PrometheusRule の一まとまりを適用します。わざと 5xx をしばらく発生させ、MyshopApiHighErrorRate アラートが for: 5m の持続時間を経て発火する様子を観察します。Alertmanager UI で alert の状態が pending から firing へ移る時点を可視化し、第24章 の Argo Rollouts AnalysisTemplate が同じ PromQL をどう分析に活用するかを一段落で比較します。
  2. Alertmanager のルーティングツリーを、ご自身の組織のチーム構成に合わせて設計し直します。severity (critical / warning / info) × team (backend / platform / data) のマトリックスを一つの表に描き、各セルにどのチャネル (PagerDuty / Slack チャネル名) が入るかを埋めます。inhibit_rules がどんなアラート殺到を防ぐかを、ご自身のシナリオで二つ三つ書いてみます。
  3. メトリックラベルにユーザー ID が入る誤って設計されたルールをわざと作って適用したあと、カーディナリティ点検コマンドで時系列数の変化を観察します。Prometheus のメモリ使用量がどう変わるか、AMP の remote write コストがどう急増しうるかを、第28章 コスト最適化 の観点から一段落で整理します。

一行まとめ: 運用クラスタの可観測性は in-cluster Prometheus + マネージド CloudWatch の二つの軸の組み合わせが標準で、kube-prometheus-stack が Prometheus · Grafana · Alertmanager · kube-state-metrics · node-exporter の五つのコンポーネントを一つのコマンドで展開してくれる。ServiceMonitor + PrometheusRule で myshop-api の 4 golden signals アラートを書き、Alertmanager が severity · team ラベルで PagerDuty / Slack を分岐する。Loki が日常デバッグのログを、CloudWatch Logs が長期保管 · 監査のログを分担する。同じメトリックが人のアラートとコードの promote の決定に同時に使われる形が可観測性の運用価値であり、runbook_url でアラートと対応手順を束ねるパターンが on-call の標準。ラベルの高カーディナリティが最も多い運用事故の出所。

次の章 #

この時点で myshop-api は、コードから配備 · 運用 · 観測まで一連の流れがすべて自動化された状態です。次の章であり 4部の最後の章では、このクラスタを一か月、一四半期、一年単位で安全に回す定期運用サイクルを扱います。

第26章 運用チェックリスト では、EKS アップグレード、RDS バックアップ · 復旧、コスト点検、セキュリティ点検の標準手順を整理し、4部 (EKS 実戦) 6章の振り返りで締めくくります。その後は 5部 (運用 · デバッグ · コスト) の本格運用の質感へ移ります。

X