為什么你的Prometheus監控"一切正常",用戶卻說系統崩了?
說個真事兒。上個月凌晨3點,我被一連串Prometheus告警吵醒。睜開眼一看,手機屏幕全是紅:
api_response_time_p99 > 200ms
db_connection_pool_usage > 85%
error_rate_5m > threshold
我趕緊爬起來打開Grafana大盤,結果看到的畫面讓我一臉懵逼:
? CPU: 45%
? Memory: 62%
? Network: 正常
? Disk I/O: 正常
? 所有Pod都是Running狀態
就這?那你告個毛線的警?
但半小時后,客服群炸了。大量用戶投訴無法登錄,有些甚至說"你們是不是服務器被黑了"。我當時腦子里就一個念頭:我的監控明明說一切正常啊,到底哪里出問題了?
折騰到早上6點才查出來:某個第三方OAuth服務商的API響應從平時的50ms降到了3秒,導致我們的登錄流程卡死,進而引發了一系列連鎖反應。
最諷刺的是什么?我們的Prometheus里根本沒有監控這個第三方依賴。因為它以前從來沒出過問題,所以壓根沒想過要加監控。
這事兒讓我開始重新思考一個問題:我們花了那么多時間搞監控,到底在監控什么?
干了這么多年監控,我發現大部分人對Prometheus的理解都有個根本性的誤區——以為監控能發現所有問題。
實際上呢?監控只能發現你預先知道會發生的問題。
為什么這么說?咱們看看Prometheus的工作原理:
你定義一個metric:http_requests_total
你寫個PromQL規則:rate(http_requests_total{status="500"}[5m]) > 0.01
然后配置Alertmanager:觸發就發釘釘/短信
看出來了嗎?這套流程的前提是你得提前知道要監控什么,閾值該設多少,什么情況算異常。
這對成熟系統來說沒問題。比如:
磁盤快滿了 → 這是"已知故障"
內存泄漏 → 這是"經典問題"
MySQL主從延遲 → 這是"教科書案例"
但真實世界的故障呢?70%都是你從來沒想到過的組合:
某個微服務的重試邏輯有bug,在特定并發下會觸發雪崩
Kubernetes調度器把所有新Pod調度到同一個節點,導致熱點
某個Java服務的GC暫停時間從5ms突然變成500ms,但還沒到你設的閾值
某個定時任務的cron表達式寫錯了,從每天一次變成每秒一次
這些問題的"癥狀"可能在Prometheus里顯示為"正常范圍內的波動"。等你發現的時候,用戶已經跑路了。就像下棋,監控只能防住你復盤過的棋路,但對手總會下出新的變化。
陷阱1:維度選擇的盲區
大家都知道監控要看"四個黃金信號":延遲、流量、錯誤、飽和度。這套方法論沒毛病,但問題在于——它假設系統行為可以用這四個維度完整描述。
實際上呢?有些故障根本不在這些維度上:
案例A - 數據正確性問題
去年遇到過一次,所有監控指標都正常,但用戶看到的賬戶余額是錯的。后來查出來是某個字段更新的SQL寫錯了,本該更新A表結果更新了B表。
P99延遲?正常 ?
錯誤率?正常 ?
QPS?正常 ?
但數據已經臟了,Prometheus根本看不到。
案例B - 長尾效應
有次壓測,看P50和P99都很漂亮。結果上線后收到投訴,說某些VIP用戶總是超時。后來才發現是P99.9的請求全掛了,而那0.1%剛好是付費用戶。
你的Prometheus可能只采集到P99,P99.9根本沒算。結果就是:大盤數據漂亮,核心用戶罵娘。
陷阱2:Cardinality爆炸的反噬
說到Prometheus,就不得不提label cardinality這個大坑。
剛開始用Prometheus的時候,我也犯過這個錯誤:覺得標簽越詳細越好,于是給metric加了一堆label:
http_requests_total{
user_id="12345", // 100萬用戶
endpoint="/api/xxx", // 500個接口
status="200", // 10種狀態碼
region="cn-beijing", // 20個地區
device_type="iOS" // 50種設備
}算一下:100萬 × 500 × 10 × 20 × 50 = 50億時間序列。
結果是什么?Prometheus直接OOM,監控系統成了故障源頭。
后來我明白了一個道理:監控不是越詳細越好,而是要抓住核心矛盾。就像拍照,如果每張照片都是100GB的原圖,你的硬盤再大也不夠用,而且根本看不過來。
現在我的原則是:核心告警不超過7個,標簽不超過5個維度,時間序列控制在100萬以內。寧可少而精,不要多而亂。
陷阱3:相關性不等于因果性
Prometheus最擅長的是發現相關性,但最不擅長的是找因果關系。
舉個例子,你看到Grafana上"數據庫CPU飆升"和"API響應變慢"同時發生,第一反應肯定是:"數據庫有問題!"
但真實原因可能是:
某個測試同學誤連了生產庫,跑了個全表掃描
某個定時任務配置錯了,從每天1次變成每秒1次
Redis緩存突然失效,查詢全打到DB
某個爬蟲在暴力爬取你的API
Prometheus只能告訴你"果"(CPU高了),但無法告訴你"因"(為什么高)。
就像看電影的截圖:你看到主角第1分鐘在笑、第5分鐘在哭、第10分鐘倒地了,但你不知道中間發生了什么。故障調試需要的不是截圖,而是完整的錄像——這就是為什么我們需要分布式追蹤。
講真,這兩年"可觀測性"這個詞被各種廠商吹得神乎其神,搞得像是買了個Observability平臺就能解決所有問題似的。
但我理解的可觀測性,本質上就一句話:承認監控有局限,所以要保留足夠的上下文信息,讓你在出問題時能快速調試。
對比一下:
| 監控思維 | 可觀測性思維 |
| "這個指標要不要加監控?" | "出問題時我能快速定位嗎?" |
| 預定義指標+閾值 | 保留高精度原始數據 |
| 聚合數據(P99、平均值) | 原始事件(traces、logs) |
| 被動響應告警 | 主動探索分析 |
具體怎么做?我總結了幾個實戰經驗:
1. 給每個請求一個TraceID
這個不用多說了,OpenTelemetry已經是標準了。關鍵是要端到端貫穿:從瀏覽器、到網關、到微服務、到數據庫,全程帶著TraceID。
出問題的時候,拿著TraceID在Jaeger里一搜,整條鏈路的耗時、依賴關系、錯誤點一目了然。
2. 監控要"極簡主義"
SRE那本書里有句話我特別認同:核心告警不要超過5-7個。
為什么?因為告警太多等于沒告警。你的團隊會麻木,真正的故障反而被淹沒了。
我現在的原則是:
每個告警必須對應一個Runbook(明確的處理流程)
季度內查看不到1次的指標,直接刪掉
能用日志解決的,就不要加監控
3. 別忘了監控"變更"
這個經常被忽略。我們花大力氣監控CPU、內存、QPS,但往往忘了最危險的因素:變更。
統計一下你們過去一年的故障,至少70%都是變更引起的:
代碼部署
配置修改
基礎設施升級
依賴版本變更
所以我現在的做法是:把所有變更事件都打到Prometheus的Annotations里,在Grafana上顯示為垂直線。出問題的時候,一眼就能看出"是不是剛剛部署了什么"。
寫這篇文章的時候,我翻了翻去年的oncall記錄。印象最深的不是那些半夜被吵醒的時刻,而是那些監控系統什么都沒發現,用戶卻已經炸了的瞬間。
監控系統就像汽車的儀表盤:它能告訴你油量、速度、轉速,但它無法告訴你"為什么發動機異響",更無法告訴你"前方會不會堵車"。
監控只能發現已知問題——這不是監控的缺陷,而是復雜系統的本質。
所以我的建議是:
? 承認監控有局限
? 為系統設計"可調試性"(logs、traces、metrics三件套)
? 保留出問題時需要的"證據"
? 培養團隊的調試直覺和RCA文化
最后借用Bryan Cantrill的一句話:"Debugging is iterative hypothesis testing." 監控給你假設的起點,但真正找到根因,靠的還是你對系統的理解。
新聞搜索



