聊聊可觀測(cè)性之?dāng)?shù)據(jù)模型
在 201x 年,隨著容器技術(shù)的出現(xiàn),容器的部署方式逐漸被各大互聯(lián)網(wǎng)公司采用,相比物理機(jī)/虛擬機(jī),容器的好處是環(huán)境隔離、輕量、快速。
但是管理容器是一件復(fù)雜的事情,后來出現(xiàn)了 Kubernetes,成為了事實(shí)上的容器管理標(biāo)準(zhǔn),目前各大公司都在使用 Kubernetes。
因?yàn)槿萜骱?Kubernetes 降低了服務(wù)(應(yīng)用)的部署和升級(jí)成本,所以催生了「微服務(wù)」的概念,服務(wù)從「單體復(fù)雜服務(wù)」向「多個(gè)簡單服務(wù)」演變,在之前,需要著重考慮服務(wù)內(nèi)的架構(gòu)設(shè)計(jì),單個(gè)服務(wù)對(duì)外提供盡可能多的能力,而在微服務(wù)中,會(huì)直接把單個(gè)服務(wù)拆分成多個(gè)服務(wù),服務(wù)之間用 API 調(diào)用。
這里也可以看到,在微服務(wù)中,架構(gòu)設(shè)計(jì)的重要性降低,API 設(shè)計(jì)的重要性提高。
另外,拆分出微服務(wù)后,編程的難度事實(shí)上降低了,對(duì)編程人員的要求也降低了。
這說明一個(gè)事實(shí),隨著基礎(chǔ)設(shè)施的不斷發(fā)展,會(huì)有越來越多的「編程能力」沉淀成基礎(chǔ)設(shè)施,使編程的難度不斷降低:軟件開發(fā)不斷向簡單的方式發(fā)展。
但是,隨著微服務(wù)的發(fā)展,服務(wù)變得太多了,管理負(fù)責(zé)度又上升了,比如怎么去解決服務(wù)發(fā)現(xiàn)的問題、怎么控制流量、服務(wù)之間怎么做隔離,服務(wù)狀態(tài)怎么觀測(cè)等等。這時(shí)候又出現(xiàn)了「服務(wù)治理」的概念,關(guān)于服務(wù)治理,有一個(gè)新的詞:Service Mesh,現(xiàn)在事實(shí)標(biāo)準(zhǔn)是 Istio。
概述
可觀測(cè)性是為了應(yīng)對(duì)微服務(wù)的復(fù)雜場(chǎng)景下發(fā)明出來的一個(gè)詞,本質(zhì)上是為了衡量系統(tǒng)運(yùn)行的狀態(tài),可觀測(cè)性是服務(wù)治理的一個(gè)維度,和功能性、可測(cè)試性、可運(yùn)維性一樣。
一般常說可觀測(cè)性包含三個(gè)度量角度:Metric、Logging、Tracing,其實(shí)還有一個(gè):Profiling。
Metric:指標(biāo),對(duì)系統(tǒng)中某一類信息的聚合統(tǒng)計(jì),比如 QPS、延遲、錯(cuò)誤率等。 Logging:日志,對(duì)系統(tǒng)所做行為的一種記錄,它是離散的,沒有相關(guān)性,為了區(qū)分這種記錄的重要程度,會(huì)分級(jí)別(DEBUG、INFO、WARN、ERROR、FATAL)。 Tracing:調(diào)用鏈,它反映的是請(qǐng)求經(jīng)過某個(gè)組件的運(yùn)行情況,經(jīng)過組件的數(shù)據(jù)叫做 Span,Span 可以體現(xiàn)經(jīng)過組件的狀態(tài)、一些關(guān)鍵屬性和事件、上下文信息。Span 之間通過 Trace ID 關(guān)聯(lián)。 Profiling:一般叫做 Continuous Profiling,持續(xù)分析,它反映的是程序內(nèi)部的運(yùn)行狀態(tài),比如棧調(diào)用、執(zhí)行時(shí)間等??梢园?Profiling 可視化成火焰圖方面分析問題。
一般來說,基于這些度量處理故障的流程是:Metric → Tracing → Logging → Profiling
根據(jù) Metric 配置的告警策略發(fā)現(xiàn)問題,基于 Tracing 查看是哪個(gè)組件出問題,基于 Logging 查看組件的日志,Profiling 分析組件具體的故障或性能問題。
數(shù)據(jù)模型
在 Tracing 領(lǐng)域,之前有兩個(gè)項(xiàng)目,一個(gè)是 OpenTracing,它是一個(gè)規(guī)范,Jaeger 就是基于 OpenTracing 的開源實(shí)現(xiàn),另一個(gè)是 OpenCensus,它是 Google 開源的度量工具。這兩個(gè)項(xiàng)目功能高度重合,在 CNCF 主導(dǎo)下合并成了 OpenTelemetry,而 OpenTracing 和 OpenCensus 也不再維護(hù)。
當(dāng)然 OpenTelemetry 不止做 Tracing,還覆蓋 Metric 和 Logging,它的目標(biāo)是統(tǒng)一可觀測(cè)性的標(biāo)準(zhǔn)協(xié)議,包括數(shù)據(jù)模型、API 規(guī)范、多語言 SDK、采集器。
OpenTelemetry 只做統(tǒng)一的協(xié)議和規(guī)范,具體數(shù)據(jù)的后端存儲(chǔ)和展示不是它的范圍。協(xié)議和規(guī)范是可觀測(cè)性對(duì)外暴露的「接口」,它的統(tǒng)一對(duì)于使用方來說是巨大的好處,目前來看,OpenTelemetry 未來會(huì)成為事實(shí)標(biāo)準(zhǔn)。
為了對(duì)接不同的后端實(shí)現(xiàn),OpenTelemetry 提供了各種 Exporter,比如為對(duì)接 Prometheus 提供了 Prometheus Exporter,對(duì)接 Cortex 和 Thanos 提供了 Prometheus Remote Write Exporter,對(duì)接 Loki 提供了 Loki Exporter,對(duì)接 Jaeger 提供了 Jaeger gRPC Exporter。
不過,目前 OpenTelemetry 還不成熟,本文的數(shù)據(jù)模型基于我們事實(shí)上使用的后端實(shí)現(xiàn)來討論(技術(shù)選型后面的文章再聊)。
Metric 我們使用分布式 Prometheus 方案 Cortex,數(shù)據(jù)模型和 Prometheus 一致 Logging 我們使用 Loki Tracing 我們使用 Grafana Tempo,Tempo 本身兼容 Zipkin、Jaeger、OpenTelemetry 等協(xié)議,所以 Tracing 直接采用 OpenTelemetry 的數(shù)據(jù)模型 Profiling 的后端實(shí)現(xiàn)基本可以復(fù)用 Loki,數(shù)據(jù)模型也和 Logging 類似
先看 Metric,它的數(shù)據(jù)模型:LabelSet + Timestamp + Number
LabelSet就是 Series,是若干個(gè) label name / value 組合,指標(biāo)名稱也是一個(gè) label name / value。Timestamp是時(shí)間戳,精度是毫米。Number是數(shù)值,類型是 float64。
下面是一個(gè) Metric 例子:

