02 基本概念:指标+日志+链路追踪=可观测性? 你好,我是翁一磊。

上节课,我们介绍了计算机系统监控的发展历史,这节课我们来具体聊一聊可观测性,以及大家对于可观测性的一些误解。

什么是可观测性?

就像我们在开篇词中说的,可观测性强调的是可以从系统向外部输出的信息来推断出系统内部状态的好坏。

当我们把“可观测性”这个概念挪到软件系统时,其实强调的也是一种度量能力,一个软件应用程序具有可观测性,意味着它能够让我们通过各种维度和各种角度去分析和理解这个系统当前所处的任何状态,无论这种状态有多奇怪、无论我们之前有没有遇到过,都不需要预先定义或预测。如果能够在不发布新代码(如增加一个用于调试的日志)的情况下理解任何奇怪或不确定性的状态,那么我们的系统就具备可观测性。

因此,可观测性是描述人们如何与他们的复杂系统互动,以及如何理解这些复杂系统的概念。如果你接受这个定义,那么看看接下来这些问题:

  • 如何收集数据并将它们组合起来进行分析?
  • 处理这些数据的技术要求是什么?
  • 要从这些数据中获益,团队需要具备哪些能力?

这些问题,我们都会在专栏中一一解答。不过别着急,这节课我们还是要先把可观测性的概念和内涵理清楚。

指标+日志+链路追踪=可观测性?

既然选择学习这门课程,你八成听过可观测性的“三大支柱”:指标(metrics),日志(logs)和链路追踪(Tracing)。但是,指标、日志再加上链路追踪,真的就是可观测性吗?让我们先来看一下这三类数据的含义。

指标:是在⼀段时间内测量的数值。它包括特定属性,例如时间戳、名称、键和值。和⽇志不同,指标在默认情况下是结构化的,这会让查询和优化存储变得更加容易。

例如:2022/05/20 12:48:22,CPU usage user,23.87%,它就表示 CPU 运行在用户态的时间占比在这一刻为 23.87%。

日志:是对特定时间发⽣的事件的⽂本记录。日志一般是非结构化字符串,会在程序执行期间被写入磁盘。每个请求会产生一行或者多行的日志,每个日志行可能包含 1-5 个维度的有用数据(例如客户端 IP,时间戳,调用方法,响应码等等)。当系统出现问题时,⽇志通常也是工程师⾸先查看的地⽅。常见的日志格式是下面的样子。 127.0.0.1 - - [24/Mar/2021:13:54:19 +0800] “GET /basic_status HTTP/1.1” 200 97 “-“ “Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36”

链路追踪:有时候也被称为分布式追踪(Distributed Tracing),表示请求通过分布式系统的端到端的路径。当请求通过主机系统时, 它执⾏的每个操作被称为“跨度”(Span)。

举个分布式调用的例子:客户端发起请求,请求首先到达负载均衡器,经过认证服务、系统服务,然后请求资源,最终返回结果;那这里面的操作就包括请求网关、身份认证、请求资源、以及返回结果等。

链路追踪一般会通过一个可视化的瀑布图展现出来。瀑布图展示了用于调试的请求的每个阶段,以及每个部分的开始时间和持续时长。

比方说,在下图这个例子里,瀑布图由 Span 组成。特定的链路追踪中的 Span 可能是根 Span(也就是最顶层的 Span),也可能是根 Span 以下的 Span。Span还可能包含 Span,这种常被称为父子关系。比如,如果服务 B 调用服务 B-1,服务 B-1 调用 B-2,那么在这条链路中,Span B 是 Span B-1 的父亲 Span,Span B-1 是 Span B-2 的父亲 Span。

图片

然而,仅仅是收集这些数据类并不能保证系统的可观测性,尤其是当你彼此独⽴地使⽤它们时。从根本上来说,指标、日志和链路追踪只是数据类型,与可观测性无关。

另一方面,这三种数据类型也有着局限性。

  • 指标

由于指标最大的特点是聚合性,它生成的数值反映了预定义时间段内系统状态的汇总报告,在此期间处于活动状态的所有请求的行为都会汇总为一个数值,因此缺乏细颗粒度。同时这些指标很可能都是彼此不相关的,没有关联性。

例如:page_load_time 指标可能会检查在最后 5 秒间加载所有活动页面所花费的平均时间;requests_per_second 指标可能会检查任何给定服务在最后一秒内打开的 HTTP 连接数。这就导致能够挖掘的信息的颗粒度是比较粗的,如果在 5 秒内发生了一千个离散事件,从 page_load_time 指标中根本无法获取某一事件的具体情况。

