一、概念
2023年的《可观测性技术发展研究报告》指出,可观测性指的是通过系统的外部输出来度量系统内部运行状态的能力。监控是可观测性的关键核心组成部分,两者是相互依赖的不同概念,监控是为提高系统的可观测性而执行的操作。
业界将可观测性能力划分为 5 个层级,其中告警(Alerting)与应用概览(Overview)属于传统监控的概念范畴。触发告警的往往是明显的症状与表象,但随着架构与应用部署方式的转变,不告警并非意味着一切正常,因此,获取系统内部的信息就显得尤为重要,而这些信息则须借助可观测性能力的另一大组成部分——主动发现(Preactive)。
1)可观测性与监控的区别
传统的监控以运维为核⼼,侧重于某些值(错误的时间和内容等),通过对照已知阈值检测特定系统的运行状况,这些阈值将提示是否存在已知设定条件的错误,这是一种被动方式,只能发现已知可能出现的错误,并且收集到的不同系统组件的数据可能是孤立的,其相互关系很难理解。
而可观测性以开发为核⼼,基于指标收集、日志分析、事件追踪、机器学习等技术和方法,提供更全面、更主动、自适应的监控和分析能力,可以全面了解所有相互关联的系统,更准确的指出问题发生的位置和原因,主动识别和定位任何故障异常,无论是已知的还是未知的错误。例如它会回答是代码变更的哪一部分造成了这种影响,并提出合适的修复方法。
在《可观测性工程》一书中提到,可观测性使得工程师不需要依靠人类的经验和直觉来检测系统的问题,可以有条不紊、客观地研究任何问题,而无须考虑之前是否了解这个系统。从开发和运维的角度将应用程序的层层调用逻辑透明化,从而方便企业快速进行故障定位,降低 MTTR(Mean time to repair,平均故障修复时间),提升整体的用户体验。
人工智能(AI)和机器学习(ML)越来越多地被用于可观测性平台,以提供自动化异常检测、根本原因分析和预测性见解。这些技术有助于减少识别和解决复杂系统中问题所需的时间和精力。
2)DevOps 和 SRE
DevOps 是 Development 和 Operations的混成词,突出重视 Dev(开发人员)和 Ops(运维人员)之间的沟通合作,通过自动化流程来使得软件构建、测试、发布更加快捷、频繁和可靠;SRE(Site Reliability Engineering)是指网站可靠性工程,直接掌管互联网公司的机器和服务,保证网站不宕机是他们的使命。
DevOps 和 SRE 团队的工作是了解生产系统和驯服复杂性。因此,他们很自然地会关心其构建和运行系统的可观测性。SRE 专注于根据服务级别目标(SLO,Service Level Objective)和错误预算(Error Budget)来管理服务。DevOps 专注于通过跨职能实践来管理服务,开发人员对其生产中的代码负责。
3)可观测性平台
构建可观测性平台需要具备:
- 统一构建方式,将各种指标、链路、追踪、用户行为、事件、安全等数据统一收集,建立多维度的直接关系,实现数据联动的能力,赋予开发、运维和测试统一的技术数据分析能力。
- 统一数据模型,涵盖各种基础设施、技术栈中收集的全量数据,不仅仅包含指标,也需要支持相关的日志以及链路等数据。数据进行结构化处理,达成数据模型的统一。
- 统一信息处理,包含采集、整理、储存等,统一查询,统一展示的基础。
- 统一查询分析,使用分析引擎进行统一的数据分析,可观测平台具备一类语法简单且功能强大的类 SQL 查询语言。
- 统一内容消费,通过优秀的图表展示,运行情况定期汇总生成报表,实时监控和告警通知,及时发现异常,通过开放 API 接口将可观测性数据对外开放。
- 环境适配能力,具备自定义集成更多被观测系统的业务信息能力,即数据编排能力或数据驱动编程能力,允许平台灵活地集成额外的数据。
二、三大支柱
可观测性的三大支柱包括:指标(metrics)、日志(log)和链路(trace),它们也叫遥测(telemetry)数据。
1)日志
日志就是系统运行过程中输出的文本数据,包含系统对指定对象执行的操作、结果和时间等信息。
日志格式往往是结构化的(例如 JSON 格式),如此定义后数据能更加清晰,并且便于机器解析。
{ "logName": ..., "resource": ..., "httpRequest": ..., "jsonPayload": { "user" : "some-user", "method" : "GET", "code" : 200, "size" : 777, "host" : "192.168.0.1", "path" : "/some-path", "referer": "some-referer", "agent" : "Opera/12.0" } }
2)指标
指标可量化系统状态和性能,某个被测量的主体在一个时间范围内的各个时间点上的测量值,例如服务器 CPU 使用情况、内存、网络流量和请求延迟等数据。
指标是传统监控系统软件中的主要组成部分,它是预聚合度量。由指标生成的数值反映了预定义时间段内系统状态的聚合报告。当将指标值收集到监控系统中时,预先聚合好的措施就成了监测系统状态的最小颗粒度。
3)链路
链路(即链路追踪)可深入了解请求路径、性能瓶颈和系统依赖关系,多个处理数据的片段(也叫 span,跨度)通过链路 ID 进行串联,组成一条链路追踪。
如果不能理解依赖关系,那么调试将会变得非常困难。例如下游数据库服务出现性能瓶颈,那么服务延迟可能就会堆积。在上游三到四层检测到延迟时,确定系统的哪个模块是问题的根源可能会非常困难,因为在几十个其他服务中都可以看到相同的延迟。
将链路可视化为瀑布图(两张图分别是两个工具)能更容易的展示请求的每个阶段,及其在每个部分的开始和结束时间,瀑布图的每一行(组成部分)就是一个 span。
为了构建任何路径的视图,每个 span 都需要 5 段数据:链路 ID、span ID、Parent ID、时间戳和执行时长。其中链路 ID 由根 span ID 生成,会贯穿请求的各个阶段;Parent ID 用来记录 sapn 之间的嵌套包含关系。
追踪工具本身有较强的侵入性,通常是以插件式的探针来实现。除了硬编码之外,还可以采用优秀的开源项目: OpenTelemetry。 OpenTelemetry(OTel)支持多种编程语言(Go、JavaScript、Java 等),能捕获指标、日志、链路等遥测数据,并且仅需埋点一次,就能将遥测数据发送至所选的后端。架构图如下图所示,由API,SDK和Collector三部分组成。
在官方的 Github 中给出了许多的示例,包括 Node 的框架 Express 和 KOA,数据库的 MySQL 和 MongoDB,甚至对日志库(bunyan)也有示例。
三、SLO
阈值告警只适用于已知的未知情况,例如 CPU 占用超过 80%、内存可用率低于 10% 等,其表现过于僵硬和粗糙,不能可靠地表明动态环境中的用户体验下降,没办法做到上下文关联。可靠的告警需要更精细的颗粒度和可靠性,这就是 SLO 可以帮助补充的地方。
SLO(Service Level Objective,服务水平目标)是测量服务健康的内部目标,提供一个安全区间,帮助团队在外部用户体验达到不可接受的水平之前进行识别和补救问题。为了解决告警疲劳的问题(所谓告警疲劳是指大量误报或不需要操作的告警会让人逐渐减少对所有告警的关注),在设计告警规则时需要两个注意点:
- 告警必须由可靠的指标触发,只有在用户服务体验处于降级时才能触发。
- 告警必须是可操作的,即确定用户已经受到了影响,需要采取行动。
SLA(Service Level Agreement,服务水平协议)是指系统服务提供者(Provider)对客户(Customer)的服务承诺,即正常运行时间、响应能力和责任等可衡量指标的协议,描述了在达到或没有达到 SLO 的后果,例如如果服务在一个月内未达到 99.95% 的可用性,那么该服务提供商每分钟都会因不合规而补偿客户。如果 SLA 是与客户之间的正式协议,那么 SLO 就是向客户作出的个人承诺,SLO 可以设定客户期望。
通常,SLO 与 SLA 类似,但更严格。例如如果 SLA 中的数据可用性阈值设置为 99.9%,那么 SLO 中的数据可用性阈值应为 99.99%。
1)SLI
SLO 是基于用户最终体验而不是系统指标作为标准,来量化服务可用性的目标。而这个目标是用 SLI(Service Level Indicator,服务水平指标)来衡量的。
SLI 把系统状态分为好和坏。存在两种类型的 SLI:
- 基于时间的测量,也叫基于窗口的测量,例如在每个 5 分钟的窗口中,延迟小于 300ms 的 P99 分位。
- 基于事件的测量,也叫基于请求的测量,例如在一个给定的滚动时间窗口中,耗时小于 300ms 的事件比例。
建议设置基于事件测量的 SLI,因为它提供了更可靠和更细化的方式来量化服务的状态。假设将用户体验定义为:用户应该能够成功地加载你的主页并迅速看到结果。用一个 SLI 来表达,意味着对事件进行限定,然后确定是否符合条件,在这个例子中,SLI 将做以下工作:
- 筛选符合条件的事件,其中事件持续时间小于 100ms。
- 如果持续时间小于 100ms,并且成功地提供了服务,则认为是好的。
- 如果持续时间大于 100ms,并且成功地提供了服务,则认为是坏的,即使返回一个成功代码。
综上所述,可以将 SLA、SLO 和 SLI 的关系如下表展示:
SLI | SLO | SLA |
衡量系统稳定性的指标,例如:
|
SLI 判断规则,例如:
|
SLA 与 SLO 类似,但判断规则稍微宽松点, 并且会描述在达到或没有达到 SLO 的后果 |
更多 SLI 指标(来自 Google 的 SRE 书籍)参考如下:
- 响应或者请求类型的服务。
- 可用性:服务成功响应的请求比例。
- 延迟:响应请求需要多长时间,超过某个阈值的请求比例。
- 吞吐量:可以处理多少个请求。
- 数据存储类型的服务。
- 可用性:数据是否可以按需访问,可以成功读取和写入的比例。
- 延迟:读取和写入需要多长时间,超过某个阈值的比例。
- 耐用性:用户所需要的特定数据是否存在。
- 数据管道(Pipeline,将输入的数据进行转换并进行输出,例如从多种来源收集日志并生成报告)。
- 正确性:进入管道的产生正确的值的记录所占的比例。
- 新鲜度:新数据或处理结果需要多长时间出现。
2)错误预算
SLI 是衡量系统稳定性的指标,包括请求计数(例如每分钟产生 2XX 或 5XX 响应的 HTTP 请求数),响应延迟时间(例如 HTTP 2XX 响应的延迟时间)等。SLO 是每个指标对应的目标,SLO 又会被转化为错误预算,因为错误预算的形式更加直观。转化后,要做的稳定性提升和保障工作,其实就是想办法不要把错误预算消耗完,或者不能把错误预算快速大量地消耗掉。
任何错误事件都会花费 SLO 中的一些错误预算(下面是一张错误预算消耗图)。错误预算表示企业愿意容忍的最大系统不可用性,即系统在不产生约定后果的情况下可以出现故障的最长时间。
如果 SLO 是为了确保 99.9% 的请求成功,那么基于时间的计算,表明系统在一个标准年内不可用的时间不超过 8 小时 45 分 57 秒,更多参数可参考下表。
SLO(正常运行时间占比) | 每年允许停机时间 | 每月允许停机时间 |
99.99% | 52 分钟 35 秒 | 4 分钟 23 秒 |
99.95% | 4 小时 22 分钟 48 秒 | 21 分钟 54 秒 |
99.9% | 8 小时 45 分钟 57 秒 | 43 分钟 50 秒 |
99.5% | 43 小时 49 分钟 45 秒 | 3 小时 39 分钟 |
99% | 87 小时 39 分钟 | 7 小时 18 分钟 |
只要有足够的错误,系统就可以提醒你有可能违反 SLO 的规定。如果一个潜在的条件影响了之前定义的那个用户体验,就应该触发告警,因为有人需要调查原因。基于 SLO 的告警都是基于症状的:它们告诉你有问题。它们是可操作的,因为现在要由响应者来确定为什么用户看到了影响并采取行动加以缓解。
当错误预算耗尽时,保障服务稳定性的优先级要高于新功能的上线。如果可以预见到错误预算即将用完,那么就有机会提前采取行动修复它,而不是任其发生。一个解决方案是追踪错误预算消耗率:
- 选择一个非零的阈值来发出告警,例如可以在预算降低到 30% 以下时发出告警。
- 创建预测消耗告警来触发零级以上告警,这些预测会告诉你当前条件是否会导致消耗整个错误预算。
第四和第五章节的内容均来源于《深入浅出可观测性》。
四、可观测性平台
以一款成熟的可观测性平台为例,在建立 SLO 之后,就可以通过仪表盘来持续跟踪,并将相关的 SLI 统一展示出来。同时,也可以针对 SLI 和 SLO 的状态设定监控器,及时获取异常事件的告警。
1)错误详细
当通过仪表盘直接查看整个服务 SLO 和错误余额的情况时,也能够同时查看相关 SLI 的情况。例如如果发现页面错误率有异常,可以进一步进行分析,查看错误的详细情况。
2)错误统计
在错误分析页面,还能进一步查看多个维度的错误统计。例如想了解具体哪些页面有报错,可以查看“页面错误率排行”,然后进一步下钻去查看这个页面(也就是 View 查看器)的详细信息。
3)页面数据
通过 View 页面,可以看到用户访问时的页面性能数据,包括页面地址、页面加载类型、页面加载时间、用户停留时间等。如果列表最左侧出现红色小三角的标记,说明这个页面是有报错的,点击其中一条可以查看详细的信息。
4)网络请求
因为之前已经做了数据的关联,所以可以继续下钻,查看相关的链路、日志和指标信息,还能够直接查看相关联的视图。也就是说,在一个界面中,可以多维度地查看各种数据。
比如说,点击“Fetch/XHR”的标签页,就可以查看用户在访问时,向后端应用发出的每一个网络请求,包括发生时间、请求的链路和持续时间;如果网络请求存在对应的 TraceID,而且已经和前端用户访问的数据进行了关联,那么就可以点击列表中的某个请求,进一步下钻来分析问题。
点开具体的请求之后会跳转至对应链路的详情页,这里可以看到链路 Tracing 的详细信息,包括整条链路中每个 span 的流转和执行时间。如下图所示,这时候可以注意到有一条红色标记的 span,这表示系统有报错,可以在详情中查看详细的报错信息,定位问题的原因。
5)关联数据
还有的时候,问题的情况比较复杂,所以希望不仅能够查看链路的属性和耗时,也能够直接查看相关的日志、主机的性能、网络情况以及相关联的视图,将各维度的数据综合在一个界面来分析。
如下图所示,当查看链路的详细信息时,能够同时关联分析链路发生时相关主机的负载情况(包括指标和网络)、相关的日志等信息。
6)MySQL
在下图中,可以将 MySQL 数据库的监控视图与 MySQL 相关的服务绑定在一起,这样在查看 MySQL 相关的链路时,也能够同时查看数据库的负载情况,更加全面地分析问题。
五、平台成熟度
建立可观测性还必须有明确的指导方针,当评估可观测性最终的实现结果时,可以将其分解为 3 个关键问题:
- 出现问题时,我多久能收到通知?是在最终用户使用体验已经开始受到影响之前吗?
- 如何快速地对问题进行分类并了解其影响?
- 如何找到根本原因以便解决问题?
对应着这三个问题,可以将可观测性成熟的程度框架分为 3 个阶段。在每个阶段,重点都是在出现问题的时候,尽可能快地修复问题,或是减轻对最终用户的影响。
1)感知到问题
解决问题的第一步是知道问题的存在,而且最好是在最终用户知道或者是被真正影响之前。通常,知道问题正在发生就足以触发补救的措施。
例如,如果你部署了新版本的服务,而该服务触发了影响用户使用的告警,那么回滚部署就是补救问题的最快途径。我们不需要了解事件的全部影响或者诊断事件的根本原因,因为这些可以等到事后再做检查和修复。
在这个阶段的关键动作和考量包括下面几点。
- 快速告警:缩短问题发生和通知触发之间的时间。
- 提高信噪比:确保告警是可操作的。
- 将通知范围仅限于需要采取行动的团队:从一开始就确定问题范围并将其发送给正确的团队。
- 自动化告警设置:让大多数服务或主机产生相同的指标数据,这意味着自动化或模板化告警,它可以帮助工程师了解问题,而无需复杂的设置过程。
2)对问题进行诊断
这一阶段的目标是快速了解问题的背景和影响。一旦告警触发,如果最近对系统的更改不是很明显需要回滚,下一步就要了解业务影响和严重性了。
例如,如果你确定只有一组有限流量切换中的用户受到影响,那么关闭该流量切换就可能解决问题。或者,如果对一个可用区域或集群的请求受到影响,你可以将请求重新路由到其他区域或集群。
在这个阶段的关键动作和考量如下。
- 具备上下文的仪表板:将告警直接链接到仪表板,这些仪表板不仅显示告警的来源,还显示相关的上下文数据。
- 高基数数据:允许工程师对数据进行“切片”,从而进一步明确问题的影响范围。
- 高维度数据:允许工程师通过尽可能多的角度、条件来聚合信息,从而在排查故障时缩小范围,明确方向。
3)理解问题
这个阶段最好发生在问题被修复之后,此时工程师可以花时间定位和理解潜在问题,而不会受到业务和用户影响带来的直接压力。随着微服务数量的不断增加,对事件进行事后分析就像是复杂网络中的导航,它也决定了你需要与哪个服务所有者
合作。
在这个阶段的关键动作和考量如下。
- 轻松理解服务依赖关系:识别有问题的服务的直接上下游依赖项。
- 在不同数据类型之间进行联合和跳转的能力:对于复杂的问题,你需要综合考量仪表盘上的指标趋势、异常值,跳转到相关的日志和链路追踪信息,需要统一的工具给出数据关联分析结果,而不是人肉地做关联和排查。
- 更进一步的是智能分析和预测:能够自动检测基础设施和应用程序问题,帮助用户提前发现 IT 系统运行过程中潜在的问题,通过根因分析,快速定位异常问题的原因,并进行提前的预警。
参考资料:
通过OpenTelemetry接入Node.js Trace数据(阿里云)
通过OpenTelemetry上报Node.js应用数据(阿里云)
Distributed Tracing 101 for Full Stack Developers
如何基于标准化的OpenTelemetry构建APM探针能力
Google SRE: SLI、SLO、SLA 、Error Budget 详解
It’s Time to Set SLA, SLO, SLI for Your Data Team — Only 3 Steps