本文为可观测性白皮书(CNCF 出品)的中文翻译,全部由同事一人完成,翻译质量颇高,故微信转发推广,希望对大家有所帮助,项目地址为 https://github.com/ob1991/observability_whitepaper_cn ,感兴趣的朋友可以 star。

摘要

随着软件的复杂性及所处理的数据量的持续增长,我们需要可观测性技术来了解工作负载的状况。软件工程师除了要了解可观测性工具外,还需要了解如何监控和观测程序成为了共识。随着对服务等级目标的更高要求,这就需要软件工程师能够更快地找到造成服务异常的原因。本文旨在带你了解云原生的各种可观测性技术。

简介

随着云计算、微服务、分布式系统的盛行,上云成为了越来越多的程序的选择。尽管这一变化使得系统更方便扩展,拥有更好的性能也更加安全,但同时也更难控制基础设施。系统负责人、开发人员和运维必须了解线上程序以及底层基础设施状况,这就要求程序能够不借助其他手段能够被观测到运行状态,例如在源代码中添加新的检测或设置断点。为了能够让不同权限的人员能够观测到程序的状态,程序就需要设计之初考虑引入哪种观测工具,然而从市面上选择各种各样观测标准的工具本身就是一件很困难的事情 根据 ClearPath Strategies 和 Honeycomb.io 对于的社区的研究表明,“四分之三的团队还没有完成或者尚处于可观测性的初级阶段”,并且“朝着实现更多可观察系统的转变动力很足”。虽然刚开始时是困难的,但是一旦达到某种令人满意的可观测性有很多的好处。文化的改变,不同的工具,不同的目标,不同的方法。如此多的细节需要考虑,这会让这段旅程变得相当混乱和痛苦。本文的目的让更多的软件和运营团队能够了解可观测,运用它并获得利益。

目标听众

本文的目标读者是:

  • SRE
  • DevOps 工程师
  • 系统负责人
  • 软件工程师
  • 基础设施工程师
  • 软件开发人员

本文对那些希望交付在可靠性、安全性和透明度能够达明显的等级,能够集成客户现有的观测系统的可观测性软件的人员有所帮助。可观察性是一个多学科主题,因此组织内其他人员,例如负责设计和实施此类软件的项目、产品、项目经理和架构师,也可能对本文感兴趣。计算机科学、信息系统、工程(或相关)学生和对可观察性领域感兴趣的人也可以在本文中找到有用的信息。

目标

云计算能够帮助科技公司优化成本、规模和设计更高效的产品,同时也引入了(结构的)复杂性。由于基础设施远程、短暂和全球分布的特性,使得系统管理员曾经对数据中心拥有的控制权丢失了。曾经那些管理人员和开发人员拥有不同kpi的公司也必须转变为构建可靠软件而共同奋斗的文化。目前已经有一些工具能够通过观测云原生系统的状态提升系统的可靠性。在可观察系统的设计和开发过程中,必须对其进行检测以向第三方发送或公开遥测数据,通常是一组工具,负责从公开的数据中提供有意义的信息。遥测数据经常以metrics、logs、traces、structured events、profiles和dumps的形式呈现。每种信号都有其目的和最佳实践,它们的滥用可能会在大规模运行软件时导致新的问题,例如“告警疲劳”和“过高的成本”。尽管存在一些新的挑战,例如文化变革、产能规划、法律问题等,但其中很多已经被早期入局的公司所解决。初学者可以从他们的总结和错误中学习,并遵循最佳实践来解决同样的问题。本文包括可观察性信号之间的区别以及应如何处理它们,列出解决常见问题时使用的几种不同方法的成功案例,并介绍了几种可观察性的工具以及如何实现自己的技术栈 ,最后介绍仍未解决的问题或者还未标准化的方法。

什么是可观测性

毫无疑问,可观察性是当今系统的理想属性。每个人都这么认为,对吧?你们中的一些人可能已经开始了可观察性之旅,而其他人现在正在阅读本白皮书,只是因为每个人都在说你应该让你的系统可观察。事实上,“可观察性”已经成为一个流行词,和其他流行词一样,每个人都想在倡导的同时留下自己的印记,但是这个词的起源和你所理解的可能有些不同。如果您要在可观察性方面升级您的程序,首先要明确其最初的目的。“在控制理论中,可观察性是衡量一个系统的内部状态可以从其外部输出的知识中推断出来的程度”[9]。理论上,它是一个系统的功能;人类和机器可以通过它观察、理解和控制系统的状态。根据定义,可观察性看起来很简单,但是在没有考虑目标的情况下决定系统应该具有哪些输出是很复杂的,这种情况下往往会出岔子 刚开始时,很容易复制别人的工作。这是使用开源的优势,同时也是开源的一种劣势。网上有很多例子,helm、ansible、trrafoem,只需运行其中一个脚本,您就可以在几分钟内建立并运行可观察性技术栈。这很容易,并且适用于其他人。因此它应该对我也有用,对吧?但是,我们并不是要鼓励您不要使用这些类型的脚本,可观察性不仅仅是使用所有漂亮而闪亮的工具。您必须意识到系统的输出是什么,比一切都更重要的是,您需要有一个目标!你可能会想:“哦,我想收集那些未来可能会用到的数据。” 然后对每种数据都存在这个想法,最后你会发现你可能正在构建一个数据湖。可观察性可用于系统开发生命周期的所有阶段。您可以在测试新功能、监控产品新能、了解客户如何使用您的产品或制定数据驱动的决策时使用它。一旦你有了自己的目标,你就会开始考虑输出,或者我们喜欢称之为信号的东西。

