On-Call 與告警設計#

On-Call(值班)是把 SLO 承諾落實到 24 小時防線的人力安排,而告警(Alerting)則是連接「系統異常」與「人介入」的橋樑。設計不當的值班會燒乾團隊、設計不當的告警會淹沒值班者。本章談如何用公式推算合理的值班人力、如何守護值班者的心理安全、以及如何讓每一次 page 都值得被吵醒。告警背後的監控訊號採集見 09 可觀測性,事故後的檢討見 15 無究責事後檢討

值班的反應時間 SLO#

值班的第一要務,是約定好「多快要有人開始處理」。這個反應時間目標應由服務的 SLO 與錯誤預算 反推:

  • 錯誤預算緊(高可用服務) ➡️ 要求短反應時間(如 5 分鐘內),值班者必須隨時可立即上線。
  • 錯誤預算寬(容忍度高的服務) ➡️ 可接受較長反應時間(如 30 分鐘),值班壓力較小。

反應時間目標直接決定了值班的形態:要求 5 分鐘響應,就意味著值班者隨身帶裝置、不能離線;若可接受 30 分鐘,則生活彈性大得多。

值班輪值模型#

模型說明適用情境
主/副(Primary/Secondary)主值班先處理;主值班漏接時副值班頂上單一時區團隊的標準配置
Follow-the-sun多時區團隊接力,各自只在白天值班跨國團隊,避免夜間值班
隨選(On-demand)平時無人專職,事故時才召集低風險、低流量服務

Follow-the-sun 是消除夜間值班最有效的辦法:把全球團隊按時區排成接力,讓每個人都只在自己的白天值班。它的代價是需要跨時區團隊與嚴謹的交接流程。

用 25% 規則推算最少人數#

健康的值班不該讓任何人長期被綁住。常見的經驗法則是 每位工程師花在值班上的時間不超過 25%——剩下的時間用於工程與削減 toil(呼應 14 章 的 50% 工程上限)。

由此可反推單一值班輪所需的最少人數。若採「主+副」兩條值班線,且每人值班占比上限為 $25%$:

$$N_{\min} = \frac{\text{值班線數}}{\text{每人值班占比上限}} = \frac{2}{0.25} = 8\ \text{人}$$

也就是說,要維持主/副雙線值班又不讓任何人超過 25%,團隊至少需要 8 人。人數不足時,只能在「拉高個人值班占比」「減少值班線數」或「縮小值班覆蓋範圍」之間取捨。

一輪事件量上限#

值班的負荷不只看時數,更看「一個班要處理多少事件」。一個經驗上限是:每次 12 小時值班班次,需要人實際介入的事件不超過約 2 件。

道理在於:認真處理一起事故——診斷、緩解、記錄、後續寫 postmortem——需要數小時的專注。班次內事件太多,值班者根本無法妥善處理每一件,只能草草滅火。

$$\text{單班事件上限} \approx 2\ \text{件 / 12 小時}$$

如果值班者持續超過這個事件量,問題不在「值班排得不夠」,而在「系統太脆弱、告警太吵」。正確的反應是回頭修系統與告警,而不是排更多人來承受更多 page。

值班者的心理安全#

可持續的值班建立在三個支柱上:

  • 明確的升級路徑(Clear escalation):值班者必須知道「我搞不定時找誰」。沒有後援的值班是孤立無援的恐慌。
  • 充分的工具與文件(Playbooks/Runbooks):每個常見告警都該有對應的處理手冊,告訴值班者「看到這個 page,先做這幾步」。
  • 無究責文化(Blameless):值班者必須相信「即使我處理失誤,也不會被究責」,才敢於果斷行動。這直接呼應 15 章

升級流程#

flowchart TD
    A[告警觸發] --> B[通知主值班]
    B --> C{在反應時間內<br/>確認?}
    C -->|是| D[主值班處理]
    C -->|否| E[升級至副值班]
    E --> F{在反應時間內<br/>確認?}
    F -->|是| G[副值班處理]
    F -->|否| H[升級至值班經理 / 召集事故指揮]
    D --> I{超出能力範圍?}
    G --> I
    I -->|是| H
    I -->|否| J[緩解並記錄]
    H --> J
    J --> K[事後檢討]

告警設計原則#

