
告警 Webhook 整合:讓告警送到對的地方
Alerts & ChatOps 那篇談的是告警策略的設計——什麼時候該告警、分幾級、怎麼路由。但實際上線後最常遇到的問題是:「告警要怎麼串到我們團隊用的通訊平台?」每個平台接受的 JSON 格式不同、認證方式不同、Rate Limit 不同,設定起來比想像中麻煩。設定錯了,告警發不出去;設定太鬆,Webhook URL 洩漏後任何人都能發假告警到你的 channel。
這篇文章整理了 Alertmanager 整合各主流平台 Webhook 的完整模板——Slack、Discord、Line Notify、PagerDuty,以及通用的 HTTP POST Webhook。每個模板都可以直接複製修改使用,不用從零開始摸索每個平台的 API 文件。同時也會談到告警風暴的防護、Webhook 安全性、以及如何監控「通知管道本身是否正常運作」這個容易被忽略的盲點。
架構概覽
flowchart LR Alert["告警來源\nPrometheus / Grafana"] -->|觸發告警| Webhook["Webhook Processor\nAlertmanager"] Webhook -->|route: critical| PD["PagerDuty\n電話通知"] Webhook -->|route: warning| Slack["Slack\n#alerts Channel"] Webhook -->|route: info| Discord["Discord\nWebhook Bot"] Webhook -->|route: all| Line["Line Notify\n團隊群組"] Webhook -->|備援| Generic["Generic HTTP\nPOST Endpoint"]
架構概覽
flowchart TD Prom[Prometheus\nalert rules] -->|fire alert| AM[Alertmanager\n:9093] AM -->|route: critical| R1[Critical Route] AM -->|route: warning| R2[Warning Route] AM -->|route: info| R3[Info Route] AM -->|route: oncall| R4[On-Call Route] R1 -->|webhook| Slack[Slack\n#incidents] R1 -->|webhook| PD[PagerDuty\nphone call] R2 -->|webhook| Discord[Discord\n#alerts] R2 -->|webhook| Line[Line Notify\n團隊群組] R3 -->|webhook| Email[Email Digest] R4 -->|webhook| Custom[Custom Webhook\n內部系統] subgraph Templates["通知模板 (.tmpl)"] T1[severity 顏色] T2[Runbook 連結] T3[告警摘要格式] end AM --> Templates Templates --> Slack Templates --> Discord Templates --> Line
Prometheus 觸發告警後送到 Alertmanager,Alertmanager 根據 label(severity、team、service)路由到不同的 receiver。每個 receiver 對應一個或多個 Webhook endpoint。通知的內容由 .tmpl 模板檔案控制,可以自訂顏色、格式、連結。一條 Critical 告警可以同時送到 Slack 和 PagerDuty,確保不會漏接。
核心概念
-
Webhook 的運作原理:Webhook 本質上就是一個 HTTP POST 請求,帶著 JSON payload 送到指定的 URL。當 Alertmanager 需要發通知時,它會把告警內容序列化成 JSON,POST 到你設定的 Webhook URL。每個平台期望的 JSON 結構不同:Slack 用
{"text": "..."}或 Block Kit 格式、Discord 用{"content": "..."}或 Embed 格式、Line Notify 用 form-urlencoded 而非 JSON。搞清楚每個平台的 payload 格式是整合的第一步。 -
Alertmanager Receiver 類型:Alertmanager 原生支援多種 receiver 類型:
slack_configs(Slack Incoming Webhook)、webhook_configs(通用 HTTP POST,可以對接任何服務)、email_configs(SMTP 寄信)、pagerduty_configs(PagerDuty Events API)、opsgenie_configs(OpsGenie Alert API)、wechat_configs(企業微信)。Slack 和 PagerDuty 有原生支援,Discord 和 Line Notify 則需要透過webhook_configs搭配自訂 payload 或中介服務來處理。 -
Message Templating(Go Template 語法):Alertmanager 使用 Go 的
text/template語法來格式化通知內容。在模板裡可以存取.Status(firing/resolved)、.Alerts(告警列表)、.GroupLabels、.CommonLabels、.CommonAnnotations等欄位。你可以用{{ range .Alerts }}迭代每個告警、用{{ if eq .Status "firing" }}做條件判斷、用{{ .Labels.severity }}存取 label 值。模板寫好後放在.tmpl檔案裡,在alertmanager.yml裡用templates欄位引入。 -
Route Matching(路由匹配):Alertmanager 的路由是樹狀結構,從根節點開始往下匹配。每個 route 可以用
match(精確匹配)或match_re(正則匹配)來過濾告警。常見的路由策略:按 severity 分流(critical 走 PagerDuty + Slack、warning 走 Discord、info 走 email);按 team 分流(backend 告警送到#backend-alerts、frontend 告警送到#frontend-alerts);按 service 分流(database 告警送給 DBA、network 告警送給 NetOps)。路由可以巢狀,先按 severity 分再按 team 分。 -
Silencing 與 Inhibition(避免告警風暴):Silencing 是手動靜音某個告警一段時間(例如維護期間暫停 HostDown 告警)。Inhibition 是自動抑制——如果 A 告警已經觸發,就自動抑制 B 告警。經典案例:Host 掛了(HostDown),上面跑的 10 個服務都會觸發告警,但真正的根因只有一個。設定
inhibit_rules讓 HostDown 抑制同一台 Host 上的所有服務告警,通知就從 10 條變成 1 條。 -
Grouping(告警聚合):
group_by設定讓 Alertmanager 把相同 label 組合的告警合併成一條通知。例如group_by: ['alertname', 'cluster']會把同一個 cluster 上的同類型告警合併。group_wait控制第一次通知前等多久(等待更多同類告警加入)、group_interval控制已有 group 收到新告警後多久再發一次通知。合理的 grouping 設定可以大幅減少通知數量,避免 alert fatigue。
實作模板
以下是各平台 Webhook 整合的完整模板,可以直接複製到你的 alertmanager.yml 裡修改使用。
模板 1:Slack Incoming Webhook
Slack 是 Alertmanager 原生支援的平台,設定最直覺。重點是用 color 欄位根據 severity 顯示不同顏色的側邊條,讓收到通知的人一眼區分嚴重程度。
# alertmanager.yml - Slack receiver
receivers:
- name: 'slack-critical'
slack_configs:
- api_url: '${SLACK_WEBHOOK_URL}'
channel: '#incidents'
send_resolved: true
# severity 對應顏色:critical=紅色, warning=橘色, info=藍色
color: '{{ if eq .Status "firing" }}{{ if eq .CommonLabels.severity "critical" }}#FF0000{{ else if eq .CommonLabels.severity "warning" }}#FFA500{{ else }}#0000FF{{ end }}{{ else }}#00FF00{{ end }}'
title: '{{ if eq .Status "firing" }}[FIRING] {{ .CommonLabels.alertname }}{{ else }}[RESOLVED] {{ .CommonLabels.alertname }}{{ end }}'
title_link: 'https://grafana.example.com/alerting/list'
text: >-
{{ range .Alerts }}
*Severity:* `{{ .Labels.severity }}`
*Instance:* `{{ .Labels.instance }}`
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*Runbook:* {{ .Annotations.runbook_url }}
*Started:* {{ .StartsAt.Format "2006-01-02 15:04:05 MST" }}
{{ if .EndsAt }}*Resolved:* {{ .EndsAt.Format "2006-01-02 15:04:05 MST" }}{{ end }}
---
{{ end }}
fallback: '{{ .CommonLabels.alertname }}: {{ .CommonAnnotations.summary }}'
icon_emoji: ':rotating_light:'
username: 'Alertmanager'模板 2:Discord Webhook
Discord 的 Webhook 格式和 Slack 完全不同,Alertmanager 沒有原生支援 Discord,需要用 webhook_configs。Discord 接受的 JSON 格式是 {"content": "...", "embeds": [...]},但 Alertmanager 預設送出的 JSON 格式是它自己的結構,Discord 無法直接解析。有兩種做法:用一個中介服務(如 alertmanager-discord)轉換格式,或利用 Discord 的 /slack 相容 endpoint。
# 方法一(推薦):使用 Discord 的 Slack-compatible endpoint
# Discord Webhook URL 後面加上 /slack 即可接受 Slack 格式
receivers:
- name: 'discord-alerts'
slack_configs:
- api_url: 'https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_WEBHOOK_TOKEN/slack'
send_resolved: true
title: '{{ if eq .Status "firing" }}[FIRING]{{ else }}[RESOLVED]{{ end }} {{ .CommonLabels.alertname }}'
text: >-
{{ range .Alerts }}
**Severity:** {{ .Labels.severity }}
**Instance:** {{ .Labels.instance }}
**Summary:** {{ .Annotations.summary }}
{{ end }}
# 方法二:使用 alertmanager-discord 中介服務
# 先部署 alertmanager-discord(一個輕量的 Go 程式)
# docker run -d -p 9094:9094 -e DISCORD_WEBHOOK=https://discord.com/api/webhooks/... benjojo/alertmanager-discord
receivers:
- name: 'discord-via-bridge'
webhook_configs:
- url: 'http://alertmanager-discord:9094'
send_resolved: true模板 3:Line Notify
Line Notify 是台灣和日本團隊常用的通知管道。Line Notify 的 API 不接受 JSON,而是用 application/x-www-form-urlencoded 格式,而且需要 Bearer Token 認證。Alertmanager 原生的 webhook_configs 無法直接對接 Line Notify,需要一個中介服務來轉換格式。
# 需要部署一個 webhook bridge 服務,將 Alertmanager JSON 轉換為 Line Notify 格式
# 以下是 bridge 服務的 docker-compose 設定
# docker-compose.line-bridge.yml
version: "3.8"
services:
line-notify-bridge:
image: node:20-alpine
restart: unless-stopped
ports:
- "127.0.0.1:9095:9095"
environment:
LINE_NOTIFY_TOKEN: '${LINE_NOTIFY_TOKEN}'
PORT: 9095
volumes:
- ./line-bridge.js:/app/index.js
command: node /app/index.js
# line-bridge.js(簡易版,接收 Alertmanager JSON 並轉發到 Line Notify)
# const http = require('http');
# const https = require('https');
# const querystring = require('querystring');
#
# const LINE_TOKEN = process.env.LINE_NOTIFY_TOKEN;
# const PORT = process.env.PORT || 9095;
#
# http.createServer((req, res) => {
# if (req.method === 'POST') {
# let body = '';
# req.on('data', chunk => body += chunk);
# req.on('end', () => {
# const alert = JSON.parse(body);
# const status = alert.status === 'firing' ? '[FIRING]' : '[RESOLVED]';
# let message = `\n${status} Alertmanager\n`;
# alert.alerts.forEach(a => {
# message += `\nAlert: ${a.labels.alertname}`;
# message += `\nSeverity: ${a.labels.severity}`;
# message += `\nInstance: ${a.labels.instance}`;
# message += `\nSummary: ${a.annotations.summary}\n`;
# });
# const postData = querystring.stringify({ message });
# const options = {
# hostname: 'notify-api.line.me',
# path: '/api/notify',
# method: 'POST',
# headers: {
# 'Content-Type': 'application/x-www-form-urlencoded',
# 'Authorization': `Bearer ${LINE_TOKEN}`,
# },
# };
# const lineReq = https.request(options, lineRes => {
# res.writeHead(lineRes.statusCode);
# res.end('OK');
# });
# lineReq.write(postData);
# lineReq.end();
# });
# }
# }).listen(PORT, () => console.log(`Line bridge listening on ${PORT}`));
# Alertmanager 設定
receivers:
- name: 'line-notify'
webhook_configs:
- url: 'http://line-notify-bridge:9095'
send_resolved: true模板 4:PagerDuty 整合(On-Call 輪值)
PagerDuty 是 on-call 管理的業界標準,適合需要 24/7 值班的團隊。Alertmanager 原生支援 PagerDuty,設定簡單。PagerDuty 會根據 Escalation Policy 決定通知誰——如果 on-call 的人 5 分鐘沒回應,自動升級到下一位。
# alertmanager.yml - PagerDuty receiver
receivers:
- name: 'pagerduty-critical'
pagerduty_configs:
- service_key: '${PAGERDUTY_SERVICE_KEY}'
# 或使用 routing_key(Events API v2)
# routing_key: '${PAGERDUTY_ROUTING_KEY}'
severity: '{{ .CommonLabels.severity }}'
# PagerDuty 的 description 欄位
description: '{{ .CommonAnnotations.summary }}'
# 自訂欄位,會顯示在 PagerDuty incident 頁面
details:
alertname: '{{ .CommonLabels.alertname }}'
instance: '{{ .CommonLabels.instance }}'
severity: '{{ .CommonLabels.severity }}'
summary: '{{ .CommonAnnotations.summary }}'
runbook: '{{ .CommonAnnotations.runbook_url }}'
grafana: 'https://grafana.example.com/d/overview'
# 告警恢復時自動 resolve PagerDuty incident
send_resolved: true
# 搭配 Slack 雙管道通知:Critical 同時送 PagerDuty 和 Slack
- name: 'critical-multi-channel'
pagerduty_configs:
- service_key: '${PAGERDUTY_SERVICE_KEY}'
send_resolved: true
slack_configs:
- api_url: '${SLACK_WEBHOOK_URL}'
channel: '#incidents'
send_resolved: true
title: '[CRITICAL] {{ .CommonLabels.alertname }}'
text: '{{ .CommonAnnotations.summary }}'模板 5:通用 Custom Webhook(HTTP POST 到任何服務)
如果你有自建的告警處理系統、內部工單系統、或任何可以接收 HTTP POST 的服務,都可以用 webhook_configs 來對接。Alertmanager 會把完整的告警 JSON 送過去,你的服務負責解析和處理。
# alertmanager.yml - Generic webhook receiver
receivers:
- name: 'custom-webhook'
webhook_configs:
- url: 'https://internal-api.example.com/alerts/incoming'
send_resolved: true
# HTTP 基本認證
http_config:
basic_auth:
username: 'alertmanager'
password: '${WEBHOOK_PASSWORD}'
# 或使用 Bearer Token
# authorization:
# type: Bearer
# credentials: '${WEBHOOK_TOKEN}'
# TLS 設定(如果用自簽憑證)
# tls_config:
# ca_file: /etc/alertmanager/certs/ca.pem
# insecure_skip_verify: false
# 最大重試次數(預設 Alertmanager 會持續重試)
# 可以用 max_alerts 限制一次通知包含的告警數
max_alerts: 10
# 另一個範例:送到內部工單系統自動開 ticket
- name: 'ticket-system'
webhook_configs:
- url: 'https://tickets.example.com/api/v1/alerts'
send_resolved: true
http_config:
authorization:
type: Bearer
credentials: '${TICKET_API_TOKEN}'模板 6:完整 alertmanager.yml(多管道路由)
以下是一個完整的 alertmanager.yml,整合了上面所有的模板,展示如何用路由規則將不同告警導向不同的通知管道。
# alertmanager.yml - 完整版多管道路由設定
global:
resolve_timeout: 5m
slack_api_url: '${SLACK_WEBHOOK_URL}'
# 引入自訂通知模板
templates:
- '/etc/alertmanager/templates/*.tmpl'
# 路由規則(樹狀結構,從上往下匹配)
route:
receiver: 'default-slack'
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
# Critical 告警:立即通知,PagerDuty + Slack + Line
- match:
severity: critical
receiver: 'critical-all-channels'
group_wait: 10s
repeat_interval: 1h
continue: false
# Warning 告警:按 team 分流
- match:
severity: warning
group_wait: 3m
repeat_interval: 4h
routes:
# Backend team 的 warning 送到 Discord
- match:
team: backend
receiver: 'discord-backend'
# Frontend team 的 warning 送到 Slack
- match:
team: frontend
receiver: 'slack-frontend'
# DBA 的 warning 送到專屬 channel
- match:
team: dba
receiver: 'slack-dba'
# 未匹配的 warning 走預設
- match_re:
team: '.*'
receiver: 'default-slack'
# Info 告警:每小時彙整,只寄 email
- match:
severity: info
receiver: 'info-email'
group_wait: 1h
repeat_interval: 24h
# 抑制規則
inhibit_rules:
# Host 掛了就抑制該 Host 上所有服務的告警
- source_match:
alertname: 'HostDown'
target_match_re:
alertname: '.+'
equal: ['instance']
# Cluster 掛了就抑制該 Cluster 的所有告警
- source_match:
alertname: 'ClusterUnreachable'
target_match_re:
alertname: '.+'
equal: ['cluster']
# Critical 告警存在時抑制同 alertname 的 Warning
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'instance']
# Receivers 定義
receivers:
- name: 'default-slack'
slack_configs:
- channel: '#alerts'
send_resolved: true
title: '{{ template "slack.title" . }}'
text: '{{ template "slack.text" . }}'
color: '{{ template "slack.color" . }}'
- name: 'critical-all-channels'
pagerduty_configs:
- service_key: '${PAGERDUTY_SERVICE_KEY}'
send_resolved: true
slack_configs:
- channel: '#incidents'
send_resolved: true
title: '{{ template "slack.title" . }}'
text: '{{ template "slack.text" . }}'
color: '{{ template "slack.color" . }}'
webhook_configs:
# Line Notify(透過 bridge 服務)
- url: 'http://line-notify-bridge:9095'
send_resolved: true
- name: 'discord-backend'
slack_configs:
- api_url: 'https://discord.com/api/webhooks/${DISCORD_BACKEND_WEBHOOK}/slack'
send_resolved: true
title: '{{ template "slack.title" . }}'
text: '{{ template "slack.text" . }}'
- name: 'slack-frontend'
slack_configs:
- channel: '#frontend-alerts'
send_resolved: true
title: '{{ template "slack.title" . }}'
text: '{{ template "slack.text" . }}'
color: '{{ template "slack.color" . }}'
- name: 'slack-dba'
slack_configs:
- channel: '#dba-alerts'
send_resolved: true
title: '{{ template "slack.title" . }}'
text: '{{ template "slack.text" . }}'
color: '{{ template "slack.color" . }}'
- name: 'info-email'
email_configs:
- to: 'team@example.com'
from: 'alertmanager@example.com'
smarthost: 'smtp.example.com:587'
auth_username: '${SMTP_USERNAME}'
auth_password: '${SMTP_PASSWORD}'
send_resolved: false
headers:
Subject: '[INFO] Alertmanager Daily Digest'Alertmanager 通知模板檔案(.tmpl)
以下是自訂的通知模板檔案,放在 /etc/alertmanager/templates/ 目錄下,讓所有 receiver 共用一致的格式。
{{/* /etc/alertmanager/templates/custom.tmpl */}}
{{/* Slack 標題模板 */}}
{{ define "slack.title" -}}
{{ if eq .Status "firing" -}}
[FIRING:{{ .Alerts.Firing | len }}] {{ .CommonLabels.alertname }}
{{- else -}}
[RESOLVED:{{ .Alerts.Resolved | len }}] {{ .CommonLabels.alertname }}
{{- end }}
{{- end }}
{{/* Slack 內文模板 */}}
{{ define "slack.text" -}}
{{ if eq .Status "firing" -}}
*Cluster:* {{ .CommonLabels.cluster | default "N/A" }}
*Namespace:* {{ .CommonLabels.namespace | default "N/A" }}
{{ end -}}
{{ range .Alerts -}}
{{ if eq .Status "firing" }}:red_circle:{{ else }}:large_green_circle:{{ end }} *{{ .Labels.alertname }}*
*Severity:* `{{ .Labels.severity }}`
*Instance:* `{{ .Labels.instance | default "N/A" }}`
*Summary:* {{ .Annotations.summary }}
{{ if .Annotations.description }}*Description:* {{ .Annotations.description }}{{ end }}
{{ if .Annotations.runbook_url }}*Runbook:* <{{ .Annotations.runbook_url }}|View Runbook>{{ end }}
*Since:* {{ .StartsAt.Format "2006-01-02 15:04:05 MST" }}
{{ if eq .Status "resolved" }}*Resolved:* {{ .EndsAt.Format "2006-01-02 15:04:05 MST" }}{{ end }}
---
{{ end -}}
*Dashboard:* <https://grafana.example.com/d/overview|Grafana Overview>
{{- end }}
{{/* Slack 顏色模板:根據 severity 和狀態決定側邊條顏色 */}}
{{ define "slack.color" -}}
{{ if eq .Status "resolved" -}}
#2EB67D
{{- else if eq .CommonLabels.severity "critical" -}}
#E01E5A
{{- else if eq .CommonLabels.severity "warning" -}}
#ECB22E
{{- else -}}
#36C5F0
{{- end }}
{{- end }}
{{/* Email 主旨模板 */}}
{{ define "email.subject" -}}
[{{ .Status | toUpper }}] {{ .CommonLabels.alertname }} ({{ .Alerts | len }} alerts)
{{- end }}常見問題與風險
-
Webhook URL 洩漏(任何人都能發通知):Webhook URL 本身就是認證——任何拿到 URL 的人都能發訊息到你的 channel。如果 URL 不小心 commit 到 Git repo 或貼在公開文件裡,攻擊者可以發送假告警製造混亂、或發送釣魚訊息。避免方式:Webhook URL 一律存在環境變數或 Secrets 管理系統 裡,
alertmanager.yml用${ENV_VAR}引用。定期輪換 Webhook URL(大多數平台支援重新產生 URL)。設定 channel 權限限制誰可以建立 Webhook。 -
Rate Limiting(平台限制太頻繁的呼叫):各平台都有 rate limit。Slack 的 Incoming Webhook 限制每秒 1 則訊息;Discord 限制每分鐘 30 則訊息(全域每秒 50 則);Line Notify 限制每小時 1000 則。超過限制的訊息會被丟棄,你的 Critical 告警可能因為 rate limit 而沒送出去。避免方式:善用 Alertmanager 的
group_by、group_wait、group_interval把多個告警合併成一條通知。repeat_interval不要設太短(Critical 至少 1 小時、Warning 至少 4 小時)。 -
告警風暴(一個根因觸發大量告警):一台 Host 掛了,上面跑的 20 個容器各觸發一個告警,再加上 node_exporter 的各種指標告警,一次可能產生 50+ 告警。如果沒有 grouping 和 inhibition,通知管道會被灌爆、團隊手機響個不停、真正重要的資訊反而被淹沒。避免方式:設定
inhibit_rules(HostDown 抑制該 Host 上所有告警)、group_by聚合同類告警、group_wait等待聚合窗口。考慮設定max_alerts限制單一通知包含的告警數量。 -
通知管道掛了但沒人知道(Alertmanager 自身的監控):Alertmanager 發送通知失敗時,它會記錄到自己的 log 和 metrics 裡,但如果沒有人監控 Alertmanager 本身,通知管道可能已經掛了幾天都沒人發現。這是「誰來監控監控系統」的經典問題。避免方式:用 Prometheus 監控 Alertmanager 的
alertmanager_notifications_failed_total指標,如果通知失敗次數增加就用另一個管道告警(例如 Alertmanager 送 Slack 失敗時改用 email 通知)。設定 Dead Man’s Switch——一個永遠處於 firing 狀態的告警(Watchdog),如果某段時間沒收到這個告警的通知,就代表通知管道斷了。 -
模板語法錯誤導致通知失敗:Go template 語法錯誤不會在 Alertmanager 啟動時被完整檢查,有時候要等到實際發送通知時才會報錯。模板裡引用了不存在的 label 或 annotation 欄位,會導致整批通知發送失敗。避免方式:模板裡使用
{{ .Labels.severity | default "unknown" }}加上 default 值避免空值錯誤。用amtool工具在部署前驗證設定檔和模板語法。在 staging 環境先測試告警通知流程。 -
時區與語系問題:Alertmanager 預設使用 UTC 時間,通知裡顯示的時間可能讓亞洲時區的團隊困惑。模板裡的時間格式化要特別注意。避免方式:在模板裡明確指定時區,或在 Alertmanager 的環境變數裡設定
TZ=Asia/Taipei。
驗證與測試
部署完成後,可以用以下方法測試通知管道是否正常運作:
# 用 amtool 發送測試告警
amtool alert add test-alert \
severity=critical \
instance=test-host:9090 \
--annotation.summary="This is a test alert" \
--annotation.runbook_url="https://wiki.example.com/runbook/test" \
--alertmanager.url=http://localhost:9093
# 確認告警已被 Alertmanager 接收
amtool alert query --alertmanager.url=http://localhost:9093
# 用 curl 直接測試 Alertmanager API
curl -X POST http://localhost:9093/api/v2/alerts \
-H 'Content-Type: application/json' \
-d '[
{
"labels": {
"alertname": "TestAlert",
"severity": "warning",
"instance": "localhost:9090",
"team": "backend"
},
"annotations": {
"summary": "This is a test alert from curl",
"runbook_url": "https://wiki.example.com/runbook/test"
},
"startsAt": "2024-09-15T10:00:00Z",
"generatorURL": "http://prometheus:9090/graph"
}
]'
# 驗證 Alertmanager 設定檔語法
amtool check-config alertmanager.yml
# 測試路由匹配(確認告警會被路由到正確的 receiver)
amtool config routes test \
--config.file=alertmanager.yml \
--tree \
severity=critical team=backend小結
Webhook 整合不只是「把 URL 貼上去」這麼簡單。每個平台的格式、認證、限制都不同,需要針對性的設定。更重要的是告警路由的設計——讓 Critical 告警能立即到達 on-call 的人手上,Warning 告警在合理的時間內被團隊看到,Info 告警不要干擾日常工作。善用 grouping、inhibition、silencing 來控制通知量,避免告警風暴把團隊淹沒。最後不要忘了監控通知管道本身——Dead Man’s Switch 是最有效的保險機制。