可观测性信号

如前所述,信号是系统产生的输出,人类或机器可以从中推断出知识。这些信号会因系统而异,并取决于您要实现的目标。它可以是您想要在某个时间点测量的东西,例如温度或 RAM 使用情况,也可以是您想要追踪的通过分布式系统的许多组件的事件。您可能想知道系统的哪个功能在随机时间点对 CPU、内存或磁盘等资源的占用最大,或者您的系统在确切时间如何发生故障的。虽然一些信号可能重叠以推断知识,但其他信号在系统的某些方面非常专业。它们都可以一起使用,以提供不同的方式来观察同一事物,或者,正如我们为初学者推荐的那样,您可以从一种或几种开始,然后逐步成熟到其他方式。您很有可能听说过“三个可观察性支柱”,即 metrics、logs 和 traces。它们是行业标准,也可能是您要开始使用的标准。我们喜欢将它们视为“主要信号”而不是“三根柱子”,原因有两个:(1)柱子带有隐含的含义,即如果缺少一根柱子,整个结构就会崩溃。但事实上并非如此, 我们可以只使用两个甚至一个信号,并且仍然可以实现其可观察性目标;(2) 去年,越来越多的信号在开源社区变得流行,例如profiles和 crash dumps,而今天的工具和方法仍然不能满足科技行业的所有需求。在不久的将来可能会出现新的信号,对这个话题感兴趣的人应该留意它们。

01.jpg

所有信号都有不同的收集或检测方式。它们具有不同的资源成本来获取、存储和分析,同时提供不同的方法来观察同一系统。与工程中的所有其他任务一样,在它们之间进行选择或全部进行选择是一种权衡游戏。在接下来的部分中,我们将通过深入挖掘每个信号来帮助您做出决定,首先是人们最喜欢的:metrics、logs和 traces,然后是两个新的可能信号:profiles 和 dumps。

Metrics

metrics是数据的数字表示。它们分为两大类:已经是数字的数据和提炼成数字的数据。前者的典型示例如温度而后者的典型示例如进程计数器。这与logs或traces不同,logs或traces侧重于有关单个事件的信息。提取数据会导致细节丢失,比如进程计数器无法获取何时发生特定增量的信息。这种权衡使metrics成为最有效的信号之一:主题专家选择了提取什么以及如何提取。这减少了保留、发送、传输、存储和处理的负载。它还可以减少运维人员的心理负担,因为他们可以快速了解情况。metrics也代表系统状态在一个时间点的状态。这与logs或traces不同,logs或traces侧重于有关单个事件的信息。metrics一般是结构化或半结构化的,通常有两种用途:

  • 实时监控和警报——metrics最常见的用途是看板,并在系统在超过阈值或行为异常时自动化触发报警。
  • 趋势分析——metrics还用在趋势分析,同时还可以在事件发生后提供历史数据分析,修复或监控以防止潜在问题再次发生。

metrics提供的信息可用于系统整体行为和健康状况的分析。metrics通常在“发生什么”,有时是“为什么”中发挥重要作用。例如,一个metric可以告诉您每秒 HTTP 请求的数量,但并不总是可以告诉您请求高峰或下降的原因。不过,它可以告诉您负载均衡器过载的原因。换句话说,metric并不总是揭示根本原因,通常作为发现问题的起点。

Logs

Logs是描述操作系统、应用程序、服务器或其他设备中活动和操作的文本流。日志可以分为不同的类别,例如:

  • 应用程序日志 - 当应用程序内部发生事件时创建应用程序日志。这些日志可帮助开发人员了解和衡量应用程序在开发期间和发布后的行为方式。
  • 安全日志 - 创建安全日志以响应系统上发生的安全事件。这些可能包括各种事件,例如登录失败、密码更改、身份验证请求失败、资源访问、资源更改(包括文件、设备、用户)或其他管理更改。系统管理员通常可以配置安全日志中包含哪些类型的事件。
  • 系统日志 - 系统日志记录操作系统本身发生的事件,例如处理物理和逻辑设备的内核级消息、引导顺序、用户或应用程序身份验证以及其他活动,包括故障和状态消息。
  • 审计日志 - 也称为审计跟踪,本质上是事件和更改的记录。通常,它们通过记录执行活动的人员、执行的活动以及系统如何响应来捕获事件。通常,系统管理员会根据业务需求确定为审计日志收集的内容。

基础设施日志 - 是基础设施管理的重要组成部分,涉及管理影响组织 IT 基础的物理和逻辑设备。这可以在本地或云端,通过 API、Syslog 或使用基于主机的代理收集的方式获得。

