結論先講

Dashboard 不是越多圖表越好——是出事時 30 秒內能看出問題在哪。 放 50 個圖表等於沒放,因為你不知道要看哪個。Four Golden Signals(延遲、流量、錯誤率、飽和度)涵蓋 90% 的監控需求,剩下 10% 用業務指標補。


先搞懂 SLI / SLO / SLA

這三個詞常常被混用,但含義完全不同:

概念意思誰訂的範例
SLI(Service Level Indicator)衡量指標的具體定義工程師P95 延遲、錯誤率、可用性
SLO(Service Level Objective)指標的目標值工程 + PMP95 < 500ms、錯誤率 < 1%
SLA(Service Level Agreement)寫進合約的承諾業務 + 法務可用性 99.9%,違約賠償

SLO 不是隨便訂的——用壓測數據當 baseline。

我們在 第 05 篇 已經搭好 Grafana,也在壓測系列累積了大量數據。這些數據正是設 SLO 的最佳依據。

從壓測數據訂 SLO

壓測結果(100 VU,正常負載):
  P95 延遲: 180ms
  錯誤率: 0%
  RPS: 450

壓測結果(500 VU,高負載):
  P95 延遲: 520ms
  錯誤率: 0.3%
  RPS: 1200

→ SLO 建議:
  P95 延遲: < 500ms(100 VU 的 2.7 倍,500 VU 剛好)
  錯誤率: < 1%(500 VU 的 3 倍餘量)
  可用性: 99.9%(月停機 < 43 分鐘)

不是拍腦袋說「P95 要 200ms 以下」——是用實際數據決定合理的目標,再加上 buffer。


Four Golden Signals

Google SRE Book 定義的四個核心指標,幾乎所有服務都適用:

1. Latency(延遲)

看什麼:請求的回應時間分佈

重點不是平均值——是百分位數

P50(中位數): 50ms   → 「一般使用者的體驗」
P90:          120ms  → 「大部分使用者的體驗」
P95:          280ms  → 「SLO 通常看這個」
P99:          1200ms → 「最慢的 1% 使用者」

為什麼不看平均值? 因為平均值會被極端值拉高或拉低。P95 = 280ms 但平均 = 800ms,代表有少數請求非常慢——平均值看不出這個分佈。

Prometheus 查詢

# P95 延遲
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
 
# 按 service 拆分
histogram_quantile(0.95,
  sum by (le, service) (
    rate(http_request_duration_seconds_bucket[5m])
  )
)

2. Traffic(流量)

看什麼:每秒請求數(RPS)

# 總 RPS
sum(rate(http_requests_total[5m]))
 
# 按 endpoint 拆分
sum by (path) (rate(http_requests_total[5m]))

流量指標的意義不是「現在多忙」——是「跟平常比正不正常」。突然掉 50% 可能代表前端壞了、DNS 出問題、或者上游服務掛了。

3. Errors(錯誤率)

看什麼:5xx 錯誤佔總請求的比例

# 錯誤率
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))

注意:4xx 不算系統錯誤——那是 client 的問題。但如果 4xx 突然暴增,也值得注意(可能是 API 合約改了)。

4. Saturation(飽和度)

看什麼:資源使用率(CPU、記憶體、DB 連線池、磁碟 I/O)

