可觀測性(Observability)是微服務架構中不可或缺的基礎能力。當系統從單體演進到數十甚至上百個獨立服務時,傳統的監控手段已無法應對分散式環境的複雜性。本章從可觀測性的三大支柱出發,涵蓋分散式追蹤原理、指標監控架構,以及生產環境的治理實踐。

為什麼需要可觀測性#

從單體到微服務的監控困境#

在單體架構時代,系統部署在集中的叢集裡,監控主要關注 CPU、記憶體、回應時間等核心指標。進入微服務時代後,情況發生了根本性改變:

  • 分散式複雜性:企業內部系統由數十甚至上百個服務組成,分散部署在不同節點
  • 認知負荷:服務由不同團隊開發,單一開發人員難以全盤理解系統的拓撲結構與互動邏輯
  • 局部失敗常態化:在分散式環境下,任何可能出問題的地方都會出錯

在錯綜複雜的分散式系統中,單一服務的 Metrics 雖然正常,但整體業務流程可能因某個下游服務的延遲或錯誤而中斷。若缺乏完整的可觀測體系,開發者將難以快速定位問題根源。

缺乏監控的代價#

  • 發布像在賭博:服務上線後無法確認健康狀態,回滾決策缺乏資料支持
  • 被動發現問題:往往是用戶投訴後才開始介入排查
  • 排查效率低落:設定錯誤難追蹤,效能瓶頸成為黑箱
  • 責任難以釐清:最終將問題歸咎於「網路波動」,真正的系統隱患被掩蓋

既然分散式環境下的局部失敗不可避免,我們必須擁有快速感知、定位並修復這些失敗的能力。監控不是「選配」,而是微服務生存的「標配」。

可觀測性的三大支柱#

可觀測性建立在三大支柱之上:Log(日誌)Trace(追蹤)Metrics(指標)。架構師在設計監控平台時,不應期望單一工具解決所有問題,而是應該思考如何將這三根支柱有效整合。

flowchart TB
    subgraph Pillars["可觀測性三大支柱"]
        direction LR
        L["Log<br/>(日誌)"]
        T["Trace<br/>(追蹤)"]
        M["Metrics<br/>(指標)"]
    end

    L --> ELK["ELK Stack"]
    T --> Tracing["CAT / Zipkin<br/>SkyWalking"]
    M --> Prom["Prometheus<br/>+ Grafana"]

    style L fill:#e3f2fd
    style T fill:#fff3e0
    style M fill:#e8f5e9

四種監控維度#

分散式系統的監控可分為四種核心維度,每種都有其獨特的適用場景與資料特性。

維度概述#

維度定義資料特性代表工具
Logging (日誌)應用程式輸出的離散事件結構化/非結構化文字ELK Stack
Tracing (追蹤)請求從進入到離開的完整路徑有開始與結束的 Request-scoped 資料CAT, Zipkin, SkyWalking
Metrics (指標)隨時間推移的數值型資料點可聚合的數值,支援標籤Prometheus + Grafana
Health Checks (健康檢查)定期探測應用的存活與就緒狀態布林值/狀態碼Prometheus Blackbox

成本效益比較#

根據 Go for Industrial Programming 的分析框架,從四個維度評估各類監控手段:

比較維度Metrics (指標)Logging (日誌)Tracing (追蹤)
CAPEX (研發成本)中(投入適中)低(搭建門檻低)高(需埋點)
OPEX (運維成本)低(維護簡單)高(需不斷擴容)中(複雜度居中)
Reaction (響應靈敏度)高(即時告警)低(靈敏度不足)低(事後分析)
Investigation (查錯能力)低(僅顯示趨勢)中(檢索還原現場)高(鏈路分析首選)

場景選擇指南

  • 監控告警:首選 Health Checks(直接反應存活)與 Metrics(反應趨勢抖動)
  • 除錯與問題定位:首選 Tracing(查看呼叫鏈效能)與 Logging(查看詳細錯誤訊息)

監控分層架構#

監控應依據職責進行分層:

層級關注點負責人
系統層 (System)CPU、記憶體、磁碟等硬體資源運維
應用層 (Application)延遲、錯誤率、QPS 等框架指標應用開發/框架開發
業務層 (Business)轉化率、登入數、下單量等核心業務資料業務研發/產品經理

