OpenTelemetry在企業內部應用所需要的技術棧
可觀測性概念
當一個軟件或系統出于運行狀態時,如果我們不對他加以觀測,那它的運行狀態對我們來說就是一個黑盒。
如上圖所示。
我們只能通過業務的表象來判斷它是否正常運行,無法在故障發生前進行預判,從而只能被動解決問題。
這類問題在微服務時代體現的更加明顯,即便是業務已經出現問題,在沒有可觀測性系統的前提下想要定位問題更是難上加難。

好在可觀測性這個概念由來已久,已經由一些業界大佬抽象出幾個基本概念:
- Logs:離散的日志信息
- Metrics:聚合的指標
- Trace:請求基本的鏈路追蹤
結合這三個指標,我們排查問題的流程一般如下:

首先根據 metrics 來判斷是否有異常,這點可以通過在 Prometheus 的 AlertManager 配置一些核心的告警指標。
比如當 CPU、內存使用率超過 80% 或者某個應用 Down 機后就發出告警。
groups:
- name: AllInstances
rules:
- alert: InstanceDown
# Condition for alerting
expr: up == 0
for: 1m
# Annotation - additional informational labels to store more information
annotations:
title: 'Instance {{ $labels.instance }} down'
description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minute.'
# Labels - additional labels to be attached to the alert
labels:
severity: 'critical'
這可以讓我們盡早發現故障。
之后我們可以通過鏈路信息找到發生故障的節點。

然后通過這里的 trace_id 在應用中找到具體的日志:
mdc.trace_id:4a686dedcdf4e95b1a83b36e62563a96再根據日志中的上下文確定具體的異常原因。
這就是一個完整的排查問題的流程。
OpenTelemetry 發展歷史


在 OpenTelemetry 開始之前還是先回顧下可觀測性的發展歷史,其中有幾個重要時間點:
- 2010 年 Google 發布了 Dapper 論文,給業界帶來了實現分布式追蹤的理論支持,之后的許多分布式鏈路追蹤實現都有它的影子
- kubernetes 的發布奠定了后續云原生社區的基礎
- Jaeger 發布后成為了主流的鏈路存儲系統
- 2019 年 OpenTracing 和 OpenCensus 合并為 OpenTelemetry
- 2021 年底 OpenTelemetry 發布第一個 GA release 版本
OpenTelemetry 是什么?

以前我們所接觸到的類似于阿里的ARMS、美團的 CAT、Pinpoint 這類系統大多都有一個公司在背后進行驅動,與廠商綁定的非常緊密。
而 OpenTelemetry 則相反,它主要由社區驅動,參與的公司眾多;同時它定義和提供了一套可觀測性的標準(包括 API、SDK、規范等數據)。
使用它你可以靈活的選擇和搭配任意的開源或商業產品來組成你的可觀測性技術棧。

因為社區非常活躍,所以當前也幾乎支持主流的開發語言。
OpenTelemetry 的架構

OpenTelemetry 的架構主要分為三個部分:
- 左側的客戶端 Agent,用于采集客戶端的數據,通常就是我們的應用。
- 中間的是 Collector-Service,用于接受客戶端的數據、內部處理、導出數據到各種存儲
- 右側的則是各種存儲層,用于存儲 Metrics、Logs、Traces 這些數據。
我們基于官方推薦的技術架構選型了我們的技術棧:

主要的區別就是使用 VictoriaMetrics 存儲指標、StackRocks 存儲 Trace,ElasticSearch 存儲日志。
只是目前我們的日志鏈路還沒有完全切換到 OpenTelemetry 的鏈路,依然是在 Pod 中掛載了一個 sidecar,在這個 sidecar 中通過 filebeat 采集日志輸出到 elasticsearch,后續也會逐步遷移。
核心項目
Collecotor
OpenTelemetry 社區的項目眾多,其中大部分都是各種語言的 SDK 和 API,其中最為關鍵的應該就是 opentelemetry-collector
也就是剛才架構圖中的中間部分,我們可以把它理解為類似 APIGateway 的角色,所有上報的 OTel 數據都得經過它的處理。

主要由以下三部分組成:
- Receiver:用于接受客戶端上報的數據
- Process:內部的數據處理器
- Exporter:將數據導出到不同的存儲
由于 OpenTelemetry 社區非常的活躍,所以這里支持的 Receiver、Processor 和 Exporter 類型非常多。



其他核心項目
我們以 Java 為例,對業務開發最重要的庫就是 opentelemetry-java-instrumentation
它可以打包一個 javaagent 給我們使用:
# Java example
java -javaagent:path/to/opentelemetry-javaagent.jar \
-jar myapp.jar
同時也支持了我們日常開發的絕大多數框架和中間件。
支持的庫與框架列表
如果我們需要在應用中自定義打樁一些 Span、Metrics ,就還需要 opentelemetry-java 這個項目。
它提供了具體的 SDK 可以方便的創建 Span 和 Metrics。
Trace
之后來看看 OpenTelemetry 中具體的三個維度的概念和應用,首先是 Trace。

