聊聊可观测性之数据模型
在 201x 年,随着容器技术的出现,容器的部署方式逐渐被各大互联网公司采用,相比物理机/虚拟机,容器的好处是环境隔离、轻量、快速。
但是管理容器是一件复杂的事情,后来出现了 Kubernetes,成为了事实上的容器管理标准,目前各大公司都在使用 Kubernetes。
因为容器和 Kubernetes 降低了服务(应用)的部署和升级成本,所以催生了「微服务」的概念,服务从「单体复杂服务」向「多个简单服务」演变,在之前,需要着重考虑服务内的架构设计,单个服务对外提供尽可能多的能力,而在微服务中,会直接把单个服务拆分成多个服务,服务之间用 API 调用。
这里也可以看到,在微服务中,架构设计的重要性降低,API 设计的重要性提高。
另外,拆分出微服务后,编程的难度事实上降低了,对编程人员的要求也降低了。
这说明一个事实,随着基础设施的不断发展,会有越来越多的「编程能力」沉淀成基础设施,使编程的难度不断降低:软件开发不断向简单的方式发展。
但是,随着微服务的发展,服务变得太多了,管理负责度又上升了,比如怎么去解决服务发现的问题、怎么控制流量、服务之间怎么做隔离,服务状态怎么观测等等。这时候又出现了「服务治理」的概念,关于服务治理,有一个新的词:Service Mesh,现在事实标准是 Istio。
概述
可观测性是为了应对微服务的复杂场景下发明出来的一个词,本质上是为了衡量系统运行的状态,可观测性是服务治理的一个维度,和功能性、可测试性、可运维性一样。
一般常说可观测性包含三个度量角度:Metric、Logging、Tracing,其实还有一个:Profiling。
Metric:指标,对系统中某一类信息的聚合统计,比如 QPS、延迟、错误率等。 Logging:日志,对系统所做行为的一种记录,它是离散的,没有相关性,为了区分这种记录的重要程度,会分级别(DEBUG、INFO、WARN、ERROR、FATAL)。 Tracing:调用链,它反映的是请求经过某个组件的运行情况,经过组件的数据叫做 Span,Span 可以体现经过组件的状态、一些关键属性和事件、上下文信息。Span 之间通过 Trace ID 关联。 Profiling:一般叫做 Continuous Profiling,持续分析,它反映的是程序内部的运行状态,比如栈调用、执行时间等。可以把 Profiling 可视化成火焰图方面分析问题。
一般来说,基于这些度量处理故障的流程是:Metric → Tracing → Logging → Profiling
根据 Metric 配置的告警策略发现问题,基于 Tracing 查看是哪个组件出问题,基于 Logging 查看组件的日志,Profiling 分析组件具体的故障或性能问题。
数据模型
在 Tracing 领域,之前有两个项目,一个是 OpenTracing
,它是一个规范,Jaeger
就是基于 OpenTracing
的开源实现,另一个是 OpenCensus
,它是 Google 开源的度量工具。这两个项目功能高度重合,在 CNCF 主导下合并成了 OpenTelemetry
,而 OpenTracing 和 OpenCensus 也不再维护。
当然 OpenTelemetry 不止做 Tracing,还覆盖 Metric 和 Logging,它的目标是统一可观测性的标准协议,包括数据模型、API 规范、多语言 SDK、采集器。
OpenTelemetry 只做统一的协议和规范,具体数据的后端存储和展示不是它的范围。协议和规范是可观测性对外暴露的「接口」,它的统一对于使用方来说是巨大的好处,目前来看,OpenTelemetry 未来会成为事实标准。
为了对接不同的后端实现,OpenTelemetry 提供了各种 Exporter,比如为对接 Prometheus 提供了 Prometheus Exporter,对接 Cortex 和 Thanos 提供了 Prometheus Remote Write Exporter,对接 Loki 提供了 Loki Exporter,对接 Jaeger 提供了 Jaeger gRPC Exporter。
不过,目前 OpenTelemetry 还不成熟,本文的数据模型基于我们事实上使用的后端实现来讨论(技术选型后面的文章再聊)。
Metric 我们使用分布式 Prometheus 方案 Cortex,数据模型和 Prometheus 一致 Logging 我们使用 Loki Tracing 我们使用 Grafana Tempo,Tempo 本身兼容 Zipkin、Jaeger、OpenTelemetry 等协议,所以 Tracing 直接采用 OpenTelemetry 的数据模型 Profiling 的后端实现基本可以复用 Loki,数据模型也和 Logging 类似
先看 Metric,它的数据模型:LabelSet + Timestamp + Number
LabelSet
就是 Series,是若干个 label name / value 组合,指标名称也是一个 label name / value。Timestamp
是时间戳,精度是毫米。Number
是数值,类型是 float64。
下面是一个 Metric 例子:
另外,Prometheus 内置几种 Metric 类型,包括 Counter、Gauge、Histogram、Summary,Counter 是自增的,Gauge 可增可减,Histogram 是直方图,Summary 是摘要,Histogram 和 Summary 区别是 Histogram 需要通过 _bucket 来计算 P 值,而 Summary 在客户端直接计算好 P 值,直接存储即可。
另外,Prometheus 还有很多内置函数,来做 Metric 的聚合,这里不再赘述。
再看 Logging,数据模型:LabelSet + Timestamp + String
和 Metric 类似,只是 Number 换成了 String,Timestamp 精度是纳秒。
在 Loki 中,使用 Logql 语法查询日志(和 Promql 类似),下面是一个例子:
{container="query-frontend",namespace="loki-dev"} |= "metrics.go" | logfmt | duration > 10s and throughput_mb < 500
下一个是 Tracing,Tracing 比较复杂:Operation Name + Start / End Timestamp + Attributes + Events + Parent + SpanContext
Operation Name:操作名
Start / End Timestamp:开始和结束时间
Attributes:KV 对,包括 Status(比如 OK、Cancelled、Permission Denied)、
SpanKind(CLIENT、SERVER、PRODUCER、CONSUMER、INTERNAL 等)、自定义信息等
Events:若干个元组列表,每个元组包括 timestamp、name、Attributes,用于记录一系列重要事件
Parent 包含父亲的 Span ID、Trace ID
SpanContext 包含自身的 Span ID、Trace ID
下面是一个例子:
最后看 Profiling,数据模型:LabelSet + Timestamp + []byte
Profiling 的数据格式是 protocol buffers,所以用 []byte。
上面介绍了四种数据模型,其实在实际场景中,它们之间也会互相融合,下面说几种常见的融合场景。
第一,Metric 和 Tracing 融合。
这里要用到 Exemplar,Exemplar 最早被用在 Google 的 StackDriver 中,后面成为了 OpenMetrics 标准的一部分,在应用通过标准 /metrics 端口暴露 Metric 时,Exemplar 信息也会被一起暴露。
Prometheus 目前已支持 Exemplar,Prometheus 通过 /metrics 采集数据时也会把 Exemplar 存储下来,并暴露单独的 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 作为一个 label pair 加入 Exemplar 中,从而可以在Prometheus 查询到 Tracing 的信息,从而将 Metric 和 Tracing 连接起来。
第二,Logging 和 Tracing 融合。
只要使用带有 Tracing 库的 SDK,每个请求都会带上 Trace ID,并把这些 ID 打在日志中。
通过 Trace ID 可以定位到一个唯一的 Tracing, 跳转到 Tracing 系统的 UI 进行查询。
第三,Metric 和 Profiling 融合。
基于 Exemplar,把 Profiling ID 也放入 Exemplar 中,Prometheus 支持存储和查询即可。
至于展示,可以在 Grafana 上开发一个 pprof 的 Panel 插件,这样可以展示 Profiling。
原文地址:https://mirror.xyz/0xFd007bb46C47D8600C139E34Df9DfceC86F0B319/hw60dfH7YMtM3jd5dT22spTpPGSS7T8yxskkddTXXro