日志在许多场景中都很有用—— metrics、traces、security、debugging。保留所有应用程序和系统相关事件的记录可以了解甚至重现导致特定情况的逐步操作。这些记录在执行根本原因分析时非常有价值,可提供信息以了解故障时应用程序或系统的状态。存储在日志中的信息很随意,因此很难从中获得信息。在过去的 30 年中,有很多尝试将schema应用于日志,但没有一个特别成功。引入schema可以使得提取相关信息更加容易,通常这是通过解析、分段和分析日志文件中的文本来完成的。日志中的数据也可以转换为其他可观察性信号,包括metrics和traces。一旦数据成为一个metric,它就可以用来分析随时间的变化。日志数据也可以通过日志分析技术进行可视化和分析。日志级别允许表达每个日志语句的重要性。日志级别包括 ERROR、WARNING、INFO 和 DEBUG。其中,ERROR 是最不详细的级别,而 DEBUG 是最详细的级别。

  1. ERROR 传达发生故障的原因和详细信息。
  2. WARNING 是需要重点关注的告警消息。
  3. INFO 消息帮助我们了解系统是如何工作的。
  4. DEBUG 是存储每个操作的非常详细信息的级别。通常,仅在故障排除期间或由于存储或性能影响而短期使用。

可以通过多种方式转发日志。第一个建议是配置标准流以将日志直接发送到存储中心。其次,将日志写入消息队列以进行过滤或添加一些信息,然后发送至存储中心。最后一种方法是使用开源数据收集器应用程序将日志发送到存储中心。将日志与其他可观察性信号结合起来,可以全面了解您的系统。在规划日志记录解决方案时,我们必须关注安全性。在将与日志相关的文件或信息发送到存储中心时,对这些文件或信息进行加密。不要在日志中存储任何个人身份信息 (PII)。最后,真正重要的数据不应仅保存在日志中。尽管日志语句很有用,但不能保证它们会被传递。

Traces

traces是一种了解分布式事务期间发生的事情的技术,例如最终用户发起的请求及其对所有下游微服务的影响 traces通常是“跟踪数据点”的集合,或称为spans,并且可能会显示为甘特图,如下例所示:

2-1.jpg

traces 通常是数据流转的一个具体实例,是可观察性的高分辨率信号。span是高度上下文化的,包含有关启动span的信息。这使得在分布式系统的不同参与者(例如服务、队列、数据库等)之间建立因果关系成为可能。虽然许多监控系统实现了自己专有的trace上下文传播方式,但业界达成了广泛的共识,即traces上下文传播应该以标准化的方式进行。这导致了 W3C 分布式traces工作组的创建以及随后发布的 W3C traces上下文规范。受 OpenZipkin 项目事实上的标准 B3 的启发,W3C Trace Context 定义了标准 HTTP header 格式来传播trace。该规范标准化了上下文信息在服务之间的发送和修改的方式。上下文信息唯一地标识分布式系统中的各个请求,还定义了一种添加和传播特定于提供者的上下文信息的方法。现在 OpenTelemetry 和 .NET 正在使用 W3C Trace Context 作为他们的标准传播格式,并且可以预期云基础设施提供商也将开始支持 W3C Trace Context,这样上下文在通过托管服务时不会中断,例如服务网关。Instrumentation 在分布式跟踪中起着至关重要的作用,负责创建数据点本身并将上下文从服务传播到服务。如果没有上下文传播,我们无法将传入的 HTTP 请求与其下游 HTTP 请求或消息的生产者及其消费者联系起来。Instrumentation 对于分布式跟踪有两个主要目的:上下文传播和span映射。在大多数情况下,上下文传播是通过使用可以与 HTTP 客户端和服务器集成的库透明地完成的。在这一部分中,可以使用 OpenTelemetry API/SDK、OpenTracing 和 OpenCensus 等项目、工具和技术。

02.jpg

Profiles

随着公司不断优化云原生应用程序,了解尽可能细粒度的性能指标变得越来越重要。其他工具通常会显示存在性能问题(即延迟、内存泄漏等)。持续收集profiles使我们能够深入了解特定系统为何会遇到此类问题。有以下几种分析器:

  • CPU 分析器
  • 堆分析器
  • GPU 分析器
  • 互斥锁分析器
  • IO 分析器
  • 特定于语言的分析器(例如 JVM Profiler)

每一种分析器中都有许多子类型的分析,它们都具有相同的目标,即展示资源在系统之间的分配方式。传统上,分析器不适合在生产中运行,因为相关开销很大。然而,由于采样分析器越来越受欢迎,它们在云环境中变得越来越流行;它们只增加了百分之几的开销,使生产中的添加分析器成为一个可行的选择。为分析器数据添加“时间”轴使用户可以全局查看他们的数据,也能细粒度的观测数据。在优化或调试云原生应用程序或者规划如何分配资源时全面了解资源变得越来越重要。trace可以帮助你了解应用程序的哪个部分导致延迟问题,profilling则可以使您可以更深入地了解为什么存在这些延迟问题。此外,它还可以帮助您了解哪些代码占用了过多的资源。运行时生成的Profiling数据通常包含源代码行号的统计信息,因此它是分析“为什么”的重要数据。

Dumps

