学术界一般会将可观测性分解为三个更具体方向进行研究,分别是:事件日志、链路追踪和聚合度量,这三个方向各有侧重,又不是完全独立
日志的职责是记录离散事件,通过这些记录事后分析出程序的行为,譬如曾经调用过什么方法,曾经操作过哪些数据
只要稍微复杂点的系统,尤其是复杂的分布式系统,往往还要有专门的全局查询和可视化功能。此时,从打印日志到分析查询之间,还隔着收集、缓冲、聚合、加工、索引、存储等若干个步骤。我们简单的梳理一下日志存储与打印的流程,下面所说的流程,就是 ELK 技术栈
日志输入就是我们在代码中使用 log 提供的方法输出,当然 print 等方法也可以,不过不推荐。这块主要是开发的工作,打出优秀的日志可以便于排查问题,我们应该尽可能的避免如下几点:
我们应当尽可能做到如下几点:
写日志是在服务节点中进行的,但我们不可能在每个节点都单独建设日志查询功能。这不是资源或工作量的问题,而是分布式系统处理一个请求要跨越多个服务节点,为了能看到跨节点的全部日志,就要有能覆盖整个链路的全局日志系统。这个需求决定了每个节点输出日志到文件后,必须将日志文件统一收集起来集中存储、建立索引,由此便催生了专门的日志收集器
为了处理多个节点的协调,除了今天流行的日志收集器,轻量高效的 Filebeat 以外,还需要有专门用于其他功能的组件,比如用于审计数据的 Auditbeat、用于无服务计算架构的 Functionbeat、用于心跳检测的 Heartbeat 等等
至于缓冲,是指流量大的时候,会有过多的日志打进 DB,导致数据库压力过大。一种最常用的缓解压力的做法是将日志接收者从 Logstash 和 Elasticsearch 转移至抗压能力更强的队列缓存,譬如在 Logstash 之前架设一个 Kafka 或者 Redis 作为缓冲层
将日志集中收集之后,存入 Elasticsearch 之前,一般还要对它们进行加工转换和聚合处理。这是因为日志是非结构化数据,一行日志中通常会包含多项信息,如果不做处理,那在 Elasticsearch 就只能以全文检索的原始方式去使用日志,既不利于统计对比,也不利于条件过滤
为了加快查询速度,我们必须要建立索引,但是对一串巨大的字符串直接建立索引肯定不是优秀的解法,我们可以根据输出日志的特征、形式,来对字符串做拆分,拆出来的数据就可以建立不同的索引了
这就引出了我们处理加工聚会的工具 Logstash。Logstash 的基本职能是把日志行中的非结构化数据,通过 Grok 表达式语法转换为上面表格那样的结构化数据,然后以 JSON 格式输出到 Elasticsearch 中(这是最普遍的输出形式,Logstash 输出也有很多插件可以具体定制不同的格式)
经过 Logstash 转换,已经结构化的日志,Elasticsearch 便可针对不同的数据项来建立索引,进行条件查询、统计、聚合等操作的了
在经过一系列的数据处理后,我们终于可以把数据放进 DB 了,也就是 ES Elasticsearch
Elasticsearch 只提供了 API 层面的查询能力,它通常搭配同样出自 Elastic.co 公司的 Kibana 一起使用,可以将 Kibana 视为 Elastic Stack 的 GUI 部分
Kibana 尽管只负责图形界面和展示,但它提供的能力远不止让你能在界面上执行 Elasticsearch 的查询那么简单。Kibana 宣传的核心能力是“探索数据并可视化”,即把存储在 Elasticsearch 中的数据被检索、聚合、统计后,定制形成各种图形、表格、指标、统计,以此观察系统的运行状态,找出日志事件中潜藏的规律和隐患
微服务时代,追踪就不只局限于调用栈了,一个外部请求需要内部若干服务的联动响应,这时候完整的调用轨迹将跨越多个服务,同时包括服务间的网络传输信息与各个服务内部的调用堆栈信息。追踪的主要目的是排查故障,如分析调用链的哪一部分、哪个方法出现错误或阻塞,接口的输入输出是否符合预期
Dapper 提出了追踪与跨度两个概念。从客户端发起请求抵达系统的边界开始,记录请求流经的每一个服务,直到到向客户端返回响应为止,这整个过程就称为一次追踪(Trace)。由于每次 Trace 都可能会调用数量不定、坐标不定的多个服务,为了能够记录具体调用了哪些服务,以及调用的顺序、开始时点、执行时长等信息,每次开始调用服务前都要先埋入一个调用记录,这个记录称为一个跨度
追踪数据一般可以使用以下三种方式来实现
度量是指对系统中某一类信息的统计聚合。譬如,证券市场的每一只股票都会定期公布财务报表,通过财报上的营收、净利、毛利、资产、负债等等一系列数据来体现过去一个财务周期中公司的经营状况,这便是一种信息聚合。像是任务管理器,就是度量的一种
度量(Metrics)的目的是揭示系统的总体运行状态,因此可以拆分为监控(Monitoring)和预警(Alert),如某些度量指标达到风险阈值时触发事件,以便自动处理或者提醒管理员介入
指标收集部分要解决两个问题:如何定义指标以及如何将这些指标告诉服务端, 如何定义指标这个问题听起来应该是与目标系统密切相关的,必须根据实际情况才能讨论,其实并不绝对,无论目标是何种系统,都是具备一些共性特征。确定目标系统前我们无法决定要收集什么指标,但指标的数据类型(Metrics Types)是可数的,所有通用的度量系统都是面向指标的数据类型来设计的:
而如何将这些指标告诉服务端这个问题,通常有两种解决方案:拉取式采集和推送式采集,所谓 Pull 是指度量系统主动从目标系统中拉取指标,相对地,Push 就是由目标系统主动向度量系统推送指标。不管是拉取还是推送,在进行操作之前,指标数据会记在内存中
存放指标一般不会选择 MySQL 或者 PG 等关系型数据库,而是时序数据库。时序数据库用于存储跟随时间而变化的数据,并且以时间(时间点或者时间区间)来建立索引的数据库
时间序列数据是历史烙印,具有不变性,、唯一性、有序性。时序数据库同时具有数据结构简单,数据量大的特点
时序数据通常只是追加,很少删改或者根本不允许删改。针对数据热点只集中在近期数据、多写少读、几乎不删改、数据只顺序追加这些特点,时序数据库被允许做出很激进的存储、访问和保留策略: