背景:アラートが実際の障害より多い問題
アラート疲労は、監視環境を初めて構築したときに実際に経験した問題です。閾値を10回以上調整した後、問題は閾値にあるのではなく、Alertmanagerの設定方法にあることに気づきました。障害が発生するたびに、CPU高負荷、メモリ高負荷、レスポンスタイム上昇、ヘルスチェック失敗といった数十件のアラートが一斉に届きます——すべて同じ1つの原因から発生しているにもかかわらず。数週間後には、チーム全員がSlackの監視チャンネルを無視するようになっていました。
実はAlertmanagerには、この問題をまさに解決するための2つの機能が用意されています:
- Silences:ダウンタイムが予定されているときやメンテナンス中に、特定のアラートグループを指定した期間だけ一時的に無効化する
- Inhibition Rules:メインアラートが発火した際に、関連する二次アラートを自動的に抑制する——根本原因のアラートが発火している間は、症状アラートを削減する
この記事では、この2つの機能を直接取り上げます。すでにAlertmanagerが基本的なreceiverの設定で動作していることを前提とします。
amtoolのインストール — Alertmanager CLIツール
amtoolはAlertmanagerのインストールパッケージに同梱されており、ターミナルから操作するための主要なツールです。まだインストールしていない場合:
# Alertmanagerをダウンロード(amtoolはパッケージに含まれています)
wget https://github.com/prometheus/alertmanager/releases/download/v0.27.0/alertmanager-0.27.0.linux-amd64.tar.gz
tar xvf alertmanager-0.27.0.linux-amd64.tar.gz
sudo mv alertmanager-0.27.0.linux-amd64/amtool /usr/local/bin/
# amtoolのデフォルト設定ファイルを作成(毎回URLを入力しなくて済む)
mkdir -p ~/.config/amtool
cat > ~/.config/amtool/config.yml <<EOF
alerting.url: http://localhost:9093
EOF
# 接続確認
amtool alert query
詳細設定
Silences — 意図的なアラートの停止
最もシンプルな方法は、http://your-server:9093のUI、Silences タブ → New Silence から行う方法です。ただし実際には、スクリプトに統合できることからamtoolをより多く使っています。
# webサービスの全アラートを2時間サイレンス
amtool silence add \
--duration=2h \
--comment="Deploy v2.1.0 — expected restart" \
alertname=~".+" \
service="web"
# アクティブなサイレンス一覧を表示
amtool silence query
# デプロイが予定より早く完了した場合、即座にサイレンスを終了
amtool silence expire <silence-id>
現在はamtoolをCI/CDパイプラインに統合しています。各デプロイ前に、スクリプトが自動的に30分間のサイレンスを作成します。ヘルスチェックが通過した後、即座に終了させることで、リリースのたびに発生する数十件の誤検知アラートを防いでいます:
#!/bin/bash
# deploy-with-silence.sh
SILENCE_ID=$(amtool silence add \
--duration=30m \
--comment="CI/CD deploy $(git rev-parse --short HEAD)" \
environment="production" \
service="api" \
--output=json | jq -r '.silenceID')
echo "Silence created: $SILENCE_ID"
# デプロイを実行
./deploy.sh
# ヘルスチェック通過後、サイレンスを終了
amtool silence expire "$SILENCE_ID"
echo "Silence expired"
重要な注意点:サイレンスを作成する際は必ずcommentフィールドを入力してください。2週間後には、なぜそのサイレンスが存在するのか自分でも覚えていませんし、チームの他のメンバーはなおさらわかりません。
Inhibition Rules — 二次アラートを自動的にブロックするロジック
この機能は、アラートノイズの根本的な問題を実際に解決できるにもかかわらず、思った以上に見過ごされています。原理はシンプルです:アラートAがアクティブな場合、指定されたラベルが一致するすべてのアラートBを自動的にブロックします。
最も典型的な例:ノードがダウンすると、そのノード上のすべてのサービスも当然ダウンします。20件の個別アラートを受け取る必要はありません——NodeDownアラート1件で、何をすべきかわかります。
/etc/alertmanager/alertmanager.ymlファイルに追加します:
inhibit_rules:
# ノードダウン時、そのノードからのwarning/critical severity全アラートをブロック
- source_matchers:
- alertname = "NodeDown"
target_matchers:
- severity =~ "warning|critical"
equal:
- instance
# criticalアラートが同一サービスのwarningアラートをブロック(criticalが発火中のwarningスパムを防ぐ)
- source_matchers:
- severity = "critical"
target_matchers:
- severity = "warning"
equal:
- alertname
- namespace
- service
# データベースダウンがアプリケーションエラーアラートをブロック(根本原因が明らかな場合に症状をブロック)
- source_matchers:
- alertname = "DatabaseDown"
- severity = "critical"
target_matchers:
- alertname =~ "HighErrorRate|SlowResponse|ServiceUnavailable"
equal:
- environment
各フィールドの説明:
source_matchers:「親」アラートの条件——inhibitionを引き起こすアラートtarget_matchers:ブロック対象のアラートの条件equal:sourceとtarget間で同じ値を持つ必要があるラベル——Alertmanagerが2つのアラートが同じインスタンス/サービスに属すると判断するための仕組み
Kubernetesクラスター向けの本番設定例
以下は、Kubernetesマルチクラスター環境で実際に運用している設定です:
inhibit_rules:
# クラスターネットワーク分断がノードレベルのアラートをブロック
- source_matchers:
- alertname = "ClusterNetworkPartition"
target_matchers:
- alertname =~ "NodeDown|NodeUnreachable"
equal:
- cluster
# ノード未準備状態がそのノード上のPodアラートをブロック
- source_matchers:
- alertname = "KubeNodeNotReady"
target_matchers:
- alertname =~ "KubePodCrashLooping|KubePodNotReady|KubeDeploymentReplicasMismatch"
equal:
- node
- cluster
# PVCフル状態がアプリケーション書き込みエラーをブロック(ストレージが根本原因の場合に症状をブロック)
- source_matchers:
- alertname = "KubePersistentVolumeFillingUp"
- severity = "critical"
target_matchers:
- alertname = "AppWriteError"
equal:
- namespace
- persistentvolumeclaim
もう1つよく使うトリック:手動でサイレンスを作成する代わりに、「メンテナンスウィンドウアラート」を定義して自動的にinhibitする方法です。具体的には、MaintenanceWindowという名前のPrometheus recording ruleにenvironment="production"ラベルを付け、メンテナンス時間帯だけアクティブになるように定義します。その後、inhibition ruleが自動的にその期間中のすべてのwarning/infoアラートをブロックするため、手動で操作する必要がありません。
確認とモニタリング
Inhibition Rulesが正しく動作していることの確認
# アクティブな全アラートを表示
amtool alert query
# inhibitされたアラートとサイレンスされたアラートも含めて表示
amtool alert query --silenced --inhibited
# 特定のラベルでフィルタリング
amtool alert query severity=critical environment=production
# リロード前に設定ファイルを検証
amtool check-config /etc/alertmanager/alertmanager.yml
アラートが意図せずブロックされている場合は、APIを直接使用してデバッグします:
# inhibit理由を含めてアラートを表示
curl -s 'http://localhost:9093/api/v2/alerts?inhibited=true&silenced=true' | \
jq '.[] | {alertname: .labels.alertname, state: .status.state, inhibitedBy: .status.inhibitedBy}'
有効性を追跡するAlertmanagerメトリクス
Alertmanager自体のメトリクスをスクレイプします——PrometheusがAlertmanagerを自己監視するための最速の方法です:
# prometheus.yml
scrape_configs:
- job_name: 'alertmanager'
static_configs:
- targets: ['localhost:9093']
最も役立つメトリクス:
alertmanager_alerts{state="active"}— アクティブなアラート数alertmanager_alerts{state="suppressed"}— 抑制されたアラート数(inhibitとsilenceの合計)alertmanager_silences{state="active"}— アクティブなサイレンス数alertmanager_notifications_total— 送信した通知の合計数alertmanager_notifications_failed_total— 失敗した通知数
Grafanaで抑制率を監視するクエリ:
# 全アラートに対する抑制されたアラートの割合
alertmanager_alerts{state="suppressed"} / ignoring(state) sum without(state)(alertmanager_alerts)
inhibition rulesを追加した後にこの割合が上昇すれば、それは良い兆候です。チームへの通知数は減りますが、受け取るアラートにはすべて確認する価値があります。
実運用から得た注意点
設定を何度も修正した経験から得た知見:
equalフィールドはsourceとtargetの両方に存在する必要がある——どちらか一方のアラートにそのラベルがない場合、ルールはマッチせず、inhibitionは機能しません。初回のデバッグ時にかなりの時間を費やした問題です。- inhibitの範囲を広げすぎない——sourceアラートの症状として明らかなアラートだけをブロックし、同じ環境からのすべてのアラートをブロックしないようにする。
- 設定のリロードに再起動は不要:
curl -X POST http://localhost:9093/-/reloadで十分。 - Silenceは長期的な解決策ではない——同じアラートに対して頻繁にサイレンスを作成する必要がある場合、閾値の設定が間違っているか、inhibition ruleを追加すべきサインです。
基本的なAlertmanagerがすでに動作しているなら、inhibition rulesの追加は約30分で完了します。システムによっては、1日あたりの通知数が50〜70%削減されることもあります。さらに重要なのは:すべてのアラートに本当の意味があるため、チームが再び注意を払うようになる点です。