分散式追蹤原理#

Google Dapper 論文#

2010 年,Google 發表了《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》論文,奠定了分散式追蹤技術的理論基礎。此論文直接啟發了 Zipkin、Pinpoint、SkyWalking 等開源工具的誕生。

Dapper 架構流程#

flowchart LR
    A["應用程式<br/>(埋點函式庫)"] -->|寫入| B["本地日誌"]
    B -->|讀取| C["Dapper Daemon"]
    C -->|傳送| D["Collector 叢集"]
    D -->|寫入| E["BigTable"]
    E -->|查詢| F["Dapper UI"]

核心概念:Trace / Span / Annotation#

分散式追蹤建立在四個核心術語之上:

  • Trace(蹤跡):代表一次完整的分散式呼叫鏈路。從用戶發出請求,經過多個服務節點,最後返回結果的完整過程
  • Span(跨度):代表 Trace 中的一個方法呼叫或遠端呼叫片段。若干個 Span 串聯起來,組成一個完整的 Trace
  • Annotation(標註):附著在 Span 上的日誌資訊,用於記錄業務資料或狀態
  • Sampling(取樣率):為降低儲存與效能開銷,系統通常採用取樣機制(如每 100 個請求記錄 1 次)

Trace ID 與 Span ID 的傳遞機制#

flowchart TD
    S1["服務 1<br/>TID=X, SID=1, PID=null"]
    S1 --> Redis["Redis<br/>TID=X, SID=2, PID=1"]
    S1 --> S2["服務 2<br/>TID=X, SID=3, PID=1"]
    S2 --> MySQL["MySQL<br/>TID=X, SID=4, PID=3"]
    S2 --> S3["服務 3<br/>TID=X, SID=5, PID=3"]

    style S1 fill:#e3f2fd
    style S2 fill:#fff3e0
    style S3 fill:#e8f5e9

後端分析系統利用 Trace ID 找到所有相關日誌,再利用 Span IDParent ID 重建樹狀的父子呼叫關係,最終在 UI 上還原出完整的瀑布流圖表。

追蹤產品比較#

技術演進的兩大分支#

市場上的追蹤產品主要分為兩個流派:

  • eBay 分支(CAL 體系):2002 年 eBay CAL -> 2011 年大眾點評 CAT,重視報表與日誌聚合
  • Google 分支(Dapper 體系):2010 年 Dapper 論文 -> 2012 年 Twitter Zipkin -> Pinpoint / SkyWalking / Jaeger,重視 Trace 模型標準化

主流產品對比#

比較維度CATZipkinPinpointSkyWalking
聚合報表最強,具備 APM 能力幾乎為零部分報表中等
服務依賴圖簡單簡單最佳良好
埋點方式侵入式 (SDK)侵入式 (SDK)非侵入 (Java Agent)非侵入 (Agent)
心跳檢測支援不支援支援支援
告警內建不內建內建內建
多語言支援Java/.NET 為主最豐富僅 Java多語言
OpenTracing不相容相容不相容相容

在選擇追蹤產品時,務必選擇相容 OpenTracing(或其後繼標準 OpenTelemetry)的產品。這確保了應用程式的埋點程式碼與後端監控設施解耦,未來切換後端時無需修改業務代碼。

侵入式 vs 非侵入式的權衡

侵入式(CAT、Zipkin)

  • 需要修改程式碼進行埋點
  • 優點:效能較好,可控性高
  • 缺點:接入成本較高,對業務代碼有侵入

非侵入式(Pinpoint、SkyWalking)

  • 基於 Java Agent(位元組碼增強/AOP)技術
  • 優點:開發人員無需修改程式碼,推廣容易
  • 缺點:效能開銷相對較大

SkyWalking 近年異軍突起,正是因為它試圖在「非侵入」與「高效能」之間取得更好的平衡,同時支援多語言探針。

CAT 架構設計#

CAT(Central Application Tracking)是大眾點評開源的分散式監控系統,國內落地案例最多(攜程、陸金所等上百家公司)。

設計目標與取捨#

CAT 優先保障應用的穩定性與效能,而非監控資料的絕對完整性:

設計目標說明
對應用無影響CAT 宕機不能影響業務應用正常運作
高實時性秒級或分鐘級的即時反饋
高吞吐量單機支撐數百甚至上千個應用
用戶端低開銷資源消耗控制在平均 2% 以內

CAT 的設計哲學中,可靠性並非首要目標。用戶端傳送訊息時若佇列已滿可能丟棄訊息,服務端在極端高峰時也可能丟棄部分資料。這是為了貫徹「對應用無影響」的最高原則。在海量請求下,丟失 0.01% 的監控資料,對分析系統健康度幾乎沒有影響。

用戶端架構#

flowchart LR
    subgraph App["應用程式"]
        R["請求進入"] --> TL["ThreadLocal<br/>訊息樹建置"]
        TL --> |Service Call| TL
        TL --> |SQL Query| TL
        TL --> |Cache Access| TL
        TL --> MQ["記憶體訊息佇列"]
    end

    MQ --> |"Sender 執行緒<br/>(非同步)"| Server["CAT Server"]

    style TL fill:#e3f2fd
    style MQ fill:#fff3e0
  • ThreadLocal 訊息樹建置:利用 ThreadLocal 維護上下文,請求進入時初始化訊息樹(Message Tree),後續的 Service Call、SQL 查詢、Cache 訪問都作為子節點掛載,請求結束時整棵樹建置完成
  • 非同步傳送機制:建置完成的訊息樹先放入記憶體佇列,後台獨立的 Sender 執行緒負責打包發送,主業務執行緒不會被網路 I/O 阻塞

服務端架構#

flowchart TD
    C["用戶端"] --> R["Receiver<br/>(接收與排隊)"]
    R --> Q["內部佇列"]
    Q --> A1["Transaction<br/>Analyzer"]
    Q --> A2["Event<br/>Analyzer"]
    Q --> A3["Heartbeat<br/>Analyzer"]

    A1 --> MySQL["MySQL<br/>(報表資料)"]
    A2 --> MySQL
    A3 --> MySQL
    A1 --> HDFS["HDFS<br/>(原始日誌)"]

    style R fill:#e3f2fd
    style MySQL fill:#e8f5e9
    style HDFS fill:#fff3e0

分層儲存策略

  • 報表資料(Reports):按分鐘、小時聚合後的統計資料存入 MySQL,保證秒級查詢響應
  • 原始日誌(Raw Trace):完整的呼叫鏈詳情非同步寫入 HDFS,解決海量日誌的長期儲存

核心監控模型#

模型定義用途
Transaction (事務)一段程式碼的執行區間,記錄開始/結束時間URL 請求、SQL 執行、RPC 呼叫
Event (事件)原子的瞬時動作,用於計數Exception 次數、特定邏輯命中次數
Heartbeat (心跳)定期上報的系統狀態快照CPU、記憶體、執行緒數、磁碟 I/O
Metric (指標)業務指標的變化值訂單金額、銷售額趨勢

Prometheus 核心概念#

Prometheus 是一款開源的系統監控與告警工具箱,由 SoundCloud 開發,使用 Go 語言編寫。其設計靈感源自 Google 內部的 Borgmon 監控系統,是 CNCF 推薦的雲原生監控標準。

核心特性#

  • 時間序列資料庫(TSDB):專為隨時間變化的數值型資料設計
  • Pull 模式(拉模式):Prometheus Server 主動從目標系統拉取資料,而非等待推送
  • 多維度資料模型:支援 Metrics 打標籤(Label),進行多維度查詢與分析
  • 單機高效能:支援消費百萬級時間序列,抓取上千個 Targets

Prometheus 專注於 Metrics(指標)Alert(告警)。它不負責 Logging(日誌)和 Tracing(鏈路追蹤)。這是使用前必須釐清的工具定位。

四種 Metric 類型#