告警的目的,是在「使用者受害」之前把對的人叫來。設計不當的告警比沒有告警更糟——它會訓練值班者忽略所有 page。核心原則:

  • 每個 page 都對應一個 SLO 威脅:只有「正在或即將違反 SLO」的事,才值得把人吵醒。CPU 高、磁碟滿但不影響使用者,不該 page,最多記一筆待辦。
  • 每個 page 都可行動(actionable):收到 page 的人必須有事可做。無事可做的告警只是噪音。
  • 聚合與去重(aggregation & dedup):同一根因引發的數十條告警,應聚合成一條,避免值班者被同一件事轟炸。
  • 症狀導向而非原因導向:對「使用者感受到的症狀」(錯誤率上升、延遲變高)告警,而非對每一個可能的內部原因告警——後者既掛一漏萬又雜訊滿天。

告警分級#

等級觸發條件通知方式反應期待
Page正在/即將違反 SLO,需立即介入電話 / 推播,吵醒人數分鐘內
Ticket需處理但不緊急,可排程工單 / IM數小時 ~ 隔日
Logging僅供事後分析,無需即時動作寫入儀表板 / 日誌

最常見的告警錯誤,是把大量「Ticket」甚至「Logging」級別的事情設成 Page。久而久之,值班者對 page 麻木,真正的 SLO 威脅反而被淹沒。寧可 page 少而精。

告警風暴與靜音#

當一個根因引發連鎖故障,告警系統可能在幾秒內噴出成百上千條 page——這就是告警風暴(alert storm)。應對手段:

  • 依賴抑制(inhibition):當上游的「資料庫不可用」告警觸發時,自動抑制所有下游「服務連不上資料庫」的衍生告警。
  • 分組(grouping):把同一服務、同一原因的告警合併成單一通知。
  • 靜音(silencing):在計畫性維護或已知事故處理期間,主動把相關告警靜音一段時間,避免重複轟炸值班者。
  • 節流與抑制重複(throttling / repeat interval):同一條告警不在短時間內反覆通知。

Prometheus 告警規則範例#

以下示範一條「症狀導向、對應 SLO、可行動」的告警,搭配 Alertmanager 的聚合與抑制:

# prometheus rules:對使用者症狀告警,而非對內部原因
groups:
  - name: checkout-slo
    rules:
      # 高燃燒率:短窗口捕捉突發大故障(呼應第 13 章 burn rate)
      - alert: CheckoutErrorBudgetFastBurn
        expr: |
          (
            sum(rate(http_requests_total{route="/checkout",status=~"5.."}[5m]))
            /
            sum(rate(http_requests_total{route="/checkout"}[5m]))
          ) > (14.4 * (1 - 0.999))
        for: 2m
        labels:
          severity: page # 對應 SLO 威脅,吵醒人
        annotations:
          summary: "結帳錯誤預算快速燃燒,2 小時內將耗盡當月預算"
          runbook: "https://runbooks.internal/checkout-error-budget"

      # 低燃燒率:長窗口捕捉緩慢滲漏
      - alert: CheckoutErrorBudgetSlowBurn
        expr: |
          (
            sum(rate(http_requests_total{route="/checkout",status=~"5.."}[1h]))
            /
            sum(rate(http_requests_total{route="/checkout"}[1h]))
          ) > (3 * (1 - 0.999))
        for: 15m
        labels:
          severity: ticket # 不緊急,排程處理
        annotations:
          summary: "結帳錯誤率緩慢偏高,需排查"
# alertmanager:聚合、去重、依賴抑制
route:
  group_by: [service, alertname] # 同服務同類告警聚合
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h # 避免短時間重複轟炸
  routes:
    - matchers: [severity="page"]
      receiver: oncall-pager
    - matchers: [severity="ticket"]
      receiver: ticket-queue

inhibit_rules:
  # 上游 DB 掛掉時,抑制所有下游「連不上 DB」的衍生告警
  - source_matchers: [alertname="DatabaseDown"]
    target_matchers: [alertname="ServiceCannotReachDatabase"]
    equal: [cluster]

小結#

  • 反應時間 SLO 由服務的錯誤預算反推,並決定值班的形態。
  • 25% 規則推算最少值班人數(主+副雙線、上限 25% ➡️ 至少 8 人);單班事件上限約 2 件。
  • 心理安全靠明確升級路徑、充分文件與無究責文化三者支撐。
  • 每個 page 都該對應 SLO 威脅、可行動、經過聚合去重;用抑制與靜音對抗告警風暴。