快速可靠的架构 - 不可止结的日志分析平台

0.
快速可靠的架构 - 不可止结的日志分析平台

在优步,我们提供集中,可靠和互动的日志记录平台,使工程师能够在规模上快速和自信地工作。使用丰富的上下文键值对标记了日志,其中工程师可以将其数据切片和骰子以引导产品改进的表面异常或有趣的模式。目前,该平台从地区的数千个服务中摄取了数百万的每秒日志,存储了几个PBS值,并从仪表板和程序提供每秒数百个查询。

自从开始开始麋鹿对于2014年登录,我们的系统流量和用例方差都有显着增长。As we reached a scale bottleneck to support this rapidly growing traffic, we decided to take our insights about Uber’s many logging use cases and build our next-gen platform to fundamentally improve the reliability, scalability, performance, and most importantly to ensure a pleasant experience for both its users and operators.

背景

在过去几年中,有机测井流量增长导致了大规模的平台部署规模,用户需求也显着演变。对基于麋鹿的平台进行了大量挑战,这在较低的规模上不可见:

  1. log架构:我们的日志是半结构化的。ES(Elasticsearch)Infers Schemas自动,将其保持在群集中,并在以下日志上执行它。不兼容的字段类型原因eS中的型冲突错误,删除了违规日志。虽然强制执行一致的模式对于具有明确定义的结构的业务事件来说是合理的,但是对于记录这导致开发人员生产力的显着降低,因为Log Schema随着时间的流量而有机地发展。例如,大量的API平台可以通过数百名工程师更新,累积eS索引映射中的数千个字段,并且不同工程师使用相同的字段名称,但在现场值中具有不同类型的几率,这导致在es中键入冲突。强制工程师要了解现有的架构,并保持其一致性只是为了打印一些服务日志效率低下。理想情况下,该平台应将架构更改为规范,并能够为用户处理具有多种类型的字段。
  2. 运营成本:如果群集受重质量疑问或群体受到负面影响,我们必须在每个区域中运行20多个es群以限制爆炸半径,或者映射爆炸.Logstash管道的数量甚至更高,每个区域超过50个,以适应特殊用例和自定义配置。昂贵的查询和映射爆炸都可能严重降低ES集群的性能,有时甚至会“冻结”它,因为我们必须重新启动集群才能将其恢复。随着集群数量的增加,这种中断变得越来越频繁。尽管我们尽了最大的努力来自动化流程,比如检测和禁用会导致映射爆炸和类型冲突的字段,在ES集群之间重新平衡流量,等等,但人工干预解决类型冲突等问题仍然不可避免。我们希望有一个平台能够支持我们的组织的大规模和新的用例,而不会引起太多的操作开销。
  3. 硬件成本:索引字段在es中昂贵昂贵,因为它需要构建和维护复杂的反转索引和前进索引结构,将其写入事务日志,定期将内存缓冲区刷新到磁盘,并执行常规背景合并以保持数量刷新索引段来自不排列的,所有这些都需要显着的处理能力并增加日志的延迟变为查询。因此,我们的ES集群将配置为最多三个级别的索引,而不是索引日志中的所有字段。但摄取所有生成的日志仍然消耗了大量的硬件资源,并且缩放了太昂贵。
  4. 聚合:如我们生产环境所发现的,超过80%的查询是聚合查询,例如术语,直方图和百分位数。虽然ES已经改进了优化前向索引结构,但仍不列为旨在支持跨大型数据集的快速聚合。性能低效率导致了令人不快的用户体验。例如,在查询最后1H日志(约为1.3TB)时,具有大量日志音量的服务的临界仪表板(左右1.3TB)并在查询最后6H日志时经常超时,无法诊断生产问题。

通过我们的ELK基础的平台,我们只设法摄取了在优步生成的日志中。基于ELK的平台的已经大量的部署和许多固有的效率低下,使其规模昂贵昂贵,以便摄取所有日志并提供我们生产环境的完整高分辨率概述。