Trace 這個概念首先是 Google Dapper 論文中提到。
如上圖所示:一次用戶請求經歷了 4 次 PRC 調用,分別也屬于不同的系統。
每一次 RPC 調用就會產生一個 Span,將這些 span 串聯起來就能形成一個調用鏈路。
這個 Span 主要包含以下信息:
- SpanName
- ParentID
- SpanID
當我們將一個 Span 放大后會看到更加具體的信息:
- TraceId
- SpanName
- ParentID
- SpanID
- 開始時間
- 結束時間 在 Dapper 論文中使用 Annotations 來存放 span 的屬性,當然也可以自定義存放一些數據,比如圖中的
"foo"。
在 OpenTelemetry 的 SDK 中稱為 attribute,而在 Jaeger 的 UI 中又稱為 tag,雖然叫法不同,但本質上是一個東西。
最終就會形成上圖中的樹狀結構的調用關系。
Span Kind

Span 中還有一個非常重要的概念,就是 Span Kind,也就是 Span 的類型,這個類型可以在排查問題時很容易得知該服務的類型。
按照官方的定義,Span 的類型分為:
- Client
- Server
- Internal
- Producer
- Consumer
對于 RPC 的客戶端和服務端自然就對應 Client 和 Server,而使用了消息隊列的生產者消費者對應的就是 Produce 和 Consumer。
除此之外發生在應用內部的一些關鍵 Span 的類型就是 Internal,比如我們需要對業務的某些關鍵函數生成 Span 時,此時的 Span 類型通常也都是 Internal。
上下文傳遞

在 Trace 中有一個關鍵技術問題需要被解決,也就是 Context 的上下文傳遞。
這個特別是在分布式系統中必須要解決,我們可以簡單把它理解為如何把上游生成的 trace_id 傳遞到下游,這樣才能在追蹤的鏈路追蹤系統中串聯起來。
這個關鍵的技術名詞在 OpenTelemetry 中稱為:Context Propagation.
在分布式系統中,數據都是通過網絡傳遞的,所以這里的本質問題依然是如何將上下文數據序列化之后,在下游可以反序列化到 Context 中。
聰明的小伙伴應該已經想到,我們可以將 trace_id 寫入到跨進程調用的元數據中:
- http 可以存放在 http header 中
- gRPC 可以存放在 meta 中
- Pulsar 可以存放在消息的 properties 中
- 其余的中間件和框架也是同理
然后在遠程調用之前使用 Inject 將數據注入到這些元數據里,下游在接收到請求后再通過一個Extract 函數將元數據解析到 Context 中,這樣 trace_id 就可以串聯起來了。


上圖就是 Pulsar 和 gRPC 傳遞 trace_id 的過程,數據都是存放在元數據中的,這里的 traceparent 的值本質上就是 trace_id.
具體的代碼細節我會在下一篇繼續分析。
Metrics

Metrics 相對于 Trace 來說則是要簡單許多,OpenTelemetry 定義了許多命名規范和標準,這樣大家在復用社區的一些監控模板時就要更加容易一些。
Metrics Exemplars

Metrics 還提供了一個 Exemplar 的功能,它的主要作用是可以將 Metrics 和 Trace 關聯在一起,這樣在通過 Metrics 發現問題時,就可以直接跳轉到鏈路系統。
因為 trace_id 可以通過 MDC 和日志關聯,所以我們可以直接通過 Metrics 定位具體應用的日志,這樣排查問題的效率將會非常高。
擴展信息
以上就是關于 OpenTelemetry 的整體架構,下面來擴展一些內容。
eBPF

eBPF 是一個運行在 Linux 內核中的虛擬機,它提供一套特殊的指令集并允許我們在不重新編譯內核、也不需要重啟應用的情況下加載自定義的邏輯。
eBPF 技術具有三大特點:
- 第一是無侵入,動態掛載,目標進程無需重啟,而且因為是 Linux 內核提供功能,所以與語言無關,任何語言都可以支持。
- 第二是高性能,eBPF 字節碼會被 JIT 成機器碼后執行,效率非常高;
- 第三是更加安全,它會運行在自己的沙箱環境中,不會導致目標進程崩潰。
eBPF 雖然有很多優點,同時也有一些局限性,比如我想監控業務代碼中的某個具體指標(訂單創建數量),此時它就難以實現了,所以還得看我們的應用場景。更適合一些云平臺,或者更偏向底層的應用。
目前 eBPF 的應用場景還不夠廣泛,但假以時日一定會成為可觀測領域的未來之星。
SigNoz
不知道大家發現沒有,如果我們直接 OpenTelemetry 技術棧會需要為 Trace、Metrics、Logs 選擇不同的存儲,而且他們的查詢界面也分散在不同的地方。
那有沒有一個統一的平臺可以給我們提供完整的可觀測體驗呢?
有這樣的需求那就有對應的廠商實現了:

SigNoz 就是這樣的平臺,它將 OpenTelemetry-collector 和數據存儲全部整合在了一起,同時全面兼容 OpenTelemetry;可以說它就是基于 OpenTelemetry 構建的一個可觀測產品。
對于一些中小廠商,不想單獨維護這些組件時是非常有用的。
OpenObserve

OpenObserve在 SigNoz 的基礎上做的更加極致一些,它提供了一個統一的存儲可以存放日志、Trace、Metrics 等數據。
這樣我們就可以只使用一個數據庫存放所有的數據,同時它也提供了完整的 UI,并且也全面兼容 OpenTelemetry。
這樣對于運維來說會更加簡單,只是可能帶來的副作用就是需要與它完全綁定。
總結
以上就是 OpenTelemetry 在企業的應用,大家可以根據自己的情況選擇自建 OTel 的技術棧,還是選擇 SigNoz 和 OpenObserve 這類的標準化產品。




