在软件开发中,core dump文件可用于对崩溃程序进行故障定位。操作系统在诸如位置、名称约定或文件大小等配置的帮助下,会在崩溃时把进程内存写入一个镜像中以供分析。然而,在云原生中,大型集群的核心转储文件集合很容易在存储甚至网络方面造成瓶颈,处理密集型应用程序最终可能会生几十G的核心转储文件。从内核 2.6+ 的Linux 的系统开始,有一种新的处理核心转储方法,即所谓的核心转储处理程序,可以通过全局设置 (/proc/sys/kernel/core_pattern) 将核心转储文件设置为写入系统中的任何位置。这意味着不是将文件的收集委托给操作系统,而是推送给应用程序。例如,在基于 Ubuntu 的发行版中,这可以在 systemd 或 abort 的支持下完成。基于 RedHat 的发行版可以使用 ABRT。在云原生环境中收集core dumps文件有两个难点:在云原生中,拥有应用程序和基础架构权限的人较少,因此对全局系统设置的清晰访问权限不太容易。另一个就是,云原生环境的数据持久性:应用程序(例如 pod)需要在崩溃重新启动之前收集其核心转储文件写入持久性卷,需要一些的技术来支持实现这一点。一个大约 5 年前的 RFC (https://lore.kernel.org/patchwork/patch/643798/) 要求 Linux 内核社区支持core_pattern命名空间 ,而不作为全局系统设置。此外,Docker 社区有同时间的open issue (moby/moby#19289),也要求 Docker 支持 core_pattern。

关联可观察性信号

毫无疑问,可观察性体系是复杂的。正如您从前面的部分中了解到的,为了更多地了解我们运行的软件的状态和行为,我们从不同的角度、不同的时间间隔和管道收集不同的数据类型:

  • Metrics:一段时间内状态的可聚合数字表示。
  • Logs:结构化 或者 可读的 离散化事件的详细信息
  • Traces: 可以绑定到系统中单个实体的生命周期的元数据位(例如请求或事务)。

我们还讨论了不属于上述类别,称之为信号的数据,例如:可持续Profiling:随着时间的推移,跨不同程序函数的各种资源的代码级消耗数(例如,MEM、CPU)。我们首先想到的疑问是,为什么要创建这么多的种类?难道我们不能只有一个“包罗万象”的东西吗?答案是不能,类似的,我们不能拥有一辆在柏油路和越野路都有效工作的自行车。每种类型的信号都针对其用途高度专业化。Metrics以实时、可靠和廉价的监控为中心,支持响应告警 - 可靠系统的基础。我们收集log lines,让我们更深入地了解有关正在运行的系统的较小细节,以获得更多上下文。在某些时候,详细信息会形成一个请求树,因此distributed tracing通过其spans和跨进程上下文传播发挥作用。有时我们需要更深入地研究,我们会转向performance application profiles以检查哪些代码效率低下并使用了意外数量的资源。正如您可能已经注意到的那样,对于一个完整、方便的可观察性系统来说,只有一个信号是不够的。例如,将太多细节放入metrics(基数)中的成本太高,而以警报所需的近实时延迟可靠地trace每个可能的操作也太昂贵。这就是为什么我们看到许多组织旨在为他们的可观察性系统安装和利用多个信号。

实现多信号可观测性

多信号可观测性是可行的,而且许多人已经做到了。但是当您退后一步,看看为实现这一目标必须构建什么时,您会发现一些主要挑战、错失的机会或效率低下:

  1. 管理多种系统 除非您愿意花钱购买 SaaS 解决方案,它会为您完成一些工作,否则现在很难让一个团队管理所有可观察性系统。拥有一个单独的专门团队来安装、管理和维护每个可观察性信号的情况并不少见,例如一个用于metrics 系统,一个用于 logs 记录技术栈,一个用于 trace。这是由于每个系统需要不同的设计模式、技术、存储系统和安装方法。这里边的细枝末节的工作是巨大的。这就是我们的目标是通过开源计划来改进,例如用于检测和转发部件的 OpenTelemetry 以及用于可扩展多信号后端的 Obsevatorium。

04.jpg

  1. 重复劳动 。当我们查看上述可观察性信号时,发现信号之间都会有明显的重叠。例如,让我们看一下上图。我们看到关于“数据在哪里”(通常称为“目标元数据”)的上下文对于每个信号都是相同的。然而,因为对于每种信号,都有一个独立的系统,我们会经常发现这些信息,通常是不一致的,保存在多个地方,并且(更糟!)多次索引和查询它。它不仅适用于目标元数据。许多事件会产生多个信号:增加指标、触发日志线和开始跟踪跨度。这意味着与此特定事件相关的元数据和上下文在整个系统中重复。在开源中,有一些尝试来减轻这种影响,但是进度缓慢,例如 Tempo。

  2. 采集时集成多种信号。鉴于多信号管道,通常需要用另一个信号的附加数据来补充每个系统。比如用metrics协议(例如 OpenMetrics/Prometheus)兼容特定traces和logs 集合创建metrics或类似地将logs组合到traces等功能。诸如 OpenTelemetry 收集器的处理器(从trace spans生成 RED metrics)和 Loki 将logs转换为metrics的功能等举措是该领域的一些现有尝试。

  3. 使用时集成多种信号。类似的,在“阅读”级别,快速导航到表示相同或相关事件的另一个可观察性信号将非常有用。这就是我们所说的信号相关性。让我们详细关注这个机会。现在有什么可以实现的?

信号相关性

为了将可观察性数据链接在一起,让我们看一下附加到所有信号的常见数据(如前所述,有时是重复的)。

05.jpg

由于收集所有可观察性信号的连续形式,每条数据都加上某个时间戳, 这允许我们在特定时间窗口内过滤信号数据,有时可达毫秒。由于上图的情况,在不同的维度上,每个可观察性信号通常都绑定到某个“目标”。要识别目标,必须存在目标元数据,这在理论上允许我们查看来自特定目标的metrics, profiles, traces 以及 log lines。为了进一步缩小范围,在采集模块中有很多向所有信号添加额外的元数据的代码组件,例如 “工厂”。

06.jpg

仅此一项就非常强大,因为它允许我们通过从一种信号中选择项目来快速导航其他信号。例如 Grafana 已经允许创建这样的链接和侧视图。但这不是全部。我们有时会有更多的细节,这些细节有时会附加到traces和logs中。特别是,分布式跟踪通过将所有spans限制在单个trace ID下。对于同一用户请求,此信息会从一个函数传递到另一个函数、从一个进程传递到另一个进程。在logs分享同样的信息也很常见,有时称为 Request ID 或Operation ID。通过统一这些ID可以实现traces和logs的联动。这使我们能够轻松地在绑定到单个请求的logs、traces和tags之间导航。

07.jpg

虽然对于某些用例来说这样的相关性水平可能已经足够了,但我们可能忽略了一个重要的问题:大规模!如此大的系统中的进程不会处理很少请求,他们为截然不同的目的和效果会执行数万亿次操作。即使我们可以从单个进程中获取所有logs或traces,但是您如何从当时正在处理的数千个并发请求中找到与您的目标相关的request ID、operation ID或trace ID?强大的日志记录语言(例如LogQL))允许您 grep 日志以获取日志级别、错误状态、消息、代码文件等详细信息。但是,这需要您了解可用字段、它们的格式以及它如何映射。如果针对某些端点的大量某些错误或高延迟的警报让您知道所有受影响的request ID,那不是更好吗?此类警报可能基于metrics,并且此类metrics在某些请求流中增加,这很可能还产生了log lines或trace,并分配了request ID、operation ID或trace ID,对吗?这听起来不错,但正如我们所知,这些数据在设计上是聚合的。出于成本和注意力的原因,我们不能传递作为聚合一部分的所有(有时是数千个)requests ID。但是关于我们可以利用的那些请求, 在这样一个聚合指标或日志的上下文中,所有相关的请求都是相似的!因此,可能不需要保留所有 ID。我们可以只附上一个,代表一个例子。这就是我们所说的 exemplar。