類型特性適用場景範例
Counter (計數器)只增不減(重啟重置)累計值HTTP 請求總數、下單總數
Gauge (測量儀)可增可減,反映當前快照狀態值CPU 使用率、線上用戶數
Histogram (直方圖)基於預定義 Bucket 統計分佈分佈區間請求延遲分佈
Summary (彙總)用戶端計算百分位數精確百分位P90、P99 延遲
Histogram vs Summary 的選擇
  • Histogram:在伺服器端(Prometheus)進行聚合計算,可以跨實例合併。適合事先不確定 Percentile 需求,或需要跨多個實例聚合的場景
  • Summary:在用戶端進行計算,直接上報百分位數。計算精確但無法跨實例合併。適合單一實例且需要精確百分位的場景

一般建議優先使用 Histogram,因為它在分散式環境中更具彈性。

Metric 資料結構#

每條 Metric 包含三個核心部分:

http_requests_total{status="200", path="/api/login"} 1024
|________________| |______________________________| |___|
   Metric Name              Labels                  Value
  • Metric Name:唯一識別指標含義
  • Label:多維度標籤,每增加一個 Label 值組合就產生一個新的時間序列
  • Value:整數或浮點數值(時間戳由 Prometheus 抓取時自動生成)

Prometheus 架構#

整體架構#

flowchart TD
    subgraph Targets["採集目標"]
        App["應用程式<br/>(Client Library)"]
        Exp["Exporters<br/>(Node/Redis/...)"]
        PG["Push Gateway<br/>(短期作業)"]
    end

    subgraph SD["服務發現"]
        K8s["Kubernetes"]
        Consul["Consul"]
        DNS["DNS"]
        Static["靜態設定"]
    end

    subgraph Server["Prometheus Server"]
        Ret["Retrieval<br/>(採集模組)"]
        Store["Storage<br/>(TSDB)"]
        PQL["PromQL<br/>(查詢引擎)"]
    end

    SD --> Ret
    App --> |Pull| Ret
    Exp --> |Pull| Ret
    PG --> |Pull| Ret

    Ret --> Store
    Store --> PQL

    PQL --> Grafana["Grafana<br/>(Dashboard)"]
    PQL --> WebUI["Web UI"]
    PQL --> API["API Clients"]

    Server --> AM["Alertmanager"]
    AM --> Email["Email"]
    AM --> Wechat["企業微信"]
    AM --> Slack["Slack"]

    style Server fill:#e8f5e9
    style AM fill:#ffebee

核心模組#

模組職責
Retrieval(採集)根據服務發現結果,定期從目標端點拉取資料
Storage(儲存)將時序資料寫入本地 TSDB(建議 SSD)
PromQL(查詢)專為時間序列設計的查詢語言,支援聚合與分析
Alertmanager(告警)處理告警的去重、分組、路由與通知

PromQL 查詢語言#

PromQL(Prometheus Query Language)是 Prometheus 內建的函數式查詢語言,支援聚合運算、速率計算與告警表達式,是 Grafana Dashboard 與告警規則的核心。

範例:計算 HTTP 500 錯誤率

sum(rate(http_requests_total{status="500"}[5m])) by (path)
/
sum(rate(http_requests_total[5m])) by (path)
  • rate():計算指定時間視窗內 Counter 的增長速率
  • sum() by (label):按指定標籤進行聚合加總
  • 整體邏輯:分子為 500 錯誤的速率,分母為所有請求的速率,相除得到每個路徑的錯誤百分比

預測性告警範例:使用 predict_linear 函式預測磁碟空間是否會在 4 小時內耗盡:

alert: DiskWillFillIn4Hours
expr: predict_linear(node_filesystem_free_bytes[1h], 4 * 3600) < 0
PromQL 核心函式與運算符
類別函式/運算符用途
速率計算rate(), irate()計算 Counter 的增長速率(rate 為平均速率,irate 為瞬時速率)
聚合運算sum(), avg(), max(), min(), count()跨時間序列聚合,可搭配 by/without 指定維度
趨勢預測predict_linear()基於線性回歸預測未來值,適合容量規劃告警
直方圖統計histogram_quantile()從 Histogram 資料計算百分位數(如 P95、P99)
時間偏移offset查詢歷史時間點的資料,用於環比分析
數學運算+, -, *, /, %, ^時間序列之間的算術運算

Alertmanager 告警管理#

Alertmanager 是 Prometheus 生態中獨立的告警管理組件,負責接收 Prometheus Server 觸發的告警事件並進行後續處理:

