昨天講了四個黃金訊號,今天要講另外兩套:USE 和 RED。
剛開始看到這三套的時候覺得也太複雜了吧,為什麼差不多東西要出三套,到底要用哪一個?查了之後發現這個困惑是有原因的——它們確實有一半重疊,但是設計的時候想的對象不一樣。搞清楚「對象是誰」,選擇就不難了。
kubectl set env deploy/pricing BUG_SILENT_DISCOUNT=false LEAK_KB_PER_REQUEST=0
kubectl set env deploy/catalog BUG_N_PLUS_ONE=false
今天要比較節點之間、Pod 之間的資源差異,不要被故障干擾。
先說為什麼不能「想到什麼就監控什麼」。
Prometheus 裝好之後,光是 node-exporter 和 kube-state-metrics 就會給我們上千個指標。這時候的問題不是資料不夠,是不知道要看哪一個。
方法論的作用就是給我們一份檢查清單:面對一個新的服務,照著清單問三四個問題,就不會漏掉重要的東西,也不會被上千個指標淹沒。
它不是什麼高深的理論,就是清單而已。
USE 是 Brendan Gregg 提出的,針對的是資源,也就是 CPU、記憶體、磁碟、網路、連線池這類「有容量上限的東西」。
對每一項資源問三個問題:
| 字母 | 問題 | 例子 |
|---|---|---|
| Utilization(使用率) | 這個資源有多忙? | CPU 用了 70% |
| Saturation(飽和度) | 有多少工作在排隊等它? | 有 5 個執行緒在等 CPU |
| Errors(錯誤) | 它有沒有出錯? | 磁碟讀取失敗次數 |
使用率和飽和度的差別是關鍵。
用餐廳比喻的話:使用率是「幾成的桌子有人坐」,飽和度是「門口排了幾個人」。
桌子坐滿(使用率 100%)但是門口沒人排隊,代表運作良好、剛好夠用;桌子坐滿而且門口排了二十個人,那才是真的出事了。只看使用率會漏掉後面那種情況。
CPU 尤其明顯:CPU 100% 不一定是問題,可能只是它正在認真工作。真正的警訊是「有多少任務在等 CPU」。
RED 是 Tom Wilkie 提出的,他後來是 Grafana Labs 的共同創辦人。它針對的是請求驅動的服務,也就是「有人來請求、你回應他」這種東西。
| 字母 | 問題 |
|---|---|
| Rate(速率) | 每秒進來幾個請求? |
| Errors(錯誤) | 其中失敗幾個? |
| Duration(耗時) | 花了多久? |
只有三個,很好記。而且這三個正好對應 Day 2 訂的 SLI:可用性 SLI 是 Errors 的反面,延遲 SLI 就是 Duration。這不是巧合,SLI 本來就該從使用者感受得到的東西挑。
| 對象 | 項目 | |
|---|---|---|
| USE | 資源(CPU、記憶體、磁碟) | 使用率、飽和度、錯誤 |
| RED | 服務(請求驅動) | 速率、錯誤、耗時 |
| 四個黃金訊號 | 服務,但是多看一項資源 | 延遲、流量、錯誤、飽和度 |
排在一起就看得出來了:四個黃金訊號大致等於 RED 加上 Saturation。它比 RED 多要求你看一眼「離塞爆還有多遠」。
所以答案不是「選哪一個」,而是:
服務用 RED,資源用 USE,兩個都要有。
四個黃金訊號可以理解成 RED 的加強版,它提醒你別忘了資源那一面。
今天要回 Grafana 建面板,先把它開起來:
kubectl port-forward -n monitoring svc/kps-grafana 3000:80
一樣要另外開一個終端機視窗放著,然後瀏覽器打開 localhost:3000。忘記密碼的話再撈一次:
kubectl get secret -n monitoring kps-grafana \
-o jsonpath="{.data.admin-password}" | base64 -d
我打算每個服務一列、三張圖。建面板的路徑跟 Day 9 一樣:右邊 Add 那塊點 Panel,再點 Configure visualization,查詢區右上角切成 Code。
以 pricing 為例:
# Rate:每秒幾個請求
sum(rate(http_requests_total{job="pricing"}[5m]))
# Errors:失敗的比例
(sum(rate(http_requests_total{job="pricing", status=~"5.."}[5m])) or vector(0))
/ sum(rate(http_requests_total{job="pricing"}[5m]))
# Duration:P95
histogram_quantile(0.95,
sum(rate(http_request_duration_seconds_bucket{job="pricing"}[5m])) by (le))
catalog 和 gateway 照著改 job 就好,三個服務就是三列。
這樣排的好處是橫向可以比較。三個服務的錯誤率放在同一個垂直位置,掃一眼就知道哪個特別高,不用一張一張點進去看數字。

