cover

Alerts & ChatOps:告警不是越多越好

有了 Metrics 和 Logs 之後,下一步是設定告警:什麼情況下要通知誰、用什麼管道通知、以及收到告警後要做什麼。告警設計得好,可以在問題影響使用者之前就介入;設計得差,團隊會被無止境的通知淹沒,最後開始忽略所有告警(alert fatigue),反而比沒有告警更危險。

架構概覽

flowchart TD
  Metric[指標超過閾值\nCPU / Disk / Error Rate] -->|觸發| AM[Alertmanager\n告警路由與聚合]
  AM -->|Critical| Urgent[即時通知\n電話 / PagerDuty]
  AM -->|Warning| Batch[批次彙整\nSlack / Discord]
  AM -->|Info| Digest[定期摘要\nEmail]
  Urgent --> Runbook[Runbook\n處理手冊]
  Batch --> Runbook
  Runbook --> Resolve[問題解決\n回報 & 覆盤]

架構概覽

flowchart TD
  Prometheus[Prometheus\nalert rules] -->|trigger| AM[Alertmanager\n:9093]

  AM -->|route by severity| Critical[Critical Route]
  AM -->|route by severity| Warning[Warning Route]
  AM -->|route by severity| Info[Info Route]

  Critical -->|immediately| Phone[Phone Call / PagerDuty]
  Critical -->|immediately| Slack[Slack #incidents]
  Warning -->|batch 5min| Slack2[Slack #alerts]
  Warning -->|batch 5min| Discord[Discord #alerts]
  Info -->|batch 1hr| Email[Email Digest]

  Slack --> Runbook[Runbook\n處理流程]
  Slack --> Acknowledge[Acknowledge\n確認接手]

Prometheus 觸發告警後送到 Alertmanager,Alertmanager 根據 severity 路由到不同的通知管道。Critical 立即通知(電話 + Slack),Warning 批次彙整後通知(5 分鐘一批),Info 每小時彙整一次用 email 送。每個告警都附帶 Runbook 連結。

核心概念

  1. 告警分級(Severity Levels):分三級。Critical(P1):服務完全不可用、資料遺失風險、安全事件。需要有人立即處理,不管是不是上班時間。例如:PostgreSQL 掛了、磁碟空間 < 5%、TLS 憑證過期。Warning(P2):服務降級、效能異常、資源即將耗盡。上班時間處理,不需要半夜叫人。例如:CPU > 85% 超過 5 分鐘、API error rate > 5%、磁碟空間 < 20%。Info(P3):需要關注但不緊急的事項。下次巡檢時處理。例如:憑證 30 天內到期、某個 exporter scrape 失敗。

  2. 告警抑制與聚合:如果 Host 掛了,上面跑的 10 個服務都會告警,一次收到 10 封通知反而造成混亂。Alertmanager 的 group_by 功能可以把同一台 Host 的告警聚合成一條通知。inhibit_rules 可以設定「如果已經有 HostDown 告警,就抑制該 Host 上所有服務的告警」,避免通知風暴。

  3. Runbook:每個告警都應該附帶一個 Runbook(處理手冊),告訴收到告警的人該怎麼做。Runbook 不需要很長,但至少要包含:(1)這個告警代表什麼、(2)可能的原因、(3)處理步驟、(4)升級路徑(如果處理不了要找誰)。沒有 Runbook 的告警等於「我知道出事了,但不知道該怎麼辦」。

  4. ChatOps 整合:把告警通知整合到團隊日常使用的通訊工具(Slack/Discord),而不是只發 email。好處是告警在團隊可見的 channel 裡,所有人都知道現在有什麼問題。可以在 channel 裡直接討論和協作,事後也有完整的對話紀錄。建議用專門的 #alerts channel,不要混在一般聊天 channel 裡。

使用情境

  • 半夜服務異常:凌晨 2 點 PostgreSQL 掛了。Alertmanager 發送 Critical 告警到 on-call 工程師的手機(PagerDuty/phone call)和 #incidents channel。工程師收到通知後,打開 Runbook 按步驟操作:檢查 PostgreSQL 日誌、重啟容器、驗證服務恢復。在 #incidents channel 回報處理過程和結果。

  • 資源使用率升高:下午磁碟使用率超過 80%,觸發 Warning 告警。Alertmanager 等 5 分鐘彙整後,發到 #alerts channel。值班的維運人員在上班時間看到後,清理舊的 Docker images 和過期的備份檔案,回報處理完成。

  • 告警回顧:每月回顧上個月的所有告警,統計各類型告警的數量和解決時間。發現某個 Warning 告警每天都觸發但每次都自動恢復(false alarm),調整閾值或移除該告警規則。發現某個 Critical 告警的解決時間太長,補充 Runbook 或改善自動化。

實作範例 / 設定範例

Alertmanager 設定

# alertmanager.yml
global:
  resolve_timeout: 5m
 
route:
  receiver: 'default'
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
 
  routes:
    # Critical: 立即通知
    - match:
        severity: critical
      receiver: 'critical-channel'
      group_wait: 10s
      repeat_interval: 1h
 
    # Warning: 5 分鐘彙整
    - match:
        severity: warning
      receiver: 'warning-channel'
      group_wait: 5m
      repeat_interval: 4h
 
    # Info: 1 小時彙整
    - match:
        severity: info
      receiver: 'info-email'
      group_wait: 1h
      repeat_interval: 24h
 
# 抑制規則:Host down 時,抑制該 Host 上的服務告警
inhibit_rules:
  - source_match:
      alertname: 'HostDown'
    target_match_re:
      alertname: '.+'
    equal: ['instance']
 
receivers:
  - name: 'default'
    slack_configs:
      - api_url: '${SLACK_WEBHOOK_URL}'
        channel: '#alerts'
        send_resolved: true
 
  - name: 'critical-channel'
    slack_configs:
      - api_url: '${SLACK_WEBHOOK_URL}'
        channel: '#incidents'
        send_resolved: true
        title: '🚨 CRITICAL: {{ .GroupLabels.alertname }}'
        text: >-
          {{ range .Alerts }}
          *Alert:* {{ .Labels.alertname }}
          *Instance:* {{ .Labels.instance }}
          *Summary:* {{ .Annotations.summary }}
          *Runbook:* {{ .Annotations.runbook_url }}
          {{ end }}
    # webhook_configs:
    #   - url: 'https://pagerduty-webhook-url'
 
  - name: 'warning-channel'
    slack_configs:
      - api_url: '${SLACK_WEBHOOK_URL}'
        channel: '#alerts'
        send_resolved: true
 
  - name: 'info-email'
    email_configs:
      - to: 'team@example.com'
        send_resolved: false

Discord Webhook 整合(替代 Slack)

# 如果團隊用 Discord,用 webhook_configs
receivers:
  - name: 'discord-alerts'
    webhook_configs:
      - url: '${DISCORD_WEBHOOK_URL}'
        send_resolved: true
        http_config:
          headers:
            Content-Type: application/json

Runbook 範本

## Runbook: DiskSpaceLow
 
### 告警條件
磁碟可用空間低於 20%
 
### 可能原因
1. Docker images 佔用過多空間
2. 日誌檔案未清理
3. 備份檔案未上傳到 MinIO 後刪除本地副本
4. 資料庫 WAL 檔案過大
 
### 處理步驟
1. 確認磁碟使用狀況
   ```bash
   df -h
   du -sh /var/lib/docker/*
   du -sh /var/log/*
  1. 清理 Docker 未使用資源
    docker system prune -af --volumes
  2. 清理過期備份
    find /tmp/pg-backups -mtime +7 -delete
  3. 檢查日誌 rotation 是否正常運作

升級路徑

如果清理後磁碟空間仍不足,聯繫 Infra Lead 評估擴容。


### 常見問題與風險

- **告警疲勞(Alert Fatigue)**:收到太多不重要的告警,團隊開始忽略所有通知,包括 Critical 的。避免方式:嚴格控制告警數量,每個告警都要有明確的 action(如果收到告警但不需要做任何事,就不應該是告警)。定期回顧和清理不實用的告警規則。

- **通知管道斷了**:Slack Webhook URL 過期或 Discord channel 被刪除,告警發不出去但沒有人知道。避免方式:設定 Alertmanager 自身的健康檢查、用多個通知管道(Slack + email)做備援。定期確認通知管道正常運作。

- **告警沒有 Runbook**:收到告警但不知道怎麼處理,只能等某個「知道的人」上線。避免方式:每建立一個告警規則就同時寫 Runbook,在告警的 annotations 裡加上 `runbook_url`。新告警沒有 Runbook 不允許上線。

- **on-call 機制不清楚**:半夜告警發到 channel 裡,但沒有指定誰要處理。每個人都覺得「應該別人會處理」。避免方式:建立明確的 on-call 排班,每週輪值,on-call 期間要確保手機通知開啟。搭配 PagerDuty 或 OpsGenie 做升級(如果 on-call 沒回應,自動通知下一位)。

### 優點

- 告警分級確保不同嚴重程度用不同管道和速度通知
- 聚合和抑制減少通知風暴
- ChatOps 整合讓告警處理過程對團隊透明

### 缺點 / 限制

- Alertmanager 設定語法不太直觀,容易設定錯誤
- 小團隊可能沒有足夠的人做 on-call 輪值
- Runbook 需要持續維護,否則很快過時

---

## 延伸閱讀

- [[11-metrics-monitoring|Metrics & Monitoring]]
- [[12-log-management|Log Management(EFK/ELK)]]
- [[14-data-movement-etl|Data Movement / ETL]]