English

實驗室:OpenTelemetry 的定位 — Collector 坐在哪一層

OpenTelemetry 不是儀表板,也不是資料庫。它是坐在 Prometheus、Tempo、Grafana 上游 的一層:與廠商無關的儀器化(instrumentation)、傳輸、以及路由。這個實驗室只交給你「一個」config 決策,就是為了讓這個定位變得具體可感。

執行模式:local-mock(Docker Compose) 預估時間:45–75 分鐘 難度:中階 涉及概念:4 個

這個實驗室在證明什麼問題

「用 OpenTelemetry 就好」跟「用 Prometheus + Grafana 就好」常常在同一句對話裡被講出來,讓人誤以為兩者是互相競爭的選項。其實不是 — 它們是不同的「層」。把它們混為一談,正是為什麼「為什麼我的 Prometheus + Grafana 組合看不到 trace」會是個結構性、無解的問題:那個技術組合裡根本沒有任何東西能存 trace。

最少需要具備的概念:

系統架構 — 五層,一張圖

OpenTelemetry 五層定位圖 應用程式透過 OpenTelemetry API 與 SDK 建立 telemetry,以 OTLP 格式送到 Collector;Collector 把 metrics pipeline 路由到 Prometheus、把 traces pipeline 路由到 Tempo。Grafana 只對兩個後端做唯讀查詢,從不直接接收 OTLP。 ① 儀器化 ② 傳輸 ③ 路由 ④ 儲存+查詢 ⑤ 視覺化 你的 App OTel API + SDK — 建立 span 與一個 counter OTLP / gRPC+HTTP OpenTelemetry Collector metrics pipeline receiver: otlp processor: batch exporter: prometheus traces pipeline receiver: otlp processor: batch exporter: otlp/tempo ↑ 你的核心任務:service.pipelines 的 exporter 連線 ↑ Prometheus metrics 儲存 + PromQL 查詢 Tempo trace 儲存 + TraceQL 查詢 唯讀查詢(2 個 datasource 外掛) Grafana 只負責視覺化 — 自己不儲存任何 telemetry Logs(第三種訊號):模式完全相同 — 加一個 logs pipeline + loki exporter。這裡沒有實作,見下方「範圍」。
metrics 路徑 traces 路徑 OTel 負責的範圍(儀器化、傳輸、路由) 視覺化,唯讀

這張圖要讓兩件事變得無可否認:(1) App 從來不會指名 Prometheus、Tempo 或 Grafana — 它只知道 OTLP 和一個 Collector 的位址;(2) Grafana 完全落在分界線下方,只從兩個它從不寫入的後端讀資料。「你的核心任務」標記以上的一切都屬於 OTel。metrics/traces 分岔以下的每一個方塊,都是 OTel 路由進去、而不是取代掉的單一訊號專用後端。

每個工具負責什麼 — 責任邊界在哪裡交接

層 / 工具負責什麼不負責什麼
App + OTel SDK透過廠商中立的 API 建立 span 與 metric instrument;編碼並送出 OTLP不知道 telemetry 最後會進哪個後端 — 那是 Collector config 的事,App 程式碼完全不用管
OTLP提供共通的傳輸格式,讓任何 OTel SDK 都能對任何支援 OTLP 的 receiver 說話不負責「意義」— 兩個系統可以成功交換合法的 OTLP bytes,卻仍然對某個欄位的意思有分歧,除非共用同一套 semantic conventions
OpenTelemetry Collector接收 OTLP,再透過具名的 service.pipelines 把每種訊號類型路由到為它而建的 exporter不負責長期儲存或查詢 telemetry — 它是路由器/處理器,不是資料庫
Prometheus只儲存並查詢 metrics,透過 pull 的方式向 Collector 的 prometheus exporter 頁面抓取不負責 trace 或 log — 它的 exporter 元件根本沒有 traces/logs 的 consumer(見下方失敗案例)
Tempo只儲存並查詢 traces,直接以 OTLP 從 Collector 接收不負責 metrics 或儀表板
Grafana透過各自獨立的 datasource 外掛查詢 Prometheus 與 Tempo,渲染出面板/Explore 畫面不接收 OTLP、不儲存 telemetry、也不做任何路由決策 — 資料在進入後端「之前」是怎麼來的,Grafana 完全不在意