介绍一个新的架构 - 不可知日志分析平台

我们的目标是收集所有在Uber产生的日志,并以较低的平台成本存储和服务,确保用户和运营商的愉快体验。总的来说,我们设计了一个新的日志分析平台,考虑到以下关键需求:

  1. 功能:
    1. 模式无关开发人员的生产力
    2. 高效支持聚合查询
    3. 支持多区域和交叉租户查询
  2. 效率和维护:
    1. 支持多租户正确巩固部署
    2. 降低成本并能够处理10倍
    3. 提高可靠性和简化操作
  3. 从麋鹿平台透明迁移,例如,用户可以继续使用Kibana以交互方式分析日志。

我们评估了各种伐木产品和存储解决方案。最终,我们决定使用点击作为底层的日志存储技术,并在其之上构建了一个抽象层来支持模式无关的数据模型。

架构无关数据模型

我们的原始日志格式化为JSON,其模式可以逐渐改变。在发出像“Job完成”这样的日志消息时,开发人员可以用键值对标记为上下文。日志消息和标签在输出日志中被编码为字段。标签值可以是原始类型,如数字或字符串和复合类型,如数组或对象。在优步中,日志平均有40个以上的字段,所有这些字段都由我们的平台同等对待并提供丰富的上下文。

为了支持架构演变,我们跟踪在Log Schema中摄取期间在字段中看到的所有类型,如下所示。在查询执行期间持续和使用模式,稍后解释。使用时间戳标记每个字段类型,该时间戳表示观察到类型何时,可用于从架构中清除陈旧信息。

单击表模式

一开始,我们尝试了两个表格模式,以保留点击的点心。第一个架构在_source列下只能以JSON格式保存原始日志,并且在查询时,通过Clickhouse的JSON Unmarshal函数访问日志字段,访问paramextractstring..但由于JSON Unmarshalling的开销,此架构,查询太慢了。

第二个架构不仅在_source中保留了_source中的原始日志,以便快速原始日志检索,而且还将所有字段展平到专用列,其中包含字段名称和类型来处理类型冲突,以便可以直接从列查询字段值。我们发现查询可能比第一个架构快50倍。但是这种模型也没有规模,因为磁盘文件的数量随着表列的数量而且,点击乐停止响应写入并读取,由此负担过量合并部分在背景中。

最后,我们到达下表模式(简化了简化了),以提供良好的查询性能,同时阻止磁盘文件的数量不受限制。基本上,每个日志都被展平到一组键值对;这些对由它们的值类型分组,如字符串,数字或stryArray。在桌子中,我们使用一对数组要存储那些密钥值对组:(String.names,String.Values)用于存储带有字符串值的键值对组,(number_array.names,number_array.values)与键值对数字数组值等。

公共元数据字段保存在专用列中,以便快速检索。特别是,_namespace列允许我们有效地支持多租户。请注意,我们总是在_source列中保留原始日志,以避免动态地重构完整的日志,这对于嵌套结构来说可能是复杂且代价高昂的。虽然我们实际上存储了两次日志,但由于有效的压缩,它不会增加太多的磁盘使用量。在生产流量中进行实验时,使用这个表模式的压缩比可以达到3倍(在某些情况下甚至是30倍),与使用ES所能达到的压缩比相当,而且往往比ES更好。

在查询执行期间,我们使用Clickhouse数组功能访问字段值,例如String.Values [indexof(String.names,'端点')] ='/ health'。我们可以从那些阵列列中的任何字段大约5倍,而不是通过Unmarsh安装原始日志来检索值。与上面的第二个架构相比,从数组列中提取来自数组列的字段值比从专用列访问它们。考虑到大多数过滤器都在字段上进行评估,我们建议在专用列中写入字段值,如果它们通常是加速查询,即自适应索引字段,利用该字段物化柱点击屋的功能。

除了用于查询执行的性能和灵活性外,该表模式也允许高效的日志摄取。如我们实验所示,单击小时节点可以每秒摄取300k日志,大约是单个ES节点可以实现的大约十倍。