功能說明
去重(Deduplication)多個 Prometheus 實例發送相同告警時自動合併,避免通知雜訊
分組(Grouping)將相關聯的告警(如同一叢集的多個節點故障)合併為一條通知
路由(Routing)根據告警標籤將通知分發到不同渠道(Email、企業微信、Slack)或不同接收團隊
靜默(Silencing)在維護視窗期間暫時抑制特定告警

資料採集策略#

方式說明適用場景
直接採集 (Whitebox)整合 Client SDK 埋點,暴露 Metrics 端點自研應用、雲原生組件
Exporter (Blackbox)使用匯出器將系統資料轉為 Prometheus 格式OS、資料庫、硬體設備
Push Gateway短期作業主動推送到中間閘道生命週期極短的批次作業
Federation(聯邦)從其他 Prometheus Server 拉取資料跨資料中心、分層監控

儲存機制(Storage V2)#

Prometheus 2.0 採用分塊(Chunking)與時間分片策略:

flowchart LR
    subgraph Memory["記憶體"]
        H["Head Block<br/>(Mutable)<br/>+ WAL"]
    end

    subgraph Disk["磁碟"]
        B1["Block 1<br/>(Immutable)<br/>0-2h"]
        B2["Block 2<br/>(Immutable)<br/>2-4h"]
        B3["Compacted Block<br/>(Immutable)<br/>0-6h"]
    end

    H -->|"每 2 小時<br/>Flush"| B1
    B1 -.->|Compaction| B3
    B2 -.->|Compaction| B3

    style H fill:#fff3e0
    style B3 fill:#e8f5e9
  • Head Block:當前接收的實時資料寫入記憶體,配合 WAL 防止資料遺失
  • Persisted Block:每 2 小時持久化到磁碟成為唯讀 Block
  • Compaction:背景程式將多個小 Block 合併成更大的 Block 以節省空間

高可用與擴展策略#

Prometheus 單機效能極為強勁(百萬級時間序列、上千個 Targets),但在大規模生產環境中仍需考慮高可用與水平擴展。

策略架構方式適用場景
基礎 HA部署兩台相同設定的 Prometheus 實例,抓取相同目標中小規模,防止單點故障
Federation(聯邦)上層 Prometheus 從下層拉取聚合後的資料跨資料中心、分層監控
遠端儲存透過 Remote Write 將資料寫入 Thanos/Cortex/VictoriaMetrics長期儲存、全局查詢、去重

Prometheus 本地儲存不支援叢集化複製,基礎 HA 模式下兩台實例的資料可能存在微小差異(抓取時間點不完全一致)。若需要長期儲存與全局去重查詢,建議引入 ThanosCortex 等方案,它們能在保持 Prometheus 簡潔性的同時提供全局視圖與無限期儲存。

TSDB 選型比較:Prometheus vs 其他時間序列資料庫

根據業界趨勢分析,主流的時間序列資料庫定位各異:

產品定位查詢語言儲存模式生態整合趨勢
Prometheus監控與告警專用 TSDBPromQL本地 TSDB + 遠端擴展CNCF 標準,K8s 原生高速成長
InfluxDB通用型 TSDBInfluxQL / Flux本地 TSMIoT、DevOps 場景豐富高速成長
OpenTSDB大規模分散式 TSDBHTTP APIHBase 底層Hadoop 生態趨於平穩
Graphite老牌監控 TSDBGraphite FunctionsWhisper成熟但迭代停滯逐漸老化

選型建議

  • 雲原生 / Kubernetes 環境:Prometheus 是事實標準,無需猶豫
  • IoT / 通用時序資料場景:InfluxDB 更為通用,支援更豐富的資料寫入模式
  • 已有 Hadoop 生態:OpenTSDB 可複用既有 HBase 叢集,但社群活躍度已下降
  • 僅需簡單指標展示:Graphite 仍可用,但不推薦新專案採用

生產治理實踐#

統一埋點策略#

儘量不要讓業務開發人員直接進行手工埋點。由公司的基礎架構團隊在核心底層庫中統一整合(MVC 框架、RPC 框架、Cache 組件、DAL 層),開發人員只需使用封裝好的框架庫即可。

