OpenTelemetry 分散式追蹤入門教學:微服務可觀測性從零開始的完整指南
什麼是 OpenTelemetry?
當你的系統從單體架構拆分成十幾個微服務之後,某天使用者回報「結帳流程變慢了」,你該從哪裡開始查起?這就是我第一次真正理解可觀測性重要性的時刻。
OpenTelemetry(簡稱 OTel)是 CNCF 旗下的開源可觀測性框架,它提供了一套統一的 API、SDK 和工具,讓開發者能夠產生、收集和匯出 Telemetry 資料——包括 Traces(追蹤)、Metrics(指標)和 Logs(日誌)。在此之前,你可能需要同時整合 Jaeger、Prometheus、ELK 等不同工具的 SDK,每個都有自己的資料格式。OTel 的出現徹底改變了這個局面,它用一套標準打通了所有的可觀測性需求。
如果你正在建置微服務架構入門的系統,那 OpenTelemetry 幾乎是你遲早需要面對的技術。與其等到出了問題才被迫學習,不如現在就開始建立正確的可觀測性基礎。
可觀測性的三大支柱
可觀測性建立在三大支柱之上,每一個都扮演不同的角色:
Traces(分散式追蹤)
Trace 記錄了一個請求從進入系統到完成的完整路徑。每個 Trace 由多個 Span 組成,每個 Span 代表一個操作單元——例如一次 HTTP 呼叫、一次資料庫查詢、或是一次訊息佇列的發送。透過 Trace,你能清楚看到請求在哪個服務花了最多時間,以及服務之間的呼叫關係。
Metrics(指標)
Metrics 是數值化的量測資料,通常以時間序列的形式儲存。常見的 Metrics 包含請求數量(Counter)、回應時間分佈(Histogram)、以及當前連線數(Gauge)。比起 Traces,Metrics 的資料量小得多,適合長期保存和趨勢分析。
Logs(日誌)
Logs 是最傳統的觀測手段,記錄離散的事件資訊。OpenTelemetry 的價值在於將 Logs 與 Traces 關聯起來——當你在 Jaeger 上看到一個異常的 Span,可以直接跳轉到對應的 Log 記錄,大幅縮短除錯時間。
我的經驗是,先把 Traces 做好,再逐步加入 Metrics 和 Logs。一開始就想三者全上會讓專案複雜度暴增,反而欲速則不達。
分散式追蹤的核心概念
在深入實作之前,有幾個核心概念必須先搞清楚:
Trace Context Propagation(追蹤上下文傳播)是分散式追蹤能夠運作的關鍵機制。當服務 A 呼叫服務 B 時,需要透過 HTTP Header(通常是 traceparent)將 Trace ID 和 Span ID 傳遞過去,這樣服務 B 才知道自己產生的 Span 屬於哪個 Trace。W3C Trace Context 是目前最廣泛採用的標準格式。
Sampling(取樣策略)決定了哪些 Trace 會被實際記錄。在高流量的生產環境中,記錄每一個請求的 Trace 既不經濟也不必要。常見的策略包括固定比例取樣(例如只記錄 10% 的請求)和尾部取樣(根據 Trace 完成後的特徵決定是否保留,例如只保留有錯誤的 Trace)。
Instrumentation(儀器化)指的是在程式碼中加入產生 Telemetry 資料的邏輯。OpenTelemetry 提供兩種方式:自動儀器化(Auto Instrumentation)能自動攔截常見的 HTTP、gRPC、資料庫呼叫;手動儀器化則讓你對業務邏輯的關鍵節點做更精確的標記。
實戰:Node.js 應用程式接入 OpenTelemetry
讓我們用一個實際的 Node.js Express 應用來示範如何接入 OpenTelemetry。首先安裝必要的套件:
npm install @opentelemetry/sdk-node \
@opentelemetry/auto-instrumentations-node \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/exporter-metrics-otlp-http建立 tracing.js 初始化檔案,這個檔案必須在應用程式的最開頭被引入:
const { NodeSDK } = require("@opentelemetry/sdk-node");
const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-http");
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "http://localhost:4318/v1/traces",
}),
instrumentations: [getNodeAutoInstrumentations()],
serviceName: "order-service",
});
sdk.start();
console.log("OpenTelemetry tracing initialized");接著在你的 package.json 中修改啟動指令:
"scripts": {
"start": "node --require ./tracing.js app.js"
}就這麼簡單,自動儀器化會攔截 Express 的路由處理、HTTP 外部呼叫、甚至是資料庫查詢(如果你使用了支援的 ORM)。每個進來的請求都會自動產生一個 Span,你完全不需要改動任何業務邏輯程式碼。
如果你需要在特定業務邏輯中加入自訂 Span,可以使用手動儀器化:
const { trace } = require("@opentelemetry/api");
const tracer = trace.getTracer("order-service");
async function processPayment(orderId, amount) {
return tracer.startActiveSpan("process-payment", async (span) => {
span.setAttribute("order.id", orderId);
span.setAttribute("payment.amount", amount);
try {
const result = await paymentGateway.charge(amount);
span.setAttribute("payment.status", "success");
return result;
} catch (error) {
span.setStatus({ code: 2, message: error.message });
throw error;
} finally {
span.end();
}
});
}這種混合使用自動和手動儀器化的方式,在實務上是最常見的做法。自動儀器化處理基礎設施層的追蹤,手動儀器化則專注在業務邏輯的關鍵節點。
OpenTelemetry Collector 架構與設定
OpenTelemetry Collector 是整個架構中的核心元件,它負責接收、處理和匯出 Telemetry 資料。你可以把它想成一個中間的資料管道,應用程式只要把資料送給 Collector,至於最終要送到 Jaeger、Prometheus 還是其他後端,都由 Collector 的設定決定。
如果你已經熟悉Docker 容器化部署教學的內容,那用 Docker Compose 來部署 Collector 會非常直覺。以下是一個基本的 otel-collector-config.yaml:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 5s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
exporters:
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]Collector 的架構分為三個部分:Receivers 負責接收資料(支援 OTLP、Zipkin 等格式)、Processors 負責處理資料(批次處理、記憶體限制、取樣等)、Exporters 負責匯出到後端系統。這種管道式的設計非常靈活,你可以輕鬆地切換後端而不需要改動應用程式。
Jaeger 與 Grafana 視覺化追蹤資料
收集到的 Trace 資料需要視覺化才能發揮價值。Jaeger 是最常搭配 OpenTelemetry 使用的追蹤後端,它提供了直覺的 UI 來查看 Trace 的瀑布圖、比較不同 Trace 的差異、以及分析服務間的依賴關係。
用 Docker Compose 快速搭建完整的觀測環境:
services:
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686"
- "4317:4317"
environment:
- COLLECTOR_OTLP_ENABLED=true
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
volumes:
- ./otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
ports:
- "4318:4318"
depends_on:
- jaeger
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true如果你的微服務運行在Kubernetes 入門教學介紹的 K8s 環境中,也可以透過 Helm Chart 來部署這套觀測堆疊。
Grafana 的強項在於將 Traces、Metrics 和 Logs 整合在同一個儀表板中。透過 Grafana Tempo 作為 Trace 後端,你可以直接在 Grafana 中從 Metrics 的異常點擊穿到相關的 Traces,再從 Traces 跳轉到 Logs,這種三合一的除錯體驗非常流暢。
生產環境的最佳實踐
在開發環境把 OpenTelemetry 跑起來不難,但要在生產環境穩定運行,有幾個重點必須注意:
取樣策略要謹慎設計。我建議從 Tail-based Sampling 開始——在 Collector 層面根據 Trace 的特徵(例如是否包含錯誤、延遲是否超過閾值)來決定是否保留。這樣既能控制成本,又不會遺漏重要的異常 Trace。
Resource Attributes 要標準化。每個服務都應該設定 service.name、service.version、deployment.environment 等標準屬性,這些會出現在每一筆 Telemetry 資料中,是查詢和過濾的基礎。
Collector 要做高可用部署。生產環境中,Collector 應該以 Gateway 模式部署,搭配負載均衡和多副本。每個節點上也可以部署 Agent 模式的 Collector 做第一層的緩衝和預處理。
注意效能開銷。自動儀器化雖然方便,但在高流量場景下可能會帶來 3-5% 的延遲增加。如果對延遲極度敏感,可以考慮只對關鍵路徑使用手動儀器化,並調低取樣率。
設定告警規則。光有可觀測性資料還不夠,你需要在 Grafana 中設定告警規則——例如當某個服務的 P99 延遲超過 500ms、或是錯誤率超過 1% 時自動通知團隊。
常見問題與疑難排解
Q:自動儀器化沒有產生任何 Span?
最常見的原因是 tracing.js 沒有在應用程式的最開頭被載入。自動儀器化需要在其他模組被 require 之前完成 monkey-patching。確認你使用了 --require ./tracing.js 的方式啟動。
Q:Trace 資料斷掉了,跨服務的 Span 沒有串聯?
檢查 Trace Context Propagation 是否正確。如果你使用了自訂的 HTTP Client 或訊息佇列,可能需要手動注入和提取 Context。確認 traceparent Header 有正確傳遞。
Q:Collector 記憶體持續增長?
確認有設定 memory_limiter Processor,並且放在 Pipeline 的最前面。另外檢查 Batch Processor 的 send_batch_size 和 timeout 設定,避免批次太大導致記憶體壓力。
Q:要如何控制 Telemetry 資料的儲存成本?
善用取樣策略和資料保留政策。Traces 通常保留 7-14 天就夠了,Metrics 可以透過降採樣保留更長時間。Logs 則根據等級設定不同的保留天數——ERROR 保留 30 天,INFO 保留 7 天。
繼續閱讀
Docker 容器化部署入門教學:後端工程師必學的環境打包術
「在我電腦上明明可以跑啊!」如果你也說過這句話,那你一定要學 Docker。這篇從零帶你搞懂容器化概念,寫出你的第一個 Dockerfile。
相關文章
Docker 容器化部署入門教學:後端工程師必學的環境打包術
「在我電腦上明明可以跑啊!」如果你也說過這句話,那你一定要學 Docker。這篇從零帶你搞懂容器化概念,寫出你的第一個 Dockerfile。
Kubernetes K8s 入門完整教學:從 Pod 到部署微服務應用的完整指南
Kubernetes(K8s)是現代雲端部署的標準工具,但入門曲線陡峭讓很多工程師望而卻步。本文從最基本的概念開始,用清楚的類比解釋 Pod、Service、Deployment 的關係,再帶你一步一步部署一個包含前端、後端和資料庫的微服務應用,讓你真正理解 K8s 的威力。
你可能也喜歡
探索其他領域的精選好文