前面六章都在處理資料。這一章處理的是人——而人是整套可觀測性系統裡最貴、最容易耗損、也最難補充的那個部件。
告警的失敗模式有兩種,而它們互為因果:
太多:一個月 400 條告警,其中 380 條會自己恢復。於是大家開始忽略它們。太少:真正的故障沒有人被通知,因為它混在那 380 條裡沒人看見。
**第二種是第一種造成的。**告警疲勞不是「大家不夠認真」,它是理性反應—— 當一個訊號的偽陽性率高到某個程度,忽略它就是最有效率的策略。所以告警設計的本質不是「怎麼不漏掉問題」,是「怎麼保住人對這個系統的信任」。
這一章要建立的判準只有一條,其餘全部從它推導:
for 與抖動:一條沒有持續時間條件的告警規則,幾乎必定會誤報。這一章與 ch06 是連著的:SLO 提供了「什麼時候該在乎」的客觀依據,而 burn rate 告警把它變成一條可以直接掛上去的規則。沒有 SLO 的告警,門檻永遠是拍腦袋訂的。
告警設計有一堆技巧,但它們全部從同一個問題推導出來:
| 層級 | 判準 | 送到哪 |
|---|---|---|
| Page(叫人) | 使用者正在受影響,而且需要人介入才會好 | 電話/推播,會吵醒人 |
| Ticket(工單) | 需要處理,但可以等上班時間 | 工單系統,隔天看 |
| Dashboard(儀表板) | 排查時有用,但不需要主動通知 | 圖表,需要時才看 |
大部分團隊的問題是把後兩類做成了第一類。「磁碟使用率 80%」——它會在三週後才滿,這是 ticket;「單一 pod 重啟了」——K8s 會自己處理,這是 dashboard。
annotations:
summary: "付款 SLO 快速燃燒(2 天內將耗盡預算)"
description: "目前錯誤率 {{ $value | humanizePercentage }},正常應低於 0.1%"
runbook_url: "https://wiki/runbooks/payment-slo-burn"
dashboard_url: "https://grafana/d/payment-overview"這是 Google SRE 最常被引用的一條原則,而它的理由非常實際。
❌ 原因告警:CPU > 80%、記憶體 > 90%、單一 pod 重啟、佇列長度 > 1000
✅ 徵狀告警:付款成功率低於 SLO、SLO 錯誤預算正在快速燃燒、關鍵旅程的黑箱探針失敗要管,但放對地方:
下面這條規則看起來很合理,但它幾乎必定會誤報:
- alert: HighErrorRate
expr: rate(errors[5m]) > 0.01 # ← 沒有 for
只要有任何一個評估週期超標(可能只是一次部署造成的瞬間 5xx),它就會響,然後在下一個週期自己恢復。
- alert: HighErrorRate
expr: rate(errors[5m]) > 0.01
for: 10m # 連續超標 10 分鐘才觸發
for 才變成 firing。for: 10m 意味著真的出事時,你也晚 10 分鐘才知道。for 的長度是「靈敏度 vs 雜訊」的直接取捨,而它應該由嚴重性決定:災難級(burn rate 14.4×) → for: 2m (寧可偶爾誤報)
嚴重(burn rate 6×) → for: 15m
一般(burn rate 3×) → for: 1h (做成 ticket)這正好對應 ch06 那張多視窗表——burn rate 的設計已經內建了這個取捨,因為長視窗負責「確定嚴重」,短視窗負責「確定還在持續」。| 參數 | 意思 | 設錯的症狀 |
|---|---|---|
| rate([5m]) 的範圍 | 算速率時看多久的資料 | 太短→空洞與抖動(ch03) |
| for | 條件要持續多久才觸發 | 沒設→部署時的瞬間尖峰全都響 |
| group_interval / repeat_interval | 多久重送一次通知 | 太短→同一問題被反覆騷擾 |
指標剛好在門檻附近徘徊時,告警會反覆 firing / resolved。除了 for,還有兩個做法:
max_over_time 之類的平滑(ch03),讓短暫回落不會立刻解除告警。就算每一條告警規則都設計良好,一次真實故障仍然可能產生幾十則通知——因為它同時影響了很多實例、很多服務。Alertmanager 這一層的工作就是把它們收斂成人類可以處理的形式。
route:
group_by: ['alertname', 'service'] # 同一個服務的同一種告警合成一則
group_wait: 30s # 第一則到達後,等 30 秒收集同組的其他告警
group_interval: 5m # 同組有新成員時,最快 5 分鐘再通知一次
repeat_interval: 4h # 問題仍未解決時,多久提醒一次group_wait 是最有價值的一個:30 台機器同時出問題時,它會等 30 秒把它們收成一則「30 個實例受影響」,而不是發 30 則通知。
inhibit_rules:
- source_matchers: [ severity="critical", alertname="ClusterDown" ]
target_matchers: [ severity="warning" ]
equal: ['cluster'] # 同一個叢集裡的 warning 全部壓住已經在處理、或正在做計畫性維護時,手動靜音一段時間。但靜音有兩個要注意的地方:
- alert: Watchdog
expr: vector(1) # 永遠為真
labels: { severity: none }
annotations: { summary: "這條告警應該永遠在響" }這是 ch01「看不到沒發生的事」與 ch03 absent() 的同一個思路,用在監控系統自己身上。它是整套告警體系裡最便宜、也最常被遺漏的一條。某次事故的檢討會上,時間軸長這樣:
02:14 支付服務開始大量失敗
02:14 告警觸發,送進值班頻道
02:54 客服接到第 6 通客訴,打電話給值班工程師 ← 40 分鐘後
02:58 開始處理
03:20 恢復把過去一個月的告警拉出來統計(這件事本身就該定期做):
| 指標 | 數字 |
|---|---|
| 一個月的告警總數 | 412 則 |
| 其中自動恢復、無需處理 | 389 則(94%) |
| 導致實際處理動作的 | 23 則 |
| 最吵的一條規則 | 單一 pod 記憶體 > 85%(161 則) |
for:瞬間尖峰立刻觸發,下一個週期就恢復。那 161 則記憶體告警全部是這樣來的(而 JVM 的記憶體本來就會在 GC 前接近上限,那是正常行為,回扣 backend ch04)。for、分組、抑制:一次故障收斂成一則通知。改造前:412 則/月,可行動 5.6%
改造後: 31 則/月,可行動 74%告警的接收端是人,而人的處理能力是有明確上限的。把這件事量化出來,告警治理才有客觀依據。
如果某條告警的處理動作每次都一樣,那它就是 toil 的訊號:
「pod 記憶體高 → 重啟它」 → 設好 liveness probe 與資源限制,讓 K8s 自己做
「磁碟滿 → 清理舊日誌」 → 設定保留策略(ch04)
「流量高 → 手動擴容」 → HPA 自動擴縮
「某個依賴慢 → 手動關掉功能」 → 熔斷與降級(見 /cool/resilience/)□ 使用者受到影響了嗎?(否 → 不該 page)
□ 有具體的處理動作嗎?(否 → 不該 page)
□ 需要現在做嗎?(否 → ticket)
□ 這個動作能自動化嗎?(能 → 自動化,不要叫人)
□ 有 for 或等效的持續條件嗎?
□ 有 runbook 嗎?
□ 這個問題會不會同時觸發其他告警?(會 → 設抑制規則)for、降級成 ticket、還是直接刪除?最後一章回到 Java 這邊:把前面七章的東西真正接到一個 Spring 專案上,並且算一次完整的帳——因為可觀測性系統很容易變成整個架構裡最貴的一套系統。
| 層級 | 判準 | 例子 | 常見錯誤 |
|---|---|---|---|
| Page(叫人) | 使用者正在受影響,且需要人介入 | SLO burn rate 14.4×、關鍵旅程探針失敗 | 把資源指標放進來(CPU、記憶體) |
| Ticket(工單) | 需要處理,但可以等上班時間 | 磁碟三週後會滿、憑證 30 天後到期、burn rate 3× | 做成 page,半夜叫醒人卻無事可做 |
| Dashboard | 排查時有用,不需要主動通知 | CPU、GC、連線池、佇列長度 | 每個指標都配一條告警 |
| 自動化 | 處理動作每次都一樣 | 重啟、擴容、清理、熔斷 | 叫人來手動執行固定動作(toil) |
| 機制 | 解決什麼 | 關鍵設定 | 注意 |
|---|---|---|---|
| 分組 grouping | 30 台機器同時出事 → 一則通知 | group_by、group_wait: 30s | group_wait 是最有價值的一個 |
| 抑制 inhibition | 叢集掛了 → 不要再送 200 則衍生告警 | source/target matchers + equal | 設太激進會壓掉真正獨立的問題 |
| 靜音 silence | 處理中或計畫維護時不要再吵 | 一定要設到期時間 | 靜音超過一個月的通常該修或該刪 |
| dead man's switch | 監控系統自己壞掉時誰通知你 | 永遠為真的 Watchdog + 外部檢查 | 最便宜也最常被遺漏的一條 |
| 指標 | 健康值 | 異常代表 | 怎麼修 |
|---|---|---|---|
| 可行動比例 | > 70% | 低於 50% 就會產生告警疲勞 | 先做減法:刪掉或降級不可行動的 |
| 每次值班中斷次數 | 12 小時班次 ≤ 2 次 | 超過就會影響處理品質 | 修根因或承認目標與投入不匹配 |
| 深夜中斷次數 | 越低越好 | 成本遠高於白天——影響隔天一整天 | 把可以等的降級成 ticket |
| 事故由誰先發現 | 告警先於使用者 | 使用者先發現=有漏報 | 檢查徵狀告警是否涵蓋該旅程 |
點擊卡片翻面查看答案,共 13 張。