學習者的步驟與檢查點

  1. 先讀懂 scaffold — 從 collector/otel-collector-config.yaml 開始,再快速瀏覽 app/server.js。
  2. 在動手跑之前,先把 5 個檢查點的預測 寫下來。
  3. 在 service.pipelines 下把兩行 exporters: [] 填好 — 這是你唯一要編輯的地方。
  4. 快速檢查:./scripts/validate-config.sh — 不需要整套 stack,填對了就會 exit 0。
  5. 完整執行:./scripts/run.sh — 啟動 app、Collector、Prometheus、Tempo、Grafana。
  6. 觀察:在 Grafana 透過 Tempo datasource 看到一筆 trace,也透過 Prometheus datasource(以及 Prometheus 自己的介面)看到持續上升的計數器。
  7. 執行失敗案例,讀懂錯誤訊息,再把你的 config 復原成可運作的版本。

每個階段的可觀察訊號:validator 的 exit code 與錯誤訊息文字、docker compose ps 的容器健康狀態、Prometheus 自己的查詢介面,以及 Grafana Explore 對兩個 datasource 的畫面。

失敗案例 — 該檢查什麼(不是該怎麼修)

把 traces 接到 prometheus exporter

有一份預先寫好、刻意寫錯的 config,把 traces pipeline 接到 prometheus exporter,而不是 otlp/tempo。用 ./scripts/failure-case.sh 執行它。Collector 並不會先啟動、再默默把 trace 丟掉 — 它根本不會啟動:在任何 receiver port 打開之前就直接失敗,並以非零結束碼在 stderr 印出一則 exporter/訊號不相容的錯誤。

該檢查什麼:那則錯誤訊息的確切文字,以及為什麼它發生在「啟動當下」,而不是事後才變成「怎麼一直沒有 trace 進來」這種現象。這個時間點正是重點 — 對 prometheus 這種訊號受限的 exporter 而言,訊號與後端配錯是一個「config 建置階段」的失敗,不是一個會默默流失資料的執行期 bug。

確切的錯誤文字,以及更深一層「這個失敗是不是雙向對稱」的細節,都放在這個實驗室的私有解答(不公開)裡 — 請在自己跑過一次、並且跑過 lab-review.md 之後再對答案。

驗收標準

前置需求、時間、成本與範圍

需要Docker + Docker Compose(v2 的 docker compose 語法)。不需要雲端帳號、不需要 API key、沒有費用 — 全部在本機執行。
預估時間45–75 分鐘。
成本風險沒有 — 全部是本機容器,不涉及計費 API。
沒有 Docker 時的退路當本機沒有 Docker 時,./scripts/validate-config.sh 會用一個獨立的 otelcol-contrib執行檔(可自動下載、不需要 root 權限)來驗證你的 service.pipelines 連線 — 雖然少了完整的 Grafana / Prometheus / Tempo 體驗,但「我的路由設定到底建不建得起來」這個核心回饋迴圈還是拿得到。
為什麼 logs 和 OTTL 不是第二個必做任務

Logs 是 OTel 的第三種訊號,會遵循這裡教的完全相同模式:一個 loki exporter 定義,加上一個 logs: { receivers: [otlp], exporters: [loki] } pipeline — 同一份檔案、同樣的形狀,跟你已經接好的那兩個一模一樣。如果把一個可執行的 Loki 後端也塞進這個實驗室,篇幅會膨脹成三倍,卻只是在教一個你已經證明會了兩次的模式。OTTL(宣告式的 pipeline 內轉換 — 例如在資料經過時遮蔽或改寫欄位)則是完全不同的關注點:它改變的是「pipeline 裡面裝了什麼」,不是「訊號走哪一條 pipeline」。等路由這一課學會之後,這兩者都只是一段話就能講完的延伸,不需要另外變成核心任務。