摄取所有和查询任何,快速

在本节中,我们讨论如何将所有日志摄入到上面创建的Clickhouse表中,无论Log Schema如何发展;通过一组自定义,高级接口查询它们,允许推断出现场类型;并自适应地使用物化列以基于访问模式提升查询性能。

模式免受摄取

日志对于oncall工程师来说是关键的调试中断。为了减少MTTR,我们努力在我们的日志分析平台中尽可能快地摄取日志。

如上图所示,从Kafka重新摄取日志以点击。我们的平台摄入管道有两个主要部分:Ingester和Batcher。ingester消耗了从Kafka的日志,并将JSON格式的日志达成键入键值对。这些键值对由它们的值类型分组并通过下游发送m3msg..Ingester不会提交Kafka偏移,直到它从Batcher接收到Acks,这意味着日志已成功写入点击屋,以至少一度交付保证提供。M3MSG运输还​​可以轻松地实现沿着摄取管道的背压,当我们的下游减慢时利用Kafka到缓冲日志。

在大批写入时,Clickhouse最适用,因此我们将多个租户包装到表格中,以确保足够快的批量来减少摄取延迟而不会增加写入率。在摄入期间,从当前日志批处理中提取日志架构,并持久地持久地在Batcher存储的元数据中用于查询服务以生成SQL。与ES不同,其中索引更新是数据摄取路径上的阻​​塞步骤,即使使用错误更新模式,我们也继续摄取数据以点击。我们假设Log Schema可以始终发展,但大多数标签都是重复的,所以它非常可能会批量可以更新架构,并在最终与点击屋中的日志同步。更重要的是,元数据存储可以保持非常大的日志模式,使我们的平台更加免受免疫映射爆炸问题。

类型感知查询

令人令人令人生畏,向用户直接编写单击小时SQL以检索自定义表模式下的日志。它要求用户知道如何使用数组列来表示键值对,如何在表之间移动日志以改善数据放置,以及自适应索引是基于查询历史等的自适应索引方式。提供熟悉的和令人愉快的用户体验,我们为我们的日志记录用例提供了一组高级查询接口,并建立了一个查询服务以自动生成SQL并与点击群集进行交互。

查询界面

下面演示查询界面,简化了简化了简化。rawquery.通过过滤条件检索原始日志;聚合Query.通过某些字段的原始值对它们进行分组后计算日志的统计信息;和BucketQuery通过针对日志评估的表达式的结果,使对日志进行分组,例如每隔10分钟核对日志。请注意,计算条款可以有自己的过滤器子句来利用条件聚合点击屋中的功能,可以方便地表达复杂分析。此外,所有这些查询都可以使用命名空间列表(即租户)访问日志,使用合并()函数在幕后。

SQL生成

查询服务在两个主要阶段的请求中生成单击小时SQL:逻辑和物理。在逻辑阶段期间,查询请求遍历以收集字段类型约束,并针对在摄入时间内收集的日志模式检查现场存在。步行通过查询请求后,收集一组字段名称及其类型约束。例如,字段“foo”应该访问字符串或stryArray键盘值,因为它存在于筛选表达式中,例如“foo”=“abc”;或字段“栏”应该访问字符串,数字,stryArray或numberArray键入的值,因为它是在组的等中使用的。

逻辑阶段的下一步骤是通过比较从查询请求中收集的类型约束和保持在日志模式中的字段类型来解析字段类型。例如,如上所述的字段“foo”,可以在架构中具有字符串类型,因此我们只能访问包含字符串值的列,同时生成Clickhouse SQL;上面示例中的字段“栏”可能具有架构中的字符串和数字类型,因此我们应该在单个SQL中访问两个类型的值。

在访问字段中访问多个类型值时,可能需要类型转换,作为SQL中的表达式,从该字段中的特定类型的值期望。目前,根据表达式中使用的操作符,我们按照下面显示的规则将非字符串类型转换为字符串或StringArray。当前者是稍后的基类型时,标量和阵列类型也是可转换的。


通过这种方式,影响基于ELK的平台的可用性的冲突问题被简单地被视为新的日志记录平台中的常态.在逻辑阶段结束时,确定字段类型,并相应地描绘表列访问表达式。例如,对于字段“栏”,我们可能会结束以下SQL表达式:

在逻辑阶段中解析的列表达式告诉我们如何访问每个字段的值。在物理阶段,查询请求中指定的各种表达式将转换为最终SQL。在物理阶段的末尾,确定查询设置,该查询设置控制点击房间如何执行查询,例如它应该为任务分配多少个线程。以下是典型翻译的示例:


ES在写时决定字段类型,而我们的平台延迟字段类型的解析直到查询,简化了摄取逻辑,大大提高了数据的完整性。一般来说,写路径比查询路径的错误预算要少得多,因为它不能停机太长时间,否则日志在Kafka中堆积并被自动删除。有了更多的错误预算,我们可以更快地迭代查询服务,甚至在检索日志时对日志应用复杂的转换,而不是在摄入管道中进行复杂的预处理,比如Logstash。

自适应索引

在分析我们的生产查询后,我们观察到只使用5%的索引字段。这意味着在ES中浪费了另外95%的领域的索引成本。因此,我们已经设计了摄取所有日志的平台,并没有向索引字段支付前期成本。但是,我们还通过将它们实现到查询加速的专用列来选择性地索引最常见的查询字段,例如以下:


单击“将”回填“字段值异步到后台的实际列,而不会阻止正在进行的读取和写入。其一个凉爽的功能是,当查询物化柱时,它可以使用在实体化的柱中使用预先填充的值,并且在实体化列尚未回填的情况下,透明地倒回基于阵列的值提取。这简化了我们的逻辑,以撰写使用物化列的SQL查询。基本上,在解析列访问表达式的同时检查字段是否进行了资格化,每当尽可能使用快速访问路径。如下所示:

实质化字段在写入路径上增加了额外的成本,因此平台周期性地清理这些列不经常访问。

可靠性,可扩展性,多区域和多租户

在本节中,我们触摸了架构设计,使我们的日志结构能够可靠地扩展,如何在地区工作,以及如何为多租户应用资源治理。

我们使用ReplicatedMergeTable点击屋中的引擎并设置RF = 2以提高系统可靠性,提高冗余。复制是异步和多主站的,因此可以将日志写入副本集中的任何可用副本,并且查询也可以访问来自任何副本的日志。因此,暂时丢失一个节点,例如重新启动或升级,不会影响系统可用性和数据耐用性。但由于异步复制,当节点永久丢失时,可以丢失某些量的日志。我们可以配置复制同步对于更高的一致性,但现在我们发现这是一个可接受的权衡,以便更好地获得。

为了缩放系统,我们转向表分片支撑在ClickHouse。一个表可以有多个分片。目前,我们只是对整个ClickHouse集群上的每个表进行切分。ClickHouse还允许我们将查询配置为跳过不可用的碎片并以最佳可用性返回结果,这在快速响应比准确度更为需要时特别有用。

单击小时仅为框中提供了对群集管理的基本支持,因此我们在平台的管理服务中增强了此功能。总的来说它就像一个国家驱动的群集管理框架。群集的目标状态描述了群集应该是什么,并保存在元数据存储中。管理工作流程在框架上实现并由管理员服务在目标状态更改时调用,或者按计划从实际状态转换为目标状态。工作流是幂等的,可以安全地重新检查以以容错方式管理群集。常见的操作租户,拓展群集,替换节点,换算字段,优化租户放置和清除旧日志 - 都是可靠的本框架自动化。

