社内向けに死活監視の仕組みを自作しよう、あるいは既存の監視SaaSを見直そうと考えているインフラ担当者に向けた内容です。個人開発者が公開した稼働監視サービス「CheckForge」の構成には、SREの現場でも繰り返し踏まれる落とし穴がいくつも含まれています。何が起きやすいのかを整理し、自分たちの監視基盤が同じ穴にはまっていないか確認する手順をまとめました。
CheckForgeはFastify(Node.js製の軽量Webフレームワーク)とCloudflare Workers(エッジで動くサーバーレス実行環境)、Supabase(PostgreSQLをベースにしたBaaS基盤)を組み合わせた稼働監視SaaSです。ウェブサイトやAPI、SSL証明書の有効期限を定期チェックし、異常時にEmail・Slack・Discord・Webhookで通知します。開発者自身が「認証とダッシュボードで終わる個人開発プロジェクトが多い中、バックグラウンドワーカーやインシデント管理まで踏み込んだ」と述べている通り、監視SaaSは一見単純でも実装すると壁にぶつかる領域です。
何が起きるか:重複アラートとインシデントの二重発生
死活監視を自作すると最初にぶつかるのが、同じ障害に対して通知が何度も飛ぶ現象です。
たとえば、10分間隔でヘルスチェックを実行する設定で、対象サーバーが5分だけ不安定になったとします。チェックのタイミングとずれが重なると、1回の障害に対してSlack通知が3〜4回連続で飛ぶことがあります。
CheckForgeの開発者も「重複アラートの防止(preventing duplicate alerts)」を、実装して初めて直面した課題の一つに挙げています。監視対象が増えるほど、この問題は目立つようになります。
影響範囲はアラート疲れ(alert fatigue、通知が多すぎて重要な警告を見逃す状態)に直結します。Slackチャンネルが誤検知やチャタリング(状態が短時間で行ったり来たりする現象)の通知で埋まると、本当に緊急の障害通知が埋もれてしまいます。
なぜ起きるか:段階的な原因分解
原因は一つではなく、複数のレイヤーが絡み合っています。順番に見ていきます。
第一の原因は、チェック実行とインシデント状態管理が分離されていないことです。ヘルスチェックのたびに「失敗した」という結果だけを見て通知を送るロジックだと、失敗が続く限り毎回アラートが飛びます。本来は「正常→異常」の状態遷移の瞬間にだけ通知すべきで、これがインシデントライフサイクル管理(incident lifecycle management)と呼ばれる設計です。
第二の原因は、分散実行環境特有のタイミング問題です。CheckForgeはCloudflare Workersでチェックを実行しますが、エッジ環境は世界中のロケーションで並行実行されるため、同一対象への複数リージョンからのチェックが同時に失敗を検知し、それぞれが独立して通知処理を走らせる可能性があります。単一サーバーのcronジョブでは起きにくい問題です。
第三の原因は、タイムアウト処理の甘さです。ヘルスチェックのタイムアウト設定が短すぎたり長すぎたりすると、実際には生きているサーバーを「ダウン」と誤検知します。開発者自身も「タイムアウト処理(timeout handling)」を学びの一つに挙げており、閾値設計の難しさを裏付けています。
自分のプロジェクトが該当するか確認する方法
まず、自作・導入済みの監視システムで以下を確認してください。
- 通知ロジックが「チェック失敗のたびに送信」なのか「状態遷移時のみ送信」なのか、通知処理のコードを直接読む
- 同一障害に対して複数リージョン・複数ワーカーから並行してチェックが走る構成か、実行環境の構成図やcron設定を確認する
- タイムアウト値がハードコードされていないか、環境変数や設定ファイル(
.envやconfig.yaml等)をgrep timeoutで検索する - インシデントのステータス(open/resolved等)を管理するテーブルやステートマシンが存在するか、DBスキーマを確認する
PrometheusのAlertmanagerを使っている場合は、inhibition(抑制ルール)やgroup_wait・group_interval・repeat_intervalの設定値をalertmanager.ymlで確認するとよいです。これらが未設定・デフォルトのままだと、同じ問題が発生しやすくなります。
DatadogやNew Relicなどの商用APMを使っている場合も、モニターの「Notify Again」設定や「Renotify Interval」を見直す価値があります。
対策の手順
対策は大きく3段階に分けて進められます。
ステップ1: 状態遷移ベースの通知に切り替える
チェック結果を毎回そのまま通知するのではなく、前回のステータスと今回のステータスを比較し、変化があった場合のみ通知するロジックに変更します。SupabaseやPostgreSQLを使っているなら、監視対象ごとにlast_statusカラムを持つテーブルを用意し、更新前後を比較するだけで実装できます。
ステップ2: デバウンス(debounce)を導入する
1回の失敗検知で即通知せず、「連続N回失敗したら通知」という閾値を設けます。CheckForgeのような構成であれば、Redis(インメモリのデータストア)に失敗カウンタを持たせ、閾値を超えた時点で初めてアラート処理を呼び出す設計が有効です。これはSLO(Service Level Objective、サービスレベル目標)運用でよく使われる「バーンレートアラート」の考え方にも通じます。急激なエラー消費だけでなく、緩やかな劣化にも段階的に反応する設計です。
ステップ3: 冪等性(idempotency)を持たせる
同じインシデントIDに対する通知処理が複数回走っても、実際の送信は1回だけになるよう、通知送信前に「送信済みフラグ」をアトミックに確認・更新する処理を入れます。分散環境では複数ワーカーが同時に同じインシデントを検知する可能性があるため、この一手間が重複防止の最後の砦になります。
まとめ
監視システムの落とし穴は、機能追加よりも状態管理の設計に起因することが多いです。
- 通知ロジックが「毎回送信」になっていないか、コードを読んで確認する
- 分散実行環境(Cloudflare Workersのようなエッジ環境含む)ではタイミング競合が起きやすいことを前提に設計する
- Alertmanagerや商用APMを使っているなら
repeat_interval・group_wait・Renotify設定を今すぐ見直す - 状態遷移ベースの通知・デバウンス・冪等性の3点は、自作でも既存ツールでも適用できる普遍的な対策です
まずは自分の監視システムのアラート送信箇所のコードを開き、「失敗するたびに送っているか」「状態が変わった時だけ送っているか」を確認するところから始めてみてください。