1 引言
2020年5月12日下午4:45,云业务通信平台Slack经历了一次全面服务瘫痪。对其数百万用户来说,服务中断持续了48分钟;而在Slack内部,导致此次故障的一连串事件始于上午8:45。这一切都始于一个软件性能错误,该错误在例行代码部署过程中被发现并立即回滚。这触发了Slack的网络层的自动缩放,使其增加到比LB允许的硬限制更多的实例。这反过来暴露了Slack如何更新LB中的主机列表的一个错误:一些LB混合了旧的和新的未注册的主机实例。8个小时后,唯一的活动主机实例是仍然注册到LB的最旧的主机实例。当自动缩放程序开始在晚上缩小主机时,它关闭了这些旧实例。由于注册到LB的所有剩余实例要么是陈旧的,要么是新的且未注册的,因此该服务经历了全面中断。
虽然对Slack事件的描述列出了导致停机的事件的逻辑时序,但确定问题的根因却需要“全员参与”。告警一发出,来自多个部门的工程师团队聚在一起,根据通过其监控数据、仪表板和告警基础设施,探讨了几种可能的假设。这一事件说明了软件行为可观测性,不仅在Slack,也在许多其他大型软件公司都是一种关键能力。
今天的网络规模、面向用户的软件系统和服务(如Slack、谷歌、脸书、推特)都是构建并运行在由弹性/共享基础设施管理的微服务上。云原生软件生态系统的分布式、异构化和复杂性越来越高,使得预测它们在面对故障和变化负载时的行为变得很有挑战性。可观测性正在成为监控和维护云原生系统的关键功能,以确保其为客户提供的服务质量。取自控制理论,通过在运行时从系统收集的遥测数据,可观察性的概念为理解软件的复杂行为带来了更好的可见性。除了简单的黑盒监控之外,可观察性为系统的正确性和性能提供了更深入的上下文洞察。其目标是缩短洞察时间,这是了解系统运行情况及原因的关键衡量标准。因此,可观察性本质上是一个涉及到各种人员(例如DevOps团队)的数据密集型和时间敏感的处理过程。
我们将可观测性视为一个数据管理问题。系统被测量并生成大量异构时间序列数据,这些数据必须被近实时地索引、存储和查询。测量可以发出四类数据:
(i)指标 metrics:仪表和计数等数值数据
(ii)事件 events:关于系统事件的高度结构化数据
(iii)日志 logs:非结构化字符串的日志
(iv)跟踪 traces:请求的执行路径
由于可观察性对于拥有数百万用户的网络公司在满足服务级别目标(SLO)时至关重要,因此这个领域已举办了很多行业活动。然而,目前的解决方案是由各种专业工具的拼凑而成,以满足每个时间序列数据类型的不同需求。这些特定的解决方案不能很好地扩展,并导致较高的性能开销、操作复杂性,以及基础设施成本。人们越来越需要重新思考数据和软件基础设施的当前设计以便实现大规模可观测性数据管理。
在这篇愿景论文中,我们以Slack的可观测性基础设施为例分析了可观测性工作负载的数据管理要求(第二节)和当今系统所面临的挑战(第三节)。然后,我们确定了构建网络规模ODMS的一般设计原则,并提供实现这些目标的新架构蓝图(第四节)。
2 指标、事件、日志、跟踪
ODMS的基本挑战在于及时洞察系统在面对海量数据和异构性时的状态。本节分析了四类可观察性数据:指标(metrics)、事件(events)、日志(logs)和跟踪(traces),一起简称MELT。我们首先讨论这些类别的独特特征,然后探讨它们对数据管理的共同要求。
2.1 指标metrics
指标metrics提供了对特定时间点的系统性能和可用性的定量测量。它们包含三种类型的数字数据:
(i)counters(计数器),只能增加或重置为零的值(例如,接收到的HTTP请求总数);
(ii)gauge(计量器),可以向上或向下以反映系统状态(例如等待响应的HTTP请求的数量);
(iii)histogram(直方图),对固定时间间隔内的观测结果进行采样,并在可配置的桶中对其进行计数(例如,HTTP请求持续时间或响应大小)。
除了数值和时间戳之外,指标metrics还包括作为一组键值对的元数据,称为tags(标签)。tags标识metrics的特定实例。metrics和tags的唯一组合称为时间序列。一对时间戳和一个值被称为样本。下图提供了一个示例指标http,用于测量每秒HTTP请求数。每个测量都标记有请求来源的数据中心、主机和路径元数据。在这个例子中有三个(颜色编码的)时间序列,每个序列的时间戳都单调增加。
metrics以两种方式使用:(i)生成关于异常系统状态的告警,或(ii)用于分析和仪表板查询。告警是使用对最新数据的小查询生成的(例如,每分钟每台主机的HTTP请求总数)。分析查询可能涉及任意时间的数据,以观察系统范围的趋势(例如,最后一天dc1中每分钟每主机的HTTP请求总数)。还可以执行更复杂的分析(例如,相似性搜索或聚类)。
指标需要一个混合存储引擎。Prometheus是一种广泛使用的指标存储引擎,它对metrics值采用压缩存储策略,对相关的标签采用倒排索引。这种策略有利于实现高压缩率和快速过滤操作。其他存储器还提出了策略(例如,数据序列存储[17])。Slack以每秒1200万个样本的速度,每天生成约40亿个时间序列,每天收集12TB(压缩)的指标数据,并将这些数据保留30天。
2.2 事件events
事件events是在运行时发出的高度结构化的数据。通常,它们来自一组有限的可能值。例如存在9种HTTP请求方法(例如,GET、POST)和一组有限的响应状态码(例如“404: Not Found”)。事件也可以用于具有更高维度的高基数数据(例如,客户ID和网络地址)。由于事件是以结构化字符串或紧凑的二进制格式发出的,因此先前关于可观察性的文献通常将事件视为日志的子类别(在2.3小节中讨论)。我们分别对它们进行分类,因为事件的数据模型、查询和访问模式与日志的数据模型和访问模式有很大不同。
下图显示了处理HTTP请求和响应的系统发出的原始事件的摘录。
除了计算趋势之外,事件通常用于识别异常系统状态的特定实例。例如,像下面这样的SQL查询将显示来自查询的IP地址192.168.100的所有POST请求。
SELECT * from Events WHERE ip=192.168.100.11 and method="POST"
数据的结构化特性和低选择性过滤的查询使列式存储成为事件的理想存储模型。事件存储中的数据通常通过列式存储的SQL查询进行访问。Slack每天生成超过250TB的原始事件数据,并在任何时候存储超过70PB的数据。出于存档或法律审计的目的,事件数据保留的时间相对较长(3-24个月)。
2.3 日志logs
日志是半结构化或非结构化字符串的集合,并通过丰富的本地上下文暴露高细粒度的信息。这种灵活性使得日志对于理解服务中发生意外行为的原因至关重要。例如,在应对停机事件时,Slack依靠日志来识别发生在其LB主机列表更新过程中的错误。类似地对于HTTP请求,开发人员可能会在错误日志中打印堆栈跟踪以及应用程序状态的异常(如下图)。
与指标不同,日志通常包含上下文信息,可以为以下问题提供更详细的答案:“是什么服务器错误导致响应的状态为500?”。这些大海捞针的查询与对事件提出的完全匹配查询有根本不同。由于需要字符串的近似匹配,因此最好使用基于倒排索引的存储解决方案。Slack每天收集约90TB的日志数据,以便保留7天。
2.4 跟踪Traces
跟踪traces封装了关于请求执行路径的信息,类似于调用图。在微服务和分布式环境中,跟踪是跨分布式服务的调用图,包括RPC调用、异步队列和其他服务间通信。traces表示为有向无环图(DAG),其中顶点表示被称为span的执行单元(比如函数调用),边表示从一个顶点到另一个顶点的因果顺序。随着OpenTelemetry等行业的努力,这个定义越来越被接受。
下图显示了通过GetProduct服务查找产品请求的跟踪(trace)摘录。为了响应此请求,GetProduct调用ProductLookup服务,该服务又进行名为MySQLSelect的数据库调用。GetProduct然后对结果进行格式化,并最终对请求做出响应。trace及其spans封装了此请求的执行路径的结构以及有关此路径的信息。span捕获了执行的持续时间和以键值对形式存储为标签的元数据。
访问特定跟踪通常有两个步骤:
(i) findTraces:用户搜索符合特定标准的trace(例如,自昨天以来超过200ms的HTTP请求,其中DB调用返回至少2行数据);
(ii)getTrace:在返回的trace列表中,用户选择一个特定的trace以甘特图的形式查看。
尽管链路跟踪在分布式系统中已经使用了几十年,但对跟踪数据的存储和管理的研究却很少。
在Slack公司,由于测量和管理跟踪数据方面的挑战,跟踪数据量低于其他数据量(例如,2TB/天)。为了管理这种复杂性,Slack以上图所示的格式表示span,类似于OpenTelemetry的跟踪API。
2.5 MELT数据的共同特征
正如前面小节中所讨论的和下表中所总结的,ODMS中四种类型的时序数据、查询和存储模型差异很大。然而,MELT数据也有几个共同的重要特征,这些特征会影响它们的整体管理方式。
数据 |
类型 |
请求 |
存储 |
容量 |
保留时间 |
metrics |
数值 字符串元数据 |
聚合 带有元数据过滤条件 |
压缩时序数据 混合列存储 |
40亿时序/天 1200万样本/天 12TB/天(压缩后) |
30天 |
events |
高度结构化字符串或二进制数据 |
按特定字符串的完全匹配过滤 |
列存储 |
250TB/天(原始) |
3~24月 |
logs |
半结构化字符串 |
字符串的近似搜索 |
倒排索引 |
90TB/天(原始) |
7天 |
traces |
关于执行时间的DAG |
割裂的图搜索 |
列存储/倒排索引 |
2TB/天(原始) |
14天 |
- 数据特征:基本上所有MELT数据都是不可变的,并且追加量很大。此外,由于动态分布式环境,随着时间的推移生成的数据量是高度可变的(即突发性)。例如,一个重大的自动缩放事件或一个新的日志异常可以在短时间内将数据输入量提高10倍。
- 查询特征:对数据新鲜度的倾向性,准确地概括了对所有可观察性数据的查询的一般性质。为了说明这一点,下表显示了按数据年龄划分的Slack查询的百分比分布,表明>97%的查询都是在不到24小时的数据上进行的。在事故发生期间,不仅需要新的数据来了解系统的当前状态,还需要快速查看补救措施是否具有预期效果。这意味着新数据通常需要比旧数据更容易访问和可用。此外,在重大事件中,与仪表板交互并针对MELT数据编写自定义查询的用户数量是正常数量的两倍,这种情况并不罕见。大多数查询都是针对仪表板和告警的,它们通常只查询所收集数据的一小部分,但也需要亚秒的延迟来支持实时决策。
- 生命周期管理:除了对数据新鲜度的偏好外,在寻找长期趋势或出于法律/商业目的时,仍需要较小比例的历史数据进行查询。例如,在上述停机期间,Slack的工程师查看了过去几周的数据,以检查是否有任何周期性模式。因此,MELT数据通常需要对新数据和历史数据进行数据生命周期管理,这表明应在ODMS中设计基于数据年龄的分层存储策略。
3 当前挑战
目前在行业中部署的ODMS解决方案是定制的方法,只能部分满足MELT数据管理的需求。这些系统是被动实施的结果,没有指导性设计原则,因此面临着类似的实际挑战。在本节中,我们将介绍Slack当前的可观察性系统基础设施,作为一个案例研究,揭示了在构建可扩展ODMS时需要考虑的关键实际挑战。
3.1 Slack的可观测性
上图描述了Slack当前的可观察性系统基础设施。与许多行业解决方案一样,它由多个工具、摄入管道和存储引擎组成,用于收集、存储和服务Slack应用程序生成的MELT数据。
Slack使用Prometheus来存储和服务metrics。它从Slack应用程序公开的HTTP端点中抓取指标数据。为了实现高可用性,Slack使用了一对Prometheus服务器,每个服务器都维护一个独立的数据副本。每个应用程序都被分配了一个由2到64台Prometheus服务器组成的池。Slack总共运行了大约100个这样的池。
事件、日志和跟踪(ELT)被推送到Apache Kafka。为了持久化,Secor消费原始数据并将其写入AmazonS3。一个自定义的Apache Spark作业将这些数据复制并转换为一个Parquet文件,该文件也存储在AmazonS3中。这些Parquet文件可以使用Presto通过SQL查询。此外,Slack使用Logstash将日志写入到Elasticsearch中,并使用内部工具将trace数据写入到供应商的跟踪解决方案和Elasticearch支持的内部跟踪存储中。日志保留7天,跟踪保留14天。日志和跟踪也被写入数据仓库(Presto),用于历史查询。
3.2 实际挑战
Slack当前的ODMS架构面临着许多实际挑战,这些挑战是不断增长的可观察性工作负载所特有的。尽管我们使用Slack作为案例研究,但我们相信,目前在行业中部署的几乎所有ODMS都面临着一个或多个这样的实际挑战。我们将这些挑战分为三大类:
- 运维复杂度高:Slack提供MELT数据的基础设施包含20多个独立的软件组件。每个组件都有一个独特的体系结构,因此需要针对集群管理、安全性和容量规划进行自定义操作。这种异构化导致了复杂的解决方案。例如,Prometheus是一个单节点系统,它维护数据的独立副本以满足高可用性要求。同时,尽管Elasticsearch内置了副本管理,但以Slack的规模来看,管理和操作起来很有挑战性。在5月12日的事件中,数据量飙升至正常峰值的4倍。在这种情况下,监测团队执行复杂的扩容操作,以继续获取新的遥测数据。
- 保持低查询延迟:对可观测性数据的查询是高度倾斜的。超过97%的查询访问了24小时以内的数据,以实现近乎实时的告警。仪表板和重大事件或产品更改会导致查询数量大幅增加,给ODMS带来巨大压力。在5月12日的事件中,Slack查看了之前8小时的数据,同时处理了历史峰值负荷的2倍。Slack的ODMS的高运维复杂性使得响应整体查询工作负载的扩展和保持低查询延迟成为一个重大挑战。
- 基础设施成本高:在PB级存储规模下,为满足不断增长的数据保留要求和确保突发性工作负载中的数据可用性,成本就会变得高昂。Slack在平衡基础设施成本、性能、持久性和数据可用性方面花费了大量时间。例如,Slack在Presto中重复处理ELT,以提高可用性和持久性,同时冒着性能降低和基础设施成本增加的风险。Slack还根据历史峰值进行规定,以处理5月12日事件期间的高峰工作量。新的峰值负荷现在是事故发生前峰值负荷的2倍。此资源调配策略增加了基础架构成本。
4 ODMS设计原则
我们现在提出了一套设计原则,以解决前面章节中描述的现实世界的需求和挑战。考虑到MELT数据的异构性,以及在以可扩展的方式满足查询性能要求的同时降低管理这些数据的复杂性的需要,我们认为ODMS应该遵循四个核心设计原则:
- 解耦实时和历史数据管理:第二节表明,ODMS的工作负载与OLAP和OLTP的混合数据库工作负载(例如HTAP)有很大不同。它摄入了数PB的潜在突发的不可变写入,并且查询偏向于24小时以内的数据。考虑到这种工作负载而设计的ODMS将实时数据管理和历史数据管理解耦。这种解耦保持了快速的接收率、对最近数据的低查询延迟,并将存储和访问历史数据的成本降至最低。
- 统一MELT数据生命周期管理:时间维度将所有MELT数据管理与一个通用框架联系在一起。独特的实时和历史数据管理策略应在基于数据年龄的统一数据生命周期中进行管理。这样做可以抽象数据从实时存储到历史存储的移动,并降低在任意时间段透明访问所有数据的成本和复杂性。
- 为MELT数据查询提供单一接口:可观测性查询通常需要来自不同MELT类型的数据。单个查询接口抽象了MELT数据的单个存储和处理需求,降低了编写这些查询的复杂性,并为跨存储策略的优化提供了机会。这一原理适用于类Polystore的可插拔存储引擎体系结构。
- 提供云原生分布式部署:由于读写的高度突发性,系统的各层在设计上应该是分布式的和弹性的,这样系统就可以随着工作负载的变化而扩展。云原生ODMS允许可观察性团队根据工作负载和数据生命周期在服务MELT数据的成本和性能之间进行灵活的权衡。
这四个原则必须集成到ODMS架构的设计过程中。与当前的行业实践不同,ODMS是作为响应特定需求的功能拼凑而成的,遵循这些原则会产生一个连贯的体系结构,从而控制ODMS的复杂性、性能和成本。
上图显示了实现这些设计原则的架构的概述。它将MELT数据的数据生命周期统一到一个单一的ODMS基础设施中。与Slack的当前系统受到现代Lambda架构的影响不同,此设计受到现代Polystore架构设计的影响。这降低了维护多个数据管道的存储需求和复杂性,并解决了透明地协调多个存储引擎的需要。
- 数据摄入和存储:来自被测量的应用程序的所有MELT输入数据都被摄入到日志副本服务中(比如Apache Kafka)。实时索引层由特定于每种数据类型的存储引擎组成,它从日志服务中提取数据并对其进行索引以实现快速访问。索引数据文件会定期迁移到类似于AmazonS3的持久存储层。此迁移由群集管理器协调。在查询时,查询引擎使用元数据存储来协调实时索引和持久存储层。必要时,查询引擎会将热点历史数据从永久存储层拉入热数据缓存,以保持查询性能。
- 查询处理:查询引擎为所有查询提供服务。通过使用元数据存储,它协调了三个数据层(实时索引、持久存储和热数据缓存)以根据时间范围过滤数据,并优化跨数据类型的查询(类似于按时间连接)。它根据预期的查询分布和成本来确定最佳的数据存放点。例如,访问持久存储中数据的特定查询可能不需要缓存。但是,当在解决问题时重复访问历史数据时,查询引擎可能会决定将数据复制到热数据缓存,以最大限度地减少查询延迟。
- 分布式和可用性:ODMS的数据路由策略应在新数据的突发时期和查询负载特别大的时期保持高可用性。实时索引层和热数据缓存层是原生弹性的,其中独立的副本是按需启动的。查询引擎的集群管理器监视实时索引层的工作负载,以确定是否增加任何组件存储。在特别繁重的数据写入(例如,来自插入指令的应用程序)或对新数据的查询期间,它会扩容实时索引层。在对历史数据进行特别繁重的读取时,它会扩容热数据缓存。双层设计还有助于支持数据的不同可用性(例如,为更新/更有价值的数据提供更高的可用性保证)。
该架构可推广到可观察性遥测统一在集中式ODMS的环境中,这使得用户可以轻松访问MELT数据。遵循这些原则的ODMS体系结构旨在帮助应对高峰负载,例如Slack 5月12日事件期间的高峰负载。查询引擎提供了一个单一的查询界面和MELT数据的统一视图。它提供了对跨实时索引和历史持久存储层查询和管理异构类型及其生命周期的复杂性的抽象。弹性热数据缓存根据数据访问模式对峰值负载做出响应,以保持低查询延迟。最后,集群管理器管理该系统中的数据生命周期和操作复杂性。它还可以根据工作负载进行伸缩,从而降低系统的整体基础设施成本。这些原则缩短了峰值负载期间的数据洞察时间,并降低了总体复杂性和成本。
5 结论
可观测性数据管理是一个新兴的研究领域,需要数据库界给予更多关注。在本文中,我们讨论了Slack(一个基于云的团队协作服务)的可观察性数据的真实世界体验及其用例。时序数据的异构性以及不断变化的工作负载特性需要一种新的可观察性数据管理体系结构。作为回应,我们提出了一种新的云原生类Polystore架构,该架构将实时和历史数据访问层与底层持久存储和查询层解耦,从而能够独立扩展它们。我们目前正在构建这个的初步原型设计以使用Slack的生产数据进行测试。