Exemplar: 某事的典型或很好的例子。

08.jpg

理论上,我们也可以将exemplars附加到profiles中,但考虑到它的专业化和用例(进程内性能调试),实际上我们很少需要链接到单个请求跟踪或日志行。

实际应用

我们讨论了在信号之间导航的方法,但它真的有用吗?让我们看两个简单的例子:

09.jpg

我们收到了 SLO 的高错误率的警报。通过metrics发现是请求高峰导致 501 错误。我们以exemplar导航到示例日志行以了解确切的可读错误消息。日志显示错误来自后端微服务,因此由于存在与request ID 匹配的trace ID,我们导航到traces。最终,我们确切地知道是什么服务/流程导致了问题,并在那里进行了更多的挖掘。

10.jpg

我们调试慢请求。我们通过跟踪采样手动触发请求并获取 trace ID.。由于跟踪视图的存在,我们可以看到在请求过程中经过了几个进程,这个例子中是 ABC-1 请求的速度非常慢。根据目标元数据和时间,我们选择了相关的 CPU usage metrics。并发现 CPU 使用率,接近机器限制,表明 CPU 饱和。要了解 CPU 使用如此频繁的原因(特别是如果它是容器中的唯一进程),我们使用相同的目标元数据和时间选择导航到 CPU profile 。

系统实践

可观测性可以在实践中实现吗?是的,但是根据我们的经验,大多数人并不知道如何实现。在这个领域有许多的公司和项目分别负责部分领域,正是由于这样导致总体框架比较混乱而且一些简单的解决方案难以发现。幸运的是,开源社区在简化和商品化这些方法方面付出了巨大的努力。让我们看看一些实现这种平滑、多信号相关设置的开源方法。为简单起见,假设您已经选择了一些metrics, logging and tracing技术栈(在实践中,通常会抽样logging or tracing 以降低成本)。从高层的角度来看,我们需要保证三个要素:

1.统一所有信号的元数据