当然,这并不是说指标完全没用,指标对于静态仪表板的构建、随时间变化的趋势分析、或监控维度是否保持在定义的阈值内很有用,但这些并不是可观测性,因为这些信息的颗粒度在做故障排查或根因分析时是远远不够的。

  • 日志

日志文件本质上是分散的事件,是一大块非结构化文本,旨在方便人类阅读,但要达到这个目的,日志通常要将一个事件的所有细节分成多行文本。这样在生产环境中,日志通常散布在数以百万计的文本行中,通过使用某种类型的日志文件解析器才可以完成对它们的搜索。解析器将日志数据拆分为信息块,并尝试以有意义的方式对它们进行分组。但是,对于非结构化数据,解析变得复杂,因为不同类型的日志文件存在不同的格式化规则里(或根本没有规则)。

针对这一点的解决方案是创建结构化日志数据,例如将上面的日志解析成下面这样。

结构化日志是机器可解析的,如果它们被重新设计为类似于结构化事件的话,可以帮助我们实现可观测的目标。关于结构化事件,后面还会做进一步介绍。 “fields”: { “agent”:”Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36”, “browser”:”Chrome”, “browserVer”:”89.0.4389.72”, “bytes”:97, “client_ip”:”127.0.0.1”, “engine”:”AppleWebKit”, “engineVer”:”537.36”, “http_method”:”GET”, “http_url”:”/basic_status”, “http_version”:”1.1”, “isBot”:false, “isMobile”:false, “message”:”127.0.0.1 - - [24/Mar/2021:13:54:19 +0800] “GET /basic_status HTTP/1.1” 200 97 “-“ “Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36””, “os”:”Intel Mac OS X 11_1_0”, “referrer”:”-“, “status”:”OK”, “status_code”:200, “ua”:”Macintosh” },

  • 链路追踪

链路追踪检测的主要问题是,如果仅靠开发人员“插桩”(英文 Instrument,有些地方也翻译成埋点,是指将有关系统状态的数据发送到监测系统)他们的代码是不够的。大量应用程序是使用可能需要额外工具的开源框架或库构建的。这在多语言架构的地方变得更加具有挑战性,因为要考虑到每种语言、框架和协议的不同。

同时,增加插桩的成本也是比较高的,很难真正做到全面覆盖。这样的方式只适用于具体的业务场景,如果其他地方有类似的需要,就需要再次插桩。而且随着产品的不断迭代,我们很难一次性把需要插桩的地方都考虑周全,这就会带来反复的工作,也可能会涉及多次上线,增加了工作量的同时也降低了系统的可靠性。另一方面,大量的插桩也会占用比较高的计算资源。

总之,指标、日志和链路追踪只是数据的类型,本身并不代表可观测性。可观测性也不是供应商提供的一种技术,而是你构建的系统的属性,就像可用性、高可用性和稳定性这些一样。

设计和构建可观测系统的目标是确保系统运行时,操作员可以检测到服务停机、错误和响应缓慢等不良行为,并可以通过足够的信息来确定问题的根本原因。

可观测性的特性

就像我在前面介绍的,我们对软件系统的“可观测性”的定义是一种度量能力,能够帮助你更好地理解和解释系统当前所处的任何状态,无论这种状态或者问题是否在之前出现过。而结构化的事件(Structured Events)就是可观测性的基础。

事件指的是特定请求与服务交互时所有信息的记录,通过事件能了解生产环境中服务所受到的影响。

那什么是结构化的事件呢?

在请求第一次进入服务时,会有一个空的地图(Map)被初始化出来。在该请求的生命周期内发生的任何细节(包括唯一的 ID、变量值、标头、请求传递的每个参数、执行时间、对远程服务的任何调用、这些远程调用的执行时间),或任何可能在之后的调试中有价值的上下文,都会附加到这个地图中。然后,当请求即将退出或出错时,刚刚所发生的事情都被丰富地记录了下来。写入该地图的数据被组织和格式化为键值对,以便于搜索。换句话说,这些数据就是结构化的事件。

这样做的好处是什么呢?

当你调试服务中的问题时,可以相互比较结构化事件,及时发现异常。当某些事件的行为与其他事件明显不同时,你可以尝试确定这些异常值的共同点。探索这些异常值,需要分析可能与你的调查相关的事件,按照这些事件中所包含的不同维度(甚至是不同维度的组合)进行过滤和分组。另一方面,对你有帮助的信息可能包含不特定于任何给定请求的运行时信息(例如容器信息或版本信息),也包含有关通过服务的每个请求的信息(例如购物车 ID、用户 ID 或会话令牌等等)。这两种类型的数据都对调试很有用。

