SLI/SLO/Error Budget#
上一章 12 可靠性與風險管理 決定了「該追求多少可靠性」,本章談的是如何把那個模糊的目標,量化成可衡量、可驅動決策的指標體系:用 SLI 衡量、用 SLO 設定目標、用 Error Budget 把目標轉化為發布節奏的籌碼。各類監控訊號(指標、日誌、追蹤)的採集細節見 09 可觀測性。
三者辨析#
| 名詞 | 中文 | 本質 | 範例 |
|---|---|---|---|
| SLI(Service Level Indicator) | 服務等級指標 | 一個量化的測量值 | 成功請求數 / 總請求數 = 99.95% |
| SLO(Service Level Objective) | 服務等級目標 | 對 SLI 設定的目標 | 過去 28 天可用率 ≥ 99.9% |
| SLA(Service Level Agreement) | 服務等級協議 | 對外承諾與罰則 | 未達 99.5% 則賠償客戶 |
三者的關係層層遞進:SLI 是事實,SLO 是內部目標,SLA 是對外承諾。實務上 SLO 應該嚴於 SLA,留出緩衝——對外承諾 99.5%,對內就該瞄準 99.9%,這樣在真正觸及 SLA 紅線前還有反應空間。
SLA 一旦寫進合約就帶有法律與賠償後果,因此通常比 SLO 寬鬆。工程團隊日常盯的是 SLO,而不是 SLA。
如何選 SLI#
好的 SLI 直接反映使用者的體驗,而不是反映系統的內部狀態。挑選原則:
- 從使用者價值倒推:問「使用者在乎什麼?」——通常是「請求有沒有成功」「回應夠不夠快」「資料是不是新的」,而不是「CPU 用了多少」。
- 用百分位而非平均值:平均延遲會被大量快請求拉低,掩蓋掉長尾的糟糕體驗。應該用 P95、P99 這類百分位,因為使用者體驗到的是「我這一次」而不是「平均下來」。
- 比率形式最易用:把 SLI 表達為「好事件 / 有效事件」的比率,落在 0–100% 之間,天然適合設定門檻。
常見的 SLI 類型,可依服務性質挑選一到數個組合使用:
| SLI 類型 | 衡量什麼 | 典型定義 | 適用服務 |
|---|---|---|---|
| 可用性(Availability) | 請求是否成功 | 成功請求數 / 總請求數 | 幾乎所有服務 |
| 延遲(Latency) | 回應是否夠快 | 延遲低於門檻的請求數 / 總請求數 | 互動式服務 |
| 品質(Quality) | 降級回應的比例 | 完整回應數 / 總回應數 | 有降級機制的服務 |
| 新鮮度(Freshness) | 資料是否夠新 | 在時限內更新的資料 / 全部資料 | 批次、快取、管線 |
| 正確性(Correctness) | 輸出是否正確 | 正確結果數 / 總結果數 | 資料處理、計算服務 |
| 吞吐量(Throughput) | 資料傳輸是否達標 | 達標傳輸數 / 總傳輸數 | 大檔傳輸、串流 |
如何訂 SLO#
- 不要從現狀出發:如果直接拿「目前系統剛好能做到 99.7%」當目標,等於把現況合理化。SLO 應由 12 章 的風險決策反推——使用者真正需要多少。
- 留安全餘裕:對外 SLA 與對內 SLO 之間要留緩衝;同樣地,內部營運目標(如告警觸發點)又該比 SLO 更嚴,讓問題在燒穿預算前就被攔截。
- 目標數字要少而精:SLO 太多會稀釋注意力。聚焦在少數幾個真正代表使用者體驗的 SLI 上。
- SLO 是活的:隨業務演進定期檢視,過嚴拖慢迭代、過鬆傷害使用者,都要調整。
Error Budget 與停機時間換算#
Error Budget(錯誤預算)是 SLO 的另一面:
$$\text{Error Budget} = 1 - \text{SLO}$$
它代表「在不違反 SLO 的前提下,這段期間允許失敗的額度」。把可用率目標換算成可容忍的停機時間:
$$\text{允許停機時間} = (1 - \text{SLO}) \times \text{時間週期}$$
以一個月(取 $30$ 天 $= 43200$ 分鐘)為週期,常見可用率對應的每月可容忍停機如下:
$$T_{\text{down}} = (1 - 0.999) \times 43200\ \text{min} = 43.2\ \text{min}$$
| SLO(可用率) | 錯誤預算 | 每月可容忍停機 | 每年可容忍停機 |
|---|---|---|---|
| 99% | 1% | 約 7.2 小時 | 約 3.65 天 |
| 99.9% | 0.1% | 約 43.2 分鐘 | 約 8.76 小時 |
| 99.95% | 0.05% | 約 21.6 分鐘 | 約 4.38 小時 |
| 99.99% | 0.01% | 約 4.32 分鐘 | 約 52.6 分鐘 |
| 99.999% | 0.001% | 約 25.9 秒 | 約 5.26 分鐘 |
「每多一個 9」對應的可容忍停機時間就少一個數量級。這正是 12 章 成本曲線陡升的量化來源——從 99.9% 到 99.99%,等於把每月的「失敗額度」從 43 分鐘壓縮到 4 分鐘。
用錯誤預算驅動發布節奏#
Error Budget 最大的價值在於把「穩定」與「創新」之間的張力,變成一條客觀的決策規則,而不是靠開發與維運互相角力:
- 預算還很充裕 ➡️ 可以大膽發布、實驗、承擔變更風險。
- 預算快要耗盡 ➡️ 凍結非必要的功能發布,把工程力轉向修穩定性問題,直到預算回補。
- 預算已經燒穿 ➡️ 只允許修復可靠性的變更上線,新功能全部排隊等待。
flowchart TD
A[計算當期已消耗的 Error Budget] --> B{預算餘額?}
B -->|充裕| C[正常發布<br/>可承擔變更風險]
B -->|偏低| D[放慢節奏<br/>凍結非必要發布]
B -->|耗盡| E[發布凍結<br/>只准修穩定性]
C --> F[持續監控 SLI]
D --> F
E --> G[預算回補後解凍]
G --> F
F --> A這條規則之所以有效,是因為它讓「該不該發布」不再是立場之爭:只要 SLO 與預算是雙方共同認可的,後續的放行或凍結就只是查表,沒有情緒。
SLO 設定範例#
以 YAML 描述一組 SLO,可被告警系統與發布閘門共同消費:
service: checkout-api
slos:
- name: availability
description: "成功處理的結帳請求比例"
sli:
good: 'http_requests_total{status!~"5..",route="/checkout"}'
total: 'http_requests_total{route="/checkout"}'
objective: 0.999 # 99.9%
window: 28d # 滾動窗口
- name: latency
description: "P99 結帳延遲低於 500ms 的比例"
sli:
good: 'http_request_duration_seconds_bucket{le="0.5",route="/checkout"}'
total: 'http_request_duration_seconds_count{route="/checkout"}'
objective: 0.99
window: 28d
error_budget_policy:
- condition: "budget_remaining > 50%"
action: "正常發布"
- condition: "budget_remaining <= 50%"
action: "放慢發布、優先修穩定性"
- condition: "budget_remaining <= 0%"
action: "凍結功能發布,只准可靠性修復"燃燒率(Burn Rate)告警
比起「預算剩多少」,更靈敏的做法是看「預算消耗得多快」——燃燒率(burn rate)。
燃燒率 = 1 表示按 SLO 允許的速度均勻消耗,剛好在窗口結束時用完預算;燃燒率 = 14.4 表示如果持續這個速度,會在窗口的 $1/14.4$ 時間內就燒光整月預算。
$$\text{Burn Rate} = \frac{\text{實際錯誤率}}{1 - \text{SLO}}$$
實務上用「多窗口、多燃燒率」組合告警:高燃燒率(如 14.4 倍)配短窗口,捕捉突發大故障;低燃燒率(如 3 倍)配長窗口,捕捉緩慢滲漏。告警規則的具體寫法見 16 On-Call 與告警設計。
小結#
- SLI 是事實、SLO 是內部目標、SLA 是對外承諾;SLO 應嚴於 SLA 並留緩衝。
- 選 SLI 從使用者價值倒推、用百分位、表達成比率;訂 SLO 不從現狀出發、保留安全餘裕。
- Error Budget = 1 − SLO,可換算成具體的可容忍停機時間,量化了「每多一個 9」的代價。
- 用錯誤預算驅動發布節奏,把穩定與創新的張力變成客觀、可查表的決策規則。