統一埋點的實施方式:

  • 日誌整合:開發 Logback/Log4j Appender,自動將 Error 日誌發送到監控系統
  • AOP 註解式埋點:通過 Spring AOP 與自定義註解降低侵入性
  • Trace ID 關聯:在應用日誌中注入 Trace ID,實現日誌系統與追蹤系統的打通

技術債識別#

架構師應定期利用監控報表識別系統中的技術債:

  • 依賴關係分析:檢視跨服務呼叫報表,關注是否出現循環依賴或非核心路徑的過度呼叫
  • N+1 查詢偵測:透過 Transaction 視圖識別迴圈中逐筆執行資料庫查詢的問題
  • 效能瓶頸定位:分析平均延遲與 P95 延遲,找出長尾延遲的服務

N+1 查詢會導致資料庫壓力激增且響應時間變長。監控系統的 Transaction 視圖可以輕易識別出此特徵(同一 SQL 在短時間內被大量重複呼叫),一旦發現必須立即整改。

紅黑榜機制#

通過「透明化」資料督促團隊改進效能:

榜單內容意義
平均延遲黑榜全公司回應速度最慢的 Top 20 服務識別效能最差的服務
P95 延遲黑榜95% 請求的延遲分佈反映服務穩定性,識別長尾延遲
紅黑榜的執行策略
  • 每週統計並以郵件發送全體研發或相關主管
  • 榜單包含:域名、所屬部門、負責人、當前延遲、過去四週排名趨勢
  • 對長期霸榜且無改善的服務,透過架構委員會介入強制整改
  • 實際成效:推行初期許多服務延遲在秒級以上,經過治理後大部分降至 200ms 以下

紅黑榜不是為了懲罰,而是為了建立「關注效能」的工程文化,讓開發人員對程式碼品質有具體的感知。

測量驅動開發(MDD)#

MDD(Metrics Driven Development) 主張整個應用開發過程由度量指標來驅動,將業務、開發、運維三方通過 Metrics 連結起來。

flowchart LR
    B["Build<br/>(建置)"] --> M["Measure<br/>(測量)"]
    M --> L["Learn<br/>(學習)"]
    L --> B

    style B fill:#e3f2fd
    style M fill:#fff3e0
    style L fill:#e8f5e9

MDD 的核心原則:

  • 開發功能前先定義指標:如同 TDD 先寫測試,MDD 要求三方共同定義核心度量指標
  • 開發人員自助埋點:工具必須低門檻,支持開發者方便地埋入業務與效能指標
  • Single Source of Truth:監控系統中的 Metrics 應成為團隊溝通與決策的唯一事實依據
  • 基於度量進行決策:功能是否保留、系統是否擴容,全憑資料說話

MDD 帶來的價值:

  • 對產品經理:獲取實時生產資料,踐行資料驅動決策
  • 對開發人員:實時感知生產狀態,聚焦真正需要改進的痛點
  • 對運維人員:透過應用層 Metrics 快速定位問題根因,打破黑盒

監控生態的整合策略#

工具定位#

不要試圖用單一工具解決所有監控問題。各工具應各司其職,形成互補:

領域推薦工具定位
日誌 (Logging)ELK Stack離散事件記錄與檢索
追蹤 (Tracing)CAT / SkyWalking呼叫鏈追蹤與 APM 報表
指標 (Metrics)Prometheus + Grafana時間序列指標與告警
健康檢查 (Health)Prometheus Blackbox服務存活與連通性探測

整合方式#

  • Trace ID 貫穿:在日誌中注入 Trace ID,實現 ELK 與追蹤系統的雙向跳轉
  • Error 日誌收斂:將錯誤日誌同時發送到 ELK 和追蹤系統(如 CAT Problem 視圖)
  • Metrics 統一暴露:應用透過 /metrics 端點同時服務 Prometheus 與其他系統
  • 告警分層:追蹤系統處理 Transaction/Event 告警,Prometheus 處理指標閾值告警

對於中小團隊,CAT 跨越了 Tracing 和 APM 的邊界,是性價比很高的「一站式」起步方案。隨著規模增長,再逐步引入 Prometheus 處理指標監控、ELK 處理日誌檢索,形成完整的可觀測性體系。