分布式表功能用于方便查询所有碎片的日志。分布式表不存储任何物理数据,但需要群集信息有关所有碎片的扇出查询并正确聚合部分结果。在开始,我们在所有单击节点上创建了所有分布式表,以便任何人都可以提供分布式查询。但随着我们将群集扩展到跨区域的数百个节点,我们发现很难以及时和一致的方式将集群信息从全局元数据存储传播到所有分布式表。因此,我们将节点分开到查询和数据角色,然后只有查询节点需要群集拓扑信息来提供分布式查询。因为查询节点的数量要低得多,因此很容易将群集信息传播给它们并快速收敛。此外,角色的分离允许我们使用不同的硬件SKU来查询和数据节点并独立缩放它们。例如,查询节点非常无状态,非常容易缩放。

通过这种架构,我们可以通过添加更多的CH节点来线性扩展集群。但是为了可靠地支持多租户,我们还需要在租户之间进行适当的隔离,以便进行摄取和查询。在食用者中,我们对每个房客施加费率限制。在查询服务中,我们控制每个租户的最大在线查询数量,并保守地将资源分配给每个查询,如查询线程,为并发查询和摄入负载留出空间。

成本和性能

与麋鹿堆栈相比,我们将平台的硬件成本降低了一半以上,同时提供了更多的生产流量。对于操作开销,我们只需要在每个区域运行一个单一的统一日志摄取管道,并通过管理服务自动化所有常用操作。此外,该平台免受型冲突错误,它曾经是操作旧平台时的呼叫工作负载的主要来源。

在性能方面,摄取延迟在一分钟内覆盖。我们的平台查询,检索和聚合跨多个区域的日志,通常在几秒钟内,特别是查询窗口不到一天。追随查询(如刷新仪表板)运行得更快,因为单击内存中的Clickhouse缓存数据。随着Clickhouse中的资源隔离的正确支持,我们的平台继续在重质查询负载下工作而不严重劣化或被堵塞。

麋鹿透明迁移

除了直接调用ES端点以检索日志之外,Uber工程师还保存了大约10k的kibana仪表板来分析日志。因此,为了便于迁移而不需要用户侧更改其仪表板或代码,我们建立了QueryBridge服务以将ES查询转换为新的查询界面,反之亦然。在验证查询结果后,我们将功能标志添加到服务中以逐步迁移用户。

我们的目标不支持全核查询语法,但只有那些发现在我们的生产中使用的eS。即便如此,翻译逻辑仍然非常复杂。例如:

  1. Lucene查询可以嵌入ES查询通过请求参数操作员。我们将它们转换为代表整个es查询的整个AST(抽象语法树)的子树。
  2. 聚合可以具有与其相关联的过滤条件,例如过滤聚合,并将其转换为使用查询请求中的特定筛选器进行计算。
  3. eS内部字段,如@Timestamp和_source必须单独处理,因为它们不是日志主体内的数据字段。
  4. 我们必须转换过滤器关键词和文本田地不同。例如,foo:“abc”表达式转换为等于(foo,“abc”)检查它是否是关键字类型,但包含(foo,“abc”)检查它是否在查询请求中的文本类型。

未来的工作

日志将高分辨率的洞察力传达到生产环境,特别是当它们被标记为高基数字段时,如请求ID,Geo位置或IP地址。能够分析近实时的日志证明对调试在线系统非常有效,并识别有趣的模式以提高产品质量。基于到目前为止的用户体验,我们认为这是一个有效的平台,可以实现优步的广泛日志分析需求。然后点击确实是一个非常强大的分析引擎,我们想继续探索。

前进,我们打算在这些有趣的地区进行跟进:

  • 支持查询服务中的SQL查询。
  • 通过使用积极改善查询延迟的方差预防和在哪里对从句进行适应性、微调索引粒度,探索这一点跳绳,并根据更多收集的统计数据微调查询设置。
  • 探索分层存储以增加数据保留并降低成本。
  • 构建新一代UI/UX来取代Kibana,更好地与后端集成,以发现和分析日志,并通过交叉检查日志、度量和跟踪方便事件调查。

如果您有兴趣加入优步拥有可靠性平台团队,并建立下一个Gen可观察性体验,申请加入我们的团队!!

注释

没有帖子展示