另外,Prometheus 內(nèi)置幾種 Metric 類型,包括 Counter、Gauge、Histogram、Summary,Counter 是自增的,Gauge 可增可減,Histogram 是直方圖,Summary 是摘要,Histogram 和 Summary 區(qū)別是 Histogram 需要通過 _bucket 來計(jì)算 P 值,而 Summary 在客戶端直接計(jì)算好 P 值,直接存儲(chǔ)即可。
另外,Prometheus 還有很多內(nèi)置函數(shù),來做 Metric 的聚合,這里不再贅述。
再看 Logging,數(shù)據(jù)模型:LabelSet + Timestamp + String
和 Metric 類似,只是 Number 換成了 String,Timestamp 精度是納秒。
在 Loki 中,使用 Logql 語法查詢?nèi)罩荆ê?Promql 類似),下面是一個(gè)例子:
{container="query-frontend",namespace="loki-dev"} |= "metrics.go" | logfmt | duration > 10s and throughput_mb < 500
下一個(gè)是 Tracing,Tracing 比較復(fù)雜:Operation Name + Start / End Timestamp + Attributes + Events + Parent + SpanContext
Operation Name:操作名
Start / End Timestamp:開始和結(jié)束時(shí)間
Attributes:KV 對(duì),包括 Status(比如 OK、Cancelled、Permission Denied)、
SpanKind(CLIENT、SERVER、PRODUCER、CONSUMER、INTERNAL 等)、自定義信息等
Events:若干個(gè)元組列表,每個(gè)元組包括 timestamp、name、Attributes,用于記錄一系列重要事件
Parent 包含父親的 Span ID、Trace ID
SpanContext 包含自身的 Span ID、Trace ID
下面是一個(gè)例子:

最后看 Profiling,數(shù)據(jù)模型:LabelSet + Timestamp + []byte
Profiling 的數(shù)據(jù)格式是 protocol buffers,所以用 []byte。
上面介紹了四種數(shù)據(jù)模型,其實(shí)在實(shí)際場(chǎng)景中,它們之間也會(huì)互相融合,下面說幾種常見的融合場(chǎng)景。
第一,Metric 和 Tracing 融合。
這里要用到 Exemplar,Exemplar 最早被用在 Google 的 StackDriver 中,后面成為了 OpenMetrics 標(biāo)準(zhǔn)的一部分,在應(yīng)用通過標(biāo)準(zhǔn) /metrics 端口暴露 Metric 時(shí),Exemplar 信息也會(huì)被一起暴露。
Prometheus 目前已支持 Exemplar,Prometheus 通過 /metrics 采集數(shù)據(jù)時(shí)也會(huì)把 Exemplar 存儲(chǔ)下來,并暴露單獨(dú)的 API 來獲取 Exemplar 信息。
$ curl -g 'http://localhost:9090/api/v1/query_exemplar?query=test_exemplar_metric_total&start=2020-09-14T15:22:25.479Z&end=020-09-14T15:23:25.479Z'
{
"status": "success",
"data": [
{
"seriesLabels": {
"__name__": "test_exemplar_metric_total",
"instance": "localhost:8090",
"job": "prometheus",
"service": "bar"
},
"exemplars": [
{
"labels": {
"traceID": "EpTxMJ40fUus7aGY"
},
"value": 6,
"timestamp": 1600096945479,
"hasTimestamp": true
}
]
},
]
}
借助 Exemplar,可以把 Trace ID 作為一個(gè) label pair 加入 Exemplar 中,從而可以在Prometheus 查詢到 Tracing 的信息,從而將 Metric 和 Tracing 連接起來。

第二,Logging 和 Tracing 融合。
只要使用帶有 Tracing 庫的 SDK,每個(gè)請(qǐng)求都會(huì)帶上 Trace ID,并把這些 ID 打在日志中。
通過 Trace ID 可以定位到一個(gè)唯一的 Tracing, 跳轉(zhuǎn)到 Tracing 系統(tǒng)的 UI 進(jìn)行查詢。
第三,Metric 和 Profiling 融合。
基于 Exemplar,把 Profiling ID 也放入 Exemplar 中,Prometheus 支持存儲(chǔ)和查詢即可。
至于展示,可以在 Grafana 上開發(fā)一個(gè) pprof 的 Panel 插件,這樣可以展示 Profiling。
原文地址:https://mirror.xyz/0xFd007bb46C47D8600C139E34Df9DfceC86F0B319/hw60dfH7YMtM3jd5dT22spTpPGSS7T8yxskkddTXXro
