アラート疲れを防ぐPrometheusアラートルール設計:ノイズを減らす5つの原則
TL;DR
深夜2時にスマホが鳴る。Slackを開くと「CPU使用率が80%を超えました」。数分待つと自然に収まっている。同じ通知が週に何度も続くと、「これはどうせ自然に収まる」という判断が体に染み付いていく。そうなったとき本当に落ちているサービスの通知まで、同じトーンで流されてしまう危険がある。
- アラート疲れの本質は「通知の数」そのものより、対応不要な通知に慣れて重要な通知まで同じ扱いになることにある
- 対策は閾値のチューニングだけでは終わらない。継続時間の要求・重要度による通知経路の分離・SLOベースへの寄せ方・自動復旧するものを黙らせる設計・Runbookリンクの必須化という5つを積み重ねる必要がある
- 実際に動くPromQLアラートルール例を示す。インストール・導入手順はPrometheus + GrafanaをVPSに入れる最小構成を参照してほしい。本記事はルール設計だけに絞る
アラート疲れとは何か、なぜ危険か
アラート疲れ(alert fatigue)は、優先順位の低い・対処不要なアラートに頻繁に反応させられることで、担当者がアラートそのものに反応しなくなっていく現象だとSysdigの解説は説明している。医療現場のモニターアラーム研究から輸入された言葉で、SREの文脈でも同じ構造がそのまま当てはまる。
厄介なのは、閾値越えの通知を一つ二つ無視しても、その瞬間には何も起きないことだ。実害が出るのは「重大な障害の通知」が「いつものノイズ」に紛れて見逃されたときで、その時点では原因が積み重なった結果としてしか気づけない。オンコール担当が「またか」と思った瞬間に、監視システムは本来の役割——本当に対応が必要な事象を、確実に人に届けること——を失う。
AWSのアラート運用に関する解説も、アラートは「テレメトリを行動に変換する」ためのものだと位置づけている。行動につながらない通知を出し続けることは、その定義そのものに反する。だからアラートルールの設計目標は「異常を検知すること」ではなく、「対応が必要な異常だけを、必要な人に届けること」に置き直す必要がある。
ノイズを減らす5つの原則
1. 閾値だけで発報せず、forで継続時間を要求する
一瞬だけ閾値を超えて数秒で戻るスパイクは、多くの場合対応不要なノイズだ。Prometheusのアラートルールにはforフィールドがあり、指定した期間ずっと条件を満たし続けた場合だけ発火(firing)に遷移する。条件を満たしている間だけ待機(pending)状態になり、forの時間に届く前に条件が外れれば何も通知されない。
さらに新しめのバージョンではkeep_firing_forも使える。これは条件が解消した後も指定時間はfiring状態を維持するフィールドで、条件がオン・オフを短い周期で繰り返す「フラッピング」を抑えるのに使える。閾値ぴったりの値が続くケースで通知が点滅するのを防げる。
2. severityラベルで重要度を分け、通知先を変える
すべてのアラートを同じSlackチャンネル・同じ電話に飛ばすと、重大度に関係なく同じ緊張感で届く。ルール側にseverity: critical / severity: warningのようなラベルを持たせ、Alertmanager側のrouteでラベルごとに受信先・再通知間隔を分けるのが基本設計になる。criticalは即オンコール呼び出し、warningは日中対応のSlackチャンネルへ、といった具合だ。
3. 閾値ベースでなくSLO・エラーバジェットの消費速度(バーンレート)で発報する
CPU使用率や単発のエラー件数を直接見るアラートは、正常な負荷変動でも簡単に閾値を超える。Googleの手法として広く参照されるのが、SLO(サービスレベル目標)に対するエラーバジェットの消費速度=バーンレートでアラートする方式だ。「バーンレート14.4倍」は「このペースが続くと本来30日かけて使うはずのエラーバジェットを1時間で2%消費する」ことを意味し、短い時間窓(例:5分)と長い時間窓(例:1時間)の両方が同時に閾値を超えたときだけ発報する「マルチウィンドウ」構成にすることで、一時的なノイズと本当の悪化を区別しやすくなる。
4. 自動復旧するものはアラートしない
Kubernetesのポッドが1回再起動して自己修復した、リトライで自然に回復した——こうした「システムが自分で直したもの」まで毎回通知すると、オンコール担当が「見なくていい通知」を学習してしまう。単発の再起動ではなく「一定時間内に繰り返し再起動している」場合だけアラートするなど、自己修復のための余白をルールの側に組み込んでおく。
5. アラートにはRunbook(対応手順)を必ずリンクする
annotationsは自由なキーを設定できるテンプレートなので、summaryとdescriptionに加えてrunbook_urlというキーを慣例として持たせておくと、通知を受け取った瞬間に「何を見て」「何をすればいいか」へ最短距離で移動できる。深夜に叩き起こされた状態で調査をゼロから始めさせる設計は、疲弊を早める一番の原因になる。
実際のPromQLアラートルール例
例1:継続時間・重要度・Runbookリンクを備えた基本形
groups:
- name: node-alerts
rules:
- alert: HighCpuUsage
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 15m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} のCPU使用率が高い状態が続いています"
description: "直近15分間、CPU使用率が85%を超え続けています。一時的なバッチ処理でなければ調査してください。"
runbook_url: "https://runbooks.example.com/high-cpu-usage"
node_cpu_seconds_totalはnode_exporterが公開する標準メトリクスで、idle(待機)モードの割合を100から引くことで使用率を求めている。15分継続という条件があるため、一時的なビルドやバックアップ処理のスパイクでは発報しない。
例2:SLOバーンレートによるマルチウィンドウアラート
groups:
- name: slo-burn-rate
rules:
- record: job:http_requests_error:ratio_rate5m
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m])) by (job)
/
sum(rate(http_requests_total[5m])) by (job)
- record: job:http_requests_error:ratio_rate1h
expr: |
sum(rate(http_requests_total{status=~"5.."}[1h])) by (job)
/
sum(rate(http_requests_total[1h])) by (job)
- alert: HighErrorBudgetBurn
expr: |
job:http_requests_error:ratio_rate5m > (14.4 * 0.001)
and
job:http_requests_error:ratio_rate1h > (14.4 * 0.001)
for: 2m
labels:
severity: critical
annotations:
summary: "{{ $labels.job }} のエラーバジェット消費が急増しています"
description: "5分・1時間の両方のウィンドウでエラー率がSLO閾値の14.4倍を超えています。このペースだと数時間で月間のエラーバジェットを使い切ります。"
runbook_url: "https://runbooks.example.com/http-error-budget-burn"
0.001は「SLO 99.9%」の場合の許容エラー率(1 - 0.999)にあたる。ここは自環境のSLO値に置き換える前提の数値であり、そのままコピーして使う値ではない。短い窓(5分)と長い窓(1時間)の両方が条件を満たしたときだけ発報するため、単発の瞬間的なエラーバーストでは鳴らない設計になっている。
まとめ
アラート疲れは、アラートの絶対数を減らすことだけでは解決しない。閾値越えの瞬間ではなく継続時間で判断し(for)、重要度で届け先を分け(severity+ Alertmanagerのroute)、閾値そのものをSLOのバーンレートに寄せ、自己修復するものは黙らせ、最後に必ずRunbookへの導線を持たせる——この5つを積み重ねることで、「本当に対応が必要な通知」だけがオンコール担当に届く状態に近づく。導入直後は静かすぎるくらいに絞り込み、そこから実際に見逃したくない事象を足していくほうが、最初から広く網を張って疲弊するより結果的に長続きする。
監視ルールの棚卸し・SLO設計の相談にも対応しています。構築・設計の相談はこちら