所有这些数据都可以用于调试并存储在你的事件中。它们是任意“宽度”的事件,因为你需要的调试数据可能包含大量字段,或是来自任意维度,而不应该有实际限制。如果要分析一个异常的状态,具有可观测性的调试方式就是尽量保留每一个请求的上下文,这样你就可以针对这个上下文分析定位修复这个Bug或者调整相关的环境配置了。

所以我们说,数据的高基数和高维度,这将成为能够发现隐藏在复杂系统架构中的其他隐藏问题的关键组成部分。我们分开来看一下。

基数的作用

在数据库的概念中,基数是指包含在一个集合中的唯一值的数量。低基数意味着这一列在其集合中有很多重复的值;高基数意味着该列包含很大比例的完全唯一的值。

举例来说,在一个包含1亿条用户记录的集合中,任何通用唯一标识符(UUID)都是高基数的,另外用户名也具有很高的基数(当然会低于UUID,因为有些名称可能是重复的)。另一方面,像性别这样的领域的基数就会很低。再举个例子,假设所有用户都是人类,像物种这样的字段可能具有最低的基数。

基数对于可观测性很重要,因为高基数信息在调试或理解系统的数据时是最有用的。如果能够按照这些字段,例如 userid、cartid、requestid 或任何其他 ID (host、container_name、hostname、version、span 等),根据其中的唯一 ID 来查询数据,是在“大海”中精确定位每一滴“水滴”的最佳方法。你总是可以通过聚合采样高基数的值获得较低基数的值(例如,通过首字母存储姓氏),但没法反过来。

维度的作用

基数指的是数据中值的唯一性,维度指的则是数据中键(key)的数量。在可观测系统中,遥测数据被生成为任意“宽度”的结构化事件,它们可以而且应该包含数百甚至数千个键值对(即维度)。事件范围越广,事件发生时获取的上下文就越丰富,在以后调试时,就越容易定义问题的原因。

假设你有一个事件模式,每个事件定义了六个高基数维度:时间、应用、主机、用户、端点以及状态。通过这六个维度,你可以创建查询,分析任何维度组合,以发现可能导致异常的相关模式。例如,你可以检索:“过去半小时内,发生在主机 host001 上的所有的502错误请求”,或是“由用户 vipuser001 在做数据导出时产生的所有403错误请求”。

也就是说,只需六个基本维度,你就可以通过一组有用的条件,来确定你的应用程序系统中可能发生的情况。但是在现代系统中,可能发生的故障的排列方式是无限的,只在传统监控数据中捕捉几个基本维度是不够的。现在想象一下,除了六个维度之外,你还可以关注数百乃至数千个包含无数细节、值、计数器或字符串的维度,这些维度在将来的某个时候可能对你的调试有帮助。例如,你可以包含像这样的维度: create_time component date_ns duration endpoint env http.route host operation parent_id pid resource service servlet.path source source_type start span_id span_type status trace.id thread.id thread_name version

有了更多可用的维度,你就可以检测各种事件,在任何一组服务请求之间建立高度复杂的关联了。数据的维度越高,就越有可能发现应用程序行为中隐藏的、难以捉摸的模式。在后面的章节,我们还会更详细地讲解这部分内容。

小结

好了,这节课就讲到这里,我来小结一下。

尽管“可观测性”这个专有名词已经出现几十年了,但在软件系统中它还是一个新事物,它带来了一些新的考虑和特性。可观测性的出现,其实也刚好符合计算机领域现阶段的需求,由于现代系统引入了额外的复杂性,系统的故障比以往任何时候都更难预测、检测和修复。

为了减轻这种复杂性,工程团队现在必须能够以灵活的方式不断收集遥测数据,及时调试问题,而不需要首先预知故障可能如何发生。可观测性让工程师能够以灵活的方式分析遥测数据,快速找到未知问题的根源。

可观测性通常被错误地描述为包含指标、日志和追踪的“三个支柱”,但其实这些只是遥测数据类型。如果我们必须拥有可观测性的三个支柱,那么它们应该是支持高基数、高维度和可探索性工具。下节课,我们会探讨可观测性与传统系统监控方法的不同之处。

课后题

在这节课的最后,留给你一道思考题。

你在使用监控工具对系统和应用进行监控的时候,遇到过哪些难以依靠单纯的监控来解决的问题?后来是如何找到问题原因的?

欢迎你在留言区和我交流讨论,我们下节课见!

参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%ba%e5%8f%af%e8%a7%82%e6%b5%8b%e6%80%a7/02%20%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5%ef%bc%9a%e6%8c%87%e6%a0%87+%e6%97%a5%e5%bf%97+%e9%93%be%e8%b7%af%e8%bf%bd%e8%b8%aa=%e5%8f%af%e8%a7%82%e6%b5%8b%e6%80%a7%ef%bc%9f.md