这可能已经是一项艰巨的任务,但我们可以做一些捷径。这种捷径称为拉模型。例如,在 Prometheus 系统中,一致的元数据要容易得多,这要归功于单一、集中管理的发现服务。在许多其他好处中,拉模型允许度量客户端(例如您的 Go 或 Python 应用程序)只关心它自己的metric 元数据,完全忽略它运行的环境。相反,这对于推送模型来说很难维护,例如 Logstash、 non-pulling OpenTelemetry receivers、 non-tailing plugins for Fluentd、Fluentbit。想象一下,一个应用程序把所在的节点定义为node,另一个则把它定义为machine,还有一个将它放在 instance标签中。在实践中有以下几个建议:

  • 假设我们坚持推送模型(对于某些情况,例如强制执行批处理作业),我们需要确保tracing, logging, and metrics添加正确且一致的元数据。在实践中使用 我们使用一些跨编程语言的三方软件(例如 Postgres)可以同步元数据,但是同步可能需要数年。或者,可以在代码层面进行同步, Service Meshes 可能对标准的进入/退出可观察性有所帮助,但无法做到开箱观测。实现这一点的另一种方法是使用可以动态重写元数据的插件,例如 OpenTelemetry, 然而在实践中这种方案随着时间的推移难以维护。
  • 第二种方案是使用拉取模型,在控制端定义目标的元数据。我们已经在Prometheus 或 Agent的开源中做到了这一点,这要归功于OpenMetrics 不断地抓取metrics,ConProf 对配置文件做同样的事情。同样,已经有许多解决方案可以从标准输出/错误中跟踪您的日志,例如 Promtail 或 OpenTelemetry tailing collector。不幸的是,还没有发现任何实现项目获取traces数据(目前)。

2.使 Operation ID, Request ID or Trace ID 统一并附加在日志系统中

这部分必须在仪器级别上得到保证。多亏了 OpenTelemetry 上下文传播和 API,我们可以通过获取trace ID(理想情况下,仅在trace被采样时)并将其添加到与此类请求相关的所有日志行中。使其统一的一种非常好的方法是利用中间件 (HTTP) 和拦截器(gRPC)编码范例。值得注意的是,即使您不想使用tracing系统或非常严格tracing 采样,在日志记录中生成和传播request ID 仍然很有用。这允许将单个请求的日志行关联在一起。

3.Exemplars

Exemplars 在开源空间新出现的,所以让我们来看看目前有什么可能以及如何采用它们。将exemplars添加到您的日志记录系统非常简单。我们可以为聚合多个请求的日志行添加一个简单的 exemplar-request= 键值标签形式的 exemplar。为metric添加exemplars却不太一样。这可能值得我有一天写一篇单独的文章,但正如您想象的那样,我们通常不能将request ID或trace ID 直接添加到metric series元数据(例如 Prometheus 标签)。这是因为它会用一个样本创建另一个一次性的、独特的series(导致无限的“cardinality”)。然而,我们可以使用 OpenMetrics 定义的一种相当新颖的模式,称为Exemplar.。这是附加信息,附加到(任何)series sample,在主要(高度索引)标签之外。这就是它在 OpenMetrics 文本格式中的外观,例如 Prometheus:

# TYPE foo histogram
foo_bucket{le="0.01"} 0
foo_bucket{le="0.1"} 8 # {} 0.054
foo_bucket{le="1"} 11 # {trace_id="KOO5S4vxi0o"} 0.67
foo_bucket{le="10"} 17 # {trace_id="oHg5SJYRHA0"} 9.8 1520879607.789
foo_bucket{le="+Inf"} 17
foo_count 17
foo_sum 324789.3
foo_created 1520430000.123

定义后,它们会被 OpenMetrics 兼容的抓取工具(例如 Prometheus)与 metric samples(确保在您的检测客户端中启用 OpenMetrics 格式)一起抓取。完成后,您可以通过 Exemplars API 查询这些示例:

curl -g 'http://localhost:9090/api/v1/query_exemplars?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": 1600096945.479,
        }
      ]
    }
  }
(...)

请注意,query 参数不是专门 ExemplarsQL 设计的, 此 API 希望被用在任何 PromQL 查询的地方例如看板、告警或者规则。该仪表支持解析这些query参数以及所有用过这个参数的series,并返回这些series相关的exemplars。这个 API 很快就被 Grafana 采用了,即使是现在,您也可以在最新版本的 AGPLv3 许可 Grafana 上呈现exemplars并允许快速链接到trace视图。当然,这只是基础。Prometheus 中有完整的基础设施和逻辑在 2021 年初完成,以支持在远程写入对examplars进行抓取、存储、查询,备份。Thanos,Grafana也开始支持exemplars 。还值得一提的是,OpenTelemetry 还继承了 OpenCensus 的某种形式的exemplars。这些与 OpenMetrics 非常相似,只可附加到histogram buckets.。然而,我们不知道有人在任何地方使用或依赖这部分 Otel metric协议,包括 Go 等相关实现。这意味着,如果您想拥有稳定的生态系统,OpenMetrics 可能是首选。另外,OpenTelemetry 也慢慢采用了 OpenMetrics。

应用案例

基于箱体的监控可以分为两种策略

  • 开箱监测
  • 关箱监测

