Prometheus × Alertmanager:カスタムWebhookレシーバーによる「運用自動化の聖域」への到達
多くのエンジニアは、Alertmanagerの通知をSlackやPagerDutyに投げて満足する。しかし、真のオブザーバビリティ・アーキテクトは、その先を見ている。通知を受け取った瞬間に何が起きるか? ログの相関分析が自動で行われ、チケットが起票され、場合によっては自動復旧スクリプトが発火する。
今回は、Alertmanagerの汎用的な通知を、我々の「運用自動化エコシステム」へとシームレスに接続するための、GoによるカスタムWebhookレシーバーの極致を解説する。
—
1. Alertmanagerペイロード:その「深淵」を読み解く
Alertmanagerが送出するWebhookは、単なる「アラートの羅列」ではない。ここには、システムの健康状態と文脈(Context)が凝縮されている。
{
“status”: “firing”,
“alerts”: [
{
“labels”: { “alertname”: “HighLatency”, “instance”: “api-prod-01” },
“annotations”: { “summary”: “Latency exceeding 500ms” },
“startsAt”: “2023-10-27T10:00:00Z”
}
],
“commonLabels”: { “env”: “prod” },
“groupLabels”: { “alertname”: “HighLatency” }
}
この構造の肝は `groupLabels` と `commonLabels` にある。複数のアラートが1つの通知にグループ化される際、どのラベルが共通項として抽出されたかを理解することが、高精度なルーティングの鍵となる。単純なパースではなく、この「グループ化のメタデータ」を活用して、チケットの優先度や宛先を動的に決定するのが、プロの仕事だ。
—
2. Goによる「超軽量・高耐久」レシーバー実装
HTTPサーバーを立ち上げるのは簡単だが、高負荷時に落ちるレシーバーはゴミだ。メモリ効率と並行処理を極限までチューニングする。
package main
import (
“encoding/json”
“net/http”
“sync”
)
// 構造体はポインタ演算やメモリ配置を意識し、アロケーションを最小限に抑える
type AlertPayload struct {
Status string `json:”status”`
Alerts []map[string]interface{} `json:”alerts”`
}
var workerPool = make(chan struct{}, 100) // 処理のバックプレッシャー制御
func webhookHandler(w http.ResponseWriter, r http.Request) {
// 1. バッファリングを最小限に抑えるため、Streamを直接デコード
var payload AlertPayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, “Bad Request”, http.StatusBadRequest)
return
}
// 2. 非同期ルーティングへ投げる(メインループをブロックしない)
workerPool <- struct{}{}
go func(p AlertPayload) {
defer func() { <-workerPool }()
processAlerts(p)
}(payload)
w.WriteHeader(http.StatusAccepted)
}
func main() {
http.HandleFunc("/hook", webhookHandler)
// タイムアウト設定を厳格に行い、Slowloris攻撃やリソース枯渇を防ぐ
server := &http.Server{
Addr: ":8080",
ReadHeaderTimeout: 3 time.Second,
}
server.ListenAndServe()
}
この実装の「魂」:
- バックプレッシャー制御: `workerPool` を導入し、アラートが爆発(Alertstorm)した際も、サーバーがメモリ不足で死なないようにガードする。
- 非同期処理: 受信と処理を完全に分離し、Alertmanager側を待たせない。
—
3. 現場で震えるほど役立つ「高度なルーティングと自動化」
単にチャットに飛ばすだけでは、運用は改善しない。以下のテクニックを組み込むことで、監視は「監視」から「制御」へと進化する。
A. 相関分析とチケット統合
受け取った `instance` ラベルをキーに、直近のトレースデータやログの統計情報をAPI経由で取得し、チケットに付与する。
- 実装のコツ: Prometheusの `query_range` APIを叩き、アラート発生前後5分間のエラーレート推移をチケットにMarkdownで貼り付ける。
B. 自動修復(Self-Healing)のトリガー
特定のラベル(例: `auto_remediate: “true”`)が付与されている場合、レシーバーは即座に内部のCLI実行エンジンを起動する。
- 極意: 実行するスクリプトは必ずべき等性(Idempotency)を担保すること。`kubectl rollout restart` のような命令を、冪等性を考慮せず連打するようなコードは論外だ。
C. 内部メトリクスの出力
自作レシーバー自身が監視対象になることを忘れてはならない。以下のメトリクスをPrometheusで公開する(自律監視)。
- `received_alerts_total`: ステータス別受信数
- `processing_latency_seconds`: 処理にかかった時間(Histogram)
- `worker_queue_depth`: 現在の滞留数
—
結びに代えて:オブザーバビリティのその先へ
監視ツールを「ただの設定」で終わらせるか、自社独自の「インテリジェントな運用プラットフォーム」へと昇華させるか。その分かれ目は、「通知の先にあるアクションをどこまでコード化できるか」に懸かっている。
あなたが書くこのレシーバーは、システムが悲鳴を上げたとき、真っ先に駆けつける「最初のレスポンダー」になる。血の通ったコードで、システムの健全性を守り抜いてほしい。
何かあれば、またコードの深淵で会おう。