三個服務、三個指標、九張圖。做這張圖的時候我把 Day 9 學到的東西又用了一次,而且踩了幾個新的坑:
每一張的 Y 軸都要自己釘。 一開始我讓它自動縮放,Rate 的 Y 軸變成 5.56 到 5.63、Duration 變成 0.09749 到 0.09753——整張圖的高度是 0.04 毫秒。線在上面劇烈震盪,看起來像出了什麼事,其實那是雜訊冒充訊號。三張的設定分別是:
| 面板 | Unit | Min | Max | Threshold |
|---|---|---|---|---|
| Rate | requests/sec | 0 | 留空 | — |
| Errors | Percent (0.0-1.0) | 0 | 0.05 | 0.01 |
| Duration | seconds (s) | 0 | 留空 | 0.3 |
Errors 的 Max 設 0.05 是因為 Day 2 訂的可用性 SLO 是 99%,反過來就是錯誤率 1% 是紅線,上面留幾倍空間就夠了。設成 1(也就是 100%)的話,錯誤率從 0 漲到 3% 在圖上只有幾個像素。
只有一條線的面板,圖例要關掉。 Grafana 有兩個地方可以設定 Legend:查詢底下那個是決定「這條線叫什麼名字」,右側面板選項裡那個才是決定「圖例要不要顯示」。這九張圖每張都只有一條線,圖例寫什麼都是把標題再講一次,關掉還多出一行高度。等一下做 by (pod) 那張有兩條線時才需要它,那時候在查詢的 Legend 選 Custom 填 {{pod}},Grafana 會自動帶入實際的 Pod 名字。
現在九張都是平線,這是對的。 故障全關著,這是基準線。它的價值不在好看,在於等一下打開故障的時候,我可以拿它來對照。
Errors 那條查詢我一開始是這樣寫的,沒有 or vector(0):
sum(rate(http_requests_total{job="pricing", status=~"5.."}[5m]))
/ sum(rate(http_requests_total{job="pricing"}[5m]))
結果圖是空的。不是 0,是什麼都沒有。
我先跑分子確認:
sum(rate(http_requests_total{job="pricing", status=~"5.."}[5m]))
也是空的。原因很單純——我的示範服務根本不會回 5xx。common.py 記的是實際的狀態碼,而 pricing 的每個端點正常情況都回 200,連 gateway 的結帳出錯時也是接住例外、回一個帶 error 欄位的 200。所以 status 這個標籤從頭到尾只有 "200",5.. 一個都比對不到。
但是為什麼是空的而不是 0? 因為 PromQL 的除法是在做序列比對:它要在左右兩邊找標籤相同的序列來配對。左邊一條序列都沒有,就沒有東西可以配,結果自然是空的。
or vector(0) 的意思是「左邊有東西就用左邊,沒有就給我一個常數 0」。加上去之後,沒有錯誤的時候會畫出一條貼在 0 的線,而不是一片空白。
這件事值得停下來想一下,因為**「沒有錯誤」和「查不到資料」在 PromQL 裡長得一模一樣,都是空的**。昨天忘記 by (le) 是空的、Day 8 標籤對不上也是空的,三個完全不同的錯,同一個症狀。差別在於,前兩個是我寫錯,這一個是真的沒有資料——而畫面上分不出來。
實務上的順序是這樣:
第 4 種正是故障三的形狀:記憶體一直漲(USE 異常),但是使用者完全無感(RED 正常)。這種訊號到底要不要告警,是後面講告警的時候要處理的核心問題。
Day 9 我列了一張「先不畫的圖」的表,把 CPU 和記憶體放進去,當時寫了一句「它們的位置是第二層,第一層的 SLI 圖告訴你有事,你才往下鑽」,然後說之後會給這個分層一個更完整的說法。
今天可以講完整了,那個分層就是這兩套方法論:
所以那張表的意思不是「CPU 沒有用」,而是「CPU 不該出現在你半夜被叫醒時第一眼看到的畫面上」。
or vector(0) 分開今天只做完了 RED 那一半。明天把 USE 實際做出來,順便做一個把服務擴成兩份的小實驗——那個實驗的結果跟我預期的完全不一樣。