一般而言,关箱监控是指从外部观察系统,操作员无法控制或了解系统的内部运作。另一方面,开箱监控是指更“传统”的监控概念,即监控那些你知道它如何运作并可以控制的系统,因此您能够更好地决定如何通过可观察性的三大支柱去监控它。

实施 SLI、SLO 和 SLA

SLI、SLO 和 SLA 指标可以让您客观地衡量服务质量和客户满意度。更重要的是,它在组织内的业务、产品和工程等不同职能之间提供了一组通用术语。工程时间在任何组织中都是稀缺资源,但每个人都觉得他们的问题是一个紧迫的问题。SLO 使此类对话更加受数据驱动,因为每个人都了解违反 SLO 的业务后果。在解决内部冲突的同时,它还通过提供有意义的抽象来实现有意义且可操作的告警,从而使您更加专注于客户。在深入研究实现细节之前,我们应该清楚定义,因为它们可能会相当混乱,有时可以互换使用。

  • 服务水平指标 (SLI):SLI 是一种服务水平指标——对所提供的服务水平的某些方面进行仔细定义的定量测量。
  • 服务水平目标 (SLO):SLO 是一个服务水平目标:即您可以承受的失败频率。由 SLI 测量的服务水平的目标值或值范围。
  • 服务水平协议 (SLA):包含违反 SLO 后果的商业合同。这是一个目标百分比错误预算:SLO 确定的一段时间内失败事件的容差。等于 100% 减去 SLO。

为了使提议的 SLO 有用且有效,您需要让所有利益相关者都同意它。产品经理必须同意这个阈值对用户来说已经足够好了——低于这个值的性能令人无法接受,值得花工程时间来修复。产品开发人员需要同意,如果错误预算已经用尽,他们将采取一些措施来降低用户的风险,直到服务恢复到预算范围内。负责维护此 SLO 的运维的团队已经同意,无需付出巨大的努力、过度劳累和倦怠,否则会损害团队和服务的长期健康。

11.jpg

可观察性数据告警

在广泛采用 metrics 收集之前,大多数软件系统仅依靠日志来解决和分类问题并获得对其系统的可见性。除了日志搜索和看板之外,日志还充当许多团队和工具的主要警报源。这种方法今天仍然存在于许多现代可观察性系统中,但通常应避免使用,以支持对时间序列metrics进行警报。更具体点说,我们将研究使用您定义的 SLO 和错误预算来执行可操作的告警。您的时间序列数据中有许多信号可以提醒您,其中许多可能是特定于应用程序的。推荐的最佳做法是使用团队的 SLO 来驱动告警。如上所述,SLO 是一个服务水平目标,即由服务水平指标衡量的服务水平的目标值或值范围。例如,REST API 的 SLO 可能是“95% 的请求必须在 500 毫秒内得到处理”。为了为您的团队提供有效的警报,您还应该定义错误预算。我们将研究如何结合您的 SLO 和错误预算来推动可操作的告警。

告警实践

构建警报可能非常复杂,很容易被误报淹没并产生警报疲劳。警报应该是可操作的,并指示某人需要采取行动的问题。我们将在下面介绍您可以实施的两种方法,一种是简单的方法,另一种是基于消耗率的方法。

目标错误率

对目标错误率发出警报是您可以采取的最简单的方法。选择一个较小的时间窗口,比如 10 分钟,并在该窗口中的错误率超过您的 SLO 时发出警报。例如,如果您的 SLO 为 99.9%,则在过去 10 分钟的错误率 >= 0.1% 时发出警报。在 Prometheus 中,这可能看起来像这样(HTTP 请求错误总数除以过去 10 分钟内所有请求的总和):

(sum(rate(http_requests_total{code=~"5.*"}[10m])) / sum(rate(http_requests_total[10m]))) > 0.001

这样做的优点是可以简单直接地查看警报逻辑中发生的情况,并在遇到错误时快速发送警报。但是,有时在不违反您定义的 SLO 时,也会触发告警。

燃烧率

燃烧率警报是一种更复杂的方法,并且可能会产生更多可操作的警报。首先,让我们更详细地定义消耗率和错误预算。所有 SLO 定义中固有的是错误预算的概念。通过声明 99.9% 的 SLO,您的意思是在某个预定义的时间量(您的 SLO 窗口)内,0.1% 的故障率(即您的错误预算)是可以接受的。“燃烧率是相对于 SLO 服务消耗错误预算的速度”。因此,例如,如果“服务使用 1 的消耗率,这意味着它消耗错误预算的速度使您在 SLO 时间窗口结束时的预算正好为 0。一段时间内的 SLO 为 99.9% 30 天的窗口,恒定的 0.1% 错误率完全使用了所有错误预算:消耗率为 1”。

12.jpg

燃烧率 99.9% SLO的错误率 持续时间
1 0.1% 30 days
2 0.2% 15 days
10 1% 3 days
1000 1000% 43 minutes

燃烧率将使我们能够减小窗口大小并创建具有良好检测时间和高精度的警报。对于我们的示例,假设将警报窗口固定为一小时并确定 5% 的错误预算支出足以通知某人,您可以推导出用于警报的消耗率。对于基于燃烧率的警报,发出警报所需的时间为:

(1 - SLO / error ratio) * alerting windows size * burn rate

告警触发时消耗的错误预算为:

(burn rate * alerting window size) / time period

因此,5% 的 30 天错误预算花费,一小时需要燃烧率为 36。告警规则现在变为:

(sum(rate(http_requests_total{code=~"5.*"}[1h])) / sum(rate(http_requests_total[1h]))) > 36 * .001

可观测性的差距

多信号关联

上面的文章很好地解释了可观察性相关性是什么,它意味着什么以及如何实现。这里,让我们快速列举一下当今多信号可观测性关联的缺陷:

  • 元数据不一致 如前所述,即使标签之间存在轻微的不一致,在使用时也可能会很烦人。重新标记技术或默认为拉模型会有所帮助。
  • 在日志系统中缺失request ID 或者 其他ID连接到Tracing ID 如前所述,这可以在instrumentation方面解决,这有时很难控制。中间件和service meshes也可以提供帮助。
  • 棘手的tracing采样 收集所有请求的所有traces and spans可能非常昂贵。这就是为什么一个项目定义了不同的采样技术,来sample那些以后可能有用的traces。定义哪些traces是重要的并非易事,因此出现了复杂的抽样。主要问题是确保日志系统中的exemplars或者trace ID 能够链接到采样的trace。如果我们的前端系统会暴露一个无法链接的exemplar(存储中没有可用的trace),那将是一个糟糕的用户体验。虽然可以在 UI 方面改进这种体验(例如,在呈现exemplar之前预先检查是否存在trace),但它并布容易解决,并且会给系统带来进一步的复杂性。理想情况下,我们可以在将exemplar 注入到metric系统之前检查是否对trace进行了采样。OpenTelemetry 编码 API 允许通过IsSampled获取采样信息 。当使用tail-based的采样或分析哪个trace可能是重要的时,就会出现问题。暂时还没有来改善这个小而烦人的问题的方法。如果您进行全采样或基于预先抽样规则(请求比率或用户的选择),这个问题就会消失。
  • examplars 尚未广泛应用 Prometheus 用户体验特别好,因为 Prometheus/OpenMetrics 是事实标准。世界各地的软件都使用这种简单的机制来添加 metrics。因为 Prometheus exemplars 是新的,OpenTelemetry tracing 库也是新的,所以广泛通过exemplars 来 “instrumenting their instrumentation”需要时间。但是!您可以先将 Prometheus exemplars 添加到您的应用程序中。这种联动模式正在成为一种新标准(例如在Thanos进行检测 ),因此通过将使用这些工具可以帮助您和您的用户,在tracing and metrics之间实现联动。
  • 高级别的metric聚合和降采样 给那些通过汇聚了 许多包含exemplars 的metrics 的规则和告警添加exemplars 的能力还没有完成。这项能力已经提上了日程。同样,我需要在后续迭代Prometheus时考虑如何进行exemplars的降采样。
  • UI 中的本地关联支持。Grafana 在多信号联动方面处于领先地位,其他联动支持更好的UIs也在本文中分享了。在 Grafana 之前,每个信号通常都有自己的视图,很少考虑其他信号。Prometheus UI也添加了其他信号的连接或者展示 exemplars。

参考文献

  1. HARTMANN, Richard. Talk given at Fosdem (Brussels), Feb 2019. Available at: https://archive.fosdem.org/2019/schedule/event/on_observability_2019/. Accessed on: June 24, 2021.
  2. SRIDHARAN, Cindy. Distributed Systems Observability. Chapter 04, The Three Pillars of Observability. 2018. Available at: https://www.oreilly.com/library/view/distributed-systems-observability/9781492033431/ch04.html. Accessed on: June 24, 2021.
  3. BEYER, Betsy; JONES, Chris; MURPHY, Niall; PETOFF, Jennifer. Site Reliability Engineering. O’Reilly Media, 2016. Available at: https://sre.google/sre-book/table-of-contents/. Accessed on: June 24, 2021.
  4. BEYER, Betsy; MURPHY, Niall; RENSIN, David; KAWAHARA, Kent; THORNE, Stephen. The Site Reliability Workbook. O’Reilly Media, 2018. Available at: https://sre.google/workbook/table-of-contents/. Accessed on: June 24, 2021.
  5. SRIDHARAN, Cindy. Monitoring and Observability. Sep 5, 2017. Available at: https://copyconstruct.medium.com/monitoring-and-observability-8417d1952e1c. Accessed on: June 24, 2011.
  6. MCCARTHY, Kate; FONG-JONES, Liz; FISHER, Danyel; MAHON, Deirdre; PERKINS, Rachel. Observability Maturity: Community Research Findings Q1, 2020. April, 2020. Available at: https://www.honeycomb.io/wp-content/uploads/2020/04/observability-maturity-report-4-3-2020-1-1.pdf. Accessed on: June 24, 2021.
  7. Kalman R. E., On the General Theory of Control Systems, Proc. 1st Int. Cong. of IFAC, Moscow 1960 1481, Butterworth, London 1961. Available at: https://www.sciencedirect.com/science/article/pii/S1474667017700948?via%3Dihub. Accessed on: June 24, 2021.