这篇文章的主要内容是展示Helios内部利用开源项目和创造性思维快速高效地向客户提供基于链路跟踪的告警机制。
卓越工程团队的品质之一是跳出框框思考,找到解决难题的创造性解决方案。作为开发领导者,我们有责任向下一代开发人员传递技术方向和实现技巧,帮助他们超越表面,解决复杂的业务问题,并在可能的情况下利用开源社区的力量。
我们将复杂的逻辑委托给一个经过验证的开源项目(Prometheus)。我们致力于将它的告警机制纳入我们的产品中。现在,告警并不是什么新鲜事——许多软件产品都提供警报来通知用户系统/产品中的事件;它不是新鲜的事件,但是这并不意味着它没有挑战性。我们通过利用OpenTelemetry收集器来适配已在使用的 Prometheus(更具体地说,AWS 托管 Prometheus,我们选择使用它来减少内部管理的维护开销)来解决这一挑战,在这样既满足了用户的产品需求,又为我们节省了大量的开发和维护时间和精力。
在这篇博文中,我将详细介绍这个解决方案,并希望它能够激励开发人员创造性地思考他们可能遇到的日常挑战。我希望我们的经验能够展示如何使用开源项目构建解决方案,从而大大提高效率,以便工程团队可以花费宝贵的时间解决更多业务挑战。
在以下部分中,我将介绍:
OpenTelemetry (OTel) 是一个开源可观测性框架,可帮助开发人员从分布式应用程序生成、收集和导出遥测数据。我们通过 OTel 收集的数据包括一些不同的信号:分布式跟踪数据,例如 HTTP 请求、数据库调用、发送到各种通信基础设施的消息,以及 CPU 使用率、内存消耗、OOM 事件等指标。
我们着手根据这些数据以及其他来源的数据构建一个告警机制,以使我们的用户能够配置条件,根据这些条件配置系统中的告警。例如,用户可能会收到有关失败的 API、耗时超过预期的数据库查询或 OOM 的Java虚拟机的警报。他们基本上可以根据他们想要的粒度和所需的通知频率来设置告警。
正如我之前提到的,许多软件产品都能提供告警机制,以便用户可以获得其应用程序中发生的事件或其他重要业务 KPI 的更新。这是一个常见的功能,但构建起来仍然很复杂。在我们的解决方案中,我们希望实现三件事:
为了做到这一点,我们转向开源,我们利用 Prometheus 的告警管理器模块。Prometheus 是一个开源的监控和告警行业标准,旨在跟踪应用程序和基础设施的性能和运行状况。Prometheus 从各种来源收集指标,并提供灵活的查询语言来分析和可视化数据。它是收集OTel指标的最常见后端之一,我们的后端已经有 Prometheus 来支持指标收集。
我们依靠像 Prometheus 这样的开源工具来为我们做跑腿工作,因为此类解决方案是由很多位聪明且经验丰富的开发人员构建的,他们在这些解决方案上工作了多年,对其进行了调整以支持许多用例,并且已经完成了所有(或至少是该领域中的大多数陷阱。我们对警报机制的设计进行了内部讨论,利用 Prometheus 的想法是团队的一些成员根据他们之前的使用经验提出的。
设置基于分布式跟踪数据的警报——由 Prometheus Alert Manager 提供支持,该 标签 可以在 Helios Sandbox 中访问
如何在 Prometheus 中配置来自 Helios Sandbox 的不同警报的示例
有了Prometheus,我们就开始添加警报机制。我们希望首先对跟踪发出警报,或者更准确地对跨度 (例如,HTTP 请求或数据库查询的结果)发出警报。Prometheus 提供指标警报,但我们需要跟踪警报。来自跟踪的数据不会按原样到达 Prometheus – 它需要转换为其数据模型。因此,为了让 Prometheus 根据实际跨度发出警报,我们需要获取一个链路跟踪数据,将其转换为指标,并配置由它触发的警报。当链路跟踪与警报条件匹配时(例如,数据库查询时间超过 5 秒),我们将跨度转换为 Prometheus 指标。
Prometheus模型符合我们的目标。对于每个事件,我们从 OTel 获取原始数据,并通过 Prometheus 将其作为指标提供。例如,如果特定操作错误在五分钟内发生超过 3 次,则应该激活警报。
我们并没有就此止步。在 Helios 中,对用户来说的一个主要好处是,我们可以从分布式链路跟踪数据转换为指标,也可以从指标返回到特定链路跟踪,因为我们维护指标的上下文。用户可以设置基于跟踪的警报,然后从警报返回到端到端流程以进行快速根本原因分析。这使用户能够最终了解其应用程序的性能和运行状况。可用的上下文(基于检测的数据)可帮助用户轻松查明应用程序流程中的问题和瓶颈,以便快速排除故障并加快故障平均解决时间 (MTTR)。
在我们的警报机制中,目标旨在对可在跟踪数据上定义的行为发出警报,例如服务 A 向服务 B 发出的失败的 HTTP 请求、对特定集合的 MongoDB 查询花费了超过 500 毫秒,或 Lambda 函数调用失败。
上述每个可以描述为基于标准 OTel 属性(如 HTTP 状态代码、跨度持续时间等)的链路追踪过滤器。在这些过滤器之上,我们支持各种聚合逻辑(例如,如果匹配链路追踪的数量在 Y 周期内达到 X)。因此,警报定义本质上是一个过滤器和一个聚合逻辑。
实现由三部分组成:
我们希望尽可能保持 OTel 原生,因此通过执行以下操作,基于 OTel Collector 构建了警报管道:
Prometheus 几乎是开箱即用的,但由于它是由 AWS 管理的,因此我们必须注意一些小细节(例如,只能使用 SNS-SQS 报告警报。
因此,我们有基于跟踪的警报,但为了确保快速进行根本原因分析,我们还希望在触发警报时提供完整的应用上下文。触发警报后,我们会向 Prometheus 查询警报定义的时间序列(如前所述,客户和警报定义 ID 的组合),并获取指标列表作为警报查询的实例 - 每个指标都有其匹配的跨度和跟踪 ID。例如,如果针对长时间运行的数据库查询配置警报,则示例跟踪将包含查询本身及其整个链路跟踪过程。
整个机制看起来像这样:
Helios 的警报机制架构 – 从客户的 OpenTelemetry SDK 报告的跨度到 Slack 中的警报
Helios Alerts Collector 架构 – 涵盖从跟踪管道到指标管道的转换
我们用于警报机制的方法是将 OTel 跟踪数据转换为 Prometheus 指标,以便利用 Prometheus 的警报管理器,从而无需实现我们自己的警报后端。让我们看看这种方法的一些缺点和优点。
尽管有很多好处,但有时使用开源工具(或您的团队无法控制的任何外部组件)可能会很棘手,因为如果它的 API 和集成机制不适合您的需求,您实际上得到的是一个“黑匣子”架构,你可能有更多的工作要做,但完全被阻止。
让我们看一个例子。在 Prometheus 中,配置警报是通过使用 API 调用更新其 YAML 定义来完成的。然而,我们使用的 AWS 管理的 Prometheus 支持通过 AWS API 调用更新这些定义,这不会直接更新 Prometheus,而是定期同步进行实际更新。为了防止这种行为出现问题(例如,由于第一次更新尚未同步而导致对警报定义的连续更新失败),我们必须实现自己的定期同步机制来封装更新。如果我们从头开始构建这个解决方案,我们就可以完全控制这个机制,并且可以随时进行更新。在这里,使用 AWS Managed Prometheus,我们没有这种控制权,这迫使我们构建额外的同步机制。
此外,您可能想要调整解决方案的一些功能 - 即在我们的例子中,我们希望在发送警报时提供更精细的数据 - 这可能是一个繁琐的过程。例如,获取在收到警报后直接触发的警报的匹配跨度 ID(即,作为 Prometheus 报告的警报有效负载的一部分)对我们来说并不适用,因此我们必须向 Prometheus 发送另一个 API 调用并查询它们,增加一些小的开销。
尽管存在这些挑战,我们知道,在不依赖 Prometheus 的情况下自己实现此功能会困难得多。我们没有从头开始开发警报逻辑,这涉及设计(不同的组件、存储等)、实施,可能还需要多次迭代的错误修复和反馈,而是提供了一个开箱即用的解决方案,为我们节省了大量的开发时间时间。
Prometheus 是一款经过验证的开源工具,具有丰富的功能,我们知道有了 Prometheus,我们就可以高枕无忧了。我们知道该工具将涵盖未来的用例,并且它将做好生产准备,并将经过众多用户对其进行塑造和微调,这给了我们很大的信心,同时节省了我们的时间。我们知道,我们将来可能想到的任何警报逻辑很可能已经在 Prometheus 中实现。如果我们自己构建它,错误的设计选择可能意味着我们将不得不破坏我们的设计或编写糟糕的代码来支持新的用例。
此外,我们方法的好处之一是使所有内容都原生于 OTel 数据模型。这意味着所有内容都会被 OTel 收集器过滤、处理、导出和接收,并且无论它是链路追踪指标(例如失败的 HTTP 请求)还是监控指标(例如高 CPU 速率)都无关紧要。
在Helios中开发警报机制可能很困难,但通过一些创造性思维和开源协作,我们高效且自信地实现了这一目标。我们利用 OTel 和 Prometheus,在可靠的周转时间内交付了复杂的警报机制。我们找到了一种将链路追踪跨度和指标关联起来的方法,这样当我们获取链路追踪数据跨度并将其转换为指标时,我们就知道如何将警报连接回业务逻辑。
我们希望这次经历不仅能激励开发者使用开源解决复杂的问题,还能成为我们用户的良好合作伙伴。创新是关键,但除了为了创新而创新之外,我们还希望对用户产生影响并改善他们的体验 - 我们希望您也能这样做。