# CPU 使用率
100 - (avg(rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
 
# 記憶體使用率
(node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)
/ node_memory_MemTotal_bytes * 100
 
# DB 連線池使用率
pg_stat_activity_count / pg_settings_max_connections * 100

第 23 篇 的容量規劃告訴我們:瓶頸會從 AP → DB → 網路逐層轉移。Dashboard 上必須看到每一層的飽和度。


Dashboard 怎麼排版

Overview Dashboard(第一眼看這個)

┌─────────────────────────────────────────────────────┐
│  🔴 SLO Status: P95 延遲 ✅ | 錯誤率 ✅ | 可用性 ✅  │
├──────────────┬──────────────┬───────────────────────┤
│  總 RPS       │  P95 延遲    │  錯誤率              │
│  (Stat Panel) │ (Stat Panel) │  (Stat Panel)        │
├──────────────┴──────────────┴───────────────────────┤
│  RPS 趨勢(按服務拆分)        Time Series           │
├─────────────────────────────────────────────────────┤
│  P95 延遲趨勢(按服務拆分)    Time Series           │
├─────────────────────────────────────────────────────┤
│  錯誤率趨勢                   Time Series           │
├────────────────────────┬────────────────────────────┤
│  CPU 使用率             │  記憶體使用率              │
├────────────────────────┼────────────────────────────┤
│  DB 連線池使用率         │  磁碟 I/O               │
└────────────────────────┴────────────────────────────┘

最上面一排是 Stat Panel:紅綠燈,一眼看出有沒有問題。下面是趨勢圖,看是在惡化還是穩定。

Service Detail Dashboard(點進去看細節)

每個服務一個:

  • 該服務的 RPS / P95 / 錯誤率
  • 該服務的 endpoint breakdown(哪個 API 最慢)
  • 該服務的 CPU / 記憶體
  • 該服務的 DB 查詢時間(如果有)
  • 最近的 Error log(直接嵌 Loki query)

業務 Dashboard

技術指標之外,加上業務指標:

指標為什麼重要
每分鐘訂單數掉了 = 用戶下不了單
付款成功率掉了 = 金流有問題
註冊轉換率掉了 = 註冊流程壞了
API 回應時間(按用戶層級)VIP 用戶是否被影響

告警規則怎麼設

基本原則

  1. 告警要 actionable:收到告警後,你能做什麼?如果答不出來,就不該告警
  2. 分級:不是所有告警都要半夜叫人起來
  3. 用 SLO 設閾值,不要用直覺

告警分級

等級條件通知方式
P0(緊急)SLO 已違反:錯誤率 > 5% 持續 5 分鐘Slack + 電話
P1(高)SLO 快違反:P95 > 800ms 持續 10 分鐘Slack
P2(中)資源告警:CPU > 80% 持續 15 分鐘Slack(上班時間)
P3(低)趨勢異常:流量比昨天同時段低 50%Dashboard 標記

Prometheus Alerting Rules 範例

groups:
  - name: slo-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
          / sum(rate(http_requests_total[5m])) > 0.05
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "錯誤率超過 5%(目前 {{ $value | humanizePercentage }})"
 
      - alert: HighLatency
        expr: |
          histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.8
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "P95 延遲超過 800ms(目前 {{ $value | humanizeDuration }})"
 
      - alert: HighCPU
        expr: |
          100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "CPU 使用率超過 80%(目前 {{ $value }}%)"

壓測數據 vs 生產數據

壓測時你得到的是「理想狀況下的效能上限」。生產環境會更複雜:

差異壓測生產
流量模式均勻有尖峰(午餐時間、促銷)
資料量測試資料(少)真實資料(多且亂)
外部依賴Mock 或本地真實的第三方 API(會慢、會掛)
網路內網跨區域、用戶端網路品質不一

壓測數據是 SLO 的起點,不是終點。 上線後要根據真實流量調整 SLO。


我的 Dashboard 演進

第一版:什麼都放(30 個圖表)
  → 出事時反而不知道看哪個

第二版:只放 Four Golden Signals
  → 知道「系統壞了」但不知道「哪裡壞」

第三版:Overview + Service Detail 兩層
  → Overview 看全局,點進去看細節
  → 加上 SLO 紅綠燈

第四版:加上業務指標
  → 技術指標正常但訂單數掉了 → 發現是前端按鈕壞了

常見的坑

1. Alert Fatigue(告警疲勞)

設太多告警 → 每天收 50 封 → 沒人看 → 真正出事時也沒人反應。

解法:每個告警都問自己「收到這個我會做什麼?」答不出來就刪掉。

2. 只看 Dashboard 不看趨勢

「CPU 60% 正常啊」——但上週同一時間是 30%。趨勢比絕對值重要。 設一個「Week-over-Week」面板比較同一指標。

3. 沒有 Runbook

告警響了,但新人不知道要做什麼。每個 P0 / P1 告警都要配一份 Runbook:

## Alert: HighErrorRate
### 可能原因
1. DB 連線池滿 → 檢查 pg_stat_activity
2. 上游服務掛了 → 檢查 dependency dashboard
3. 最近有部署 → 檢查最近 30 分鐘的 deploy history
 
### 處理步驟
1. 確認影響範圍(哪些 endpoint、多少用戶)
2. 如果是最近部署造成 → rollback
3. 如果是 DB → 擴連線池 / kill long queries

下一篇

資料一致性(一):Saga Pattern 取代分散式 Transaction — 可觀測性搞定了,你知道系統「哪裡壞了」。但微服務最棘手的問題不是效能——是資料一致性。「訂單成立但沒扣庫存」這種事在單體不可能發生,在微服務卻天天上演。


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:可觀測性(二):集中 Log 不再拼湊三份 log → 下一篇:資料一致性(一):Saga Pattern 取代分散式 Transaction