上周五,我们宣布了所有 Cloudflare DNS 分析工具. 由于我们的规模很大(当你读完这篇文章的时候,Cloudflare DNS 将处理数以百万计的 DNS 查询) 我们必须非常有创意的解决该问题. 在本文中,我们将介绍 DNS 分析工具的组件,这些组件帮助我们每月处理数以万亿计的日志.
Cloudflare 已经有一个用于 HTTP 日志的数据管道.我们希望 DNS 分析工具可以利用该功能.每当边缘服务处理一个 HTTP 请求时,它就会以 Cap'n Proto 格式生成一个结构化的日志消息,并将其发送到本地多路复用服务.考虑到数据量,我们只记录部分 DNS 消息数据,只包含我们感兴趣的数据,例如响应码,大小或 query name,这使得我们每个消息平均只保留约 150 字节数据.然后将其与元数据处理(例如在查询处理期间触发的定时信息和异常)融合.在边缘融合数据和元数据的好处是,我们可以将计算成本分散到成千上万的边缘服务器,并且只记录我们需要的信息.
多路复用服务(称为 "日志转发器")正在每个边缘节点上运行,从多个服务组装日志消息并将其传输到我们的仓库,以便通过 TLS 安全通道进行处理.运行在仓库中的对应服务将日志接收并解分解到几个 Apache Kafka 集群中.Apache Kafka 用于生产者和下游消费者之间做缓冲,防止消费者故障或需要维护时的数据丢失.自 0.10 版本以来,Kafka 允许通过机架感知分配副本,从而提高对机架或站点故障的恢复能力,为我们提供容错的未处理消息存储.
拥有结构化日志队列使我们能够追溯性地查问题,而不需要访问生产节点. 在项目的早期阶段,我们会跳过队列并找到我们所需的粗略时间段的偏移量,然后将数据以 Parquet 格式提取到 HDFS 中,以供离线分析.
关于聚合
HTTP 分析服务是围绕生成聚合的流处理器构建的,因此我们计划利用 Apache Spark 将日志自动传输到 HDFS.由于 Parquet 本身不支持索引以避免全表扫描,因此在线分析或通过 API 提供报告是不切实际的.虽然有像 parquet-index 这样的扩展可以在数据上创建索引,但也不能实时运行.鉴于此,最初的设计是仅向客户显示汇总报告,并保留原始数据用以内部故障排除.
汇总摘要的问题在于,它们只能处理基数较低(大量唯一值)的列.通过聚合,给定时间范围内的每个列都会扩展到很大的行数(与唯一条目个数相等),因此可以将响应码(例如只有 12 个可能的值,但不包含查询名称)进行聚合.如果域名受欢迎,假设每分钟被查询 1000 次,那么可以预期每分钟做聚合可以减少 1000 倍的数据,然而实际上并不是这样.
由于 DNS 缓存的存在,解析器在 TTL 期间不会进行 DNS 查询. TTL 往往超过一分钟.因此,服务器多次看到相同的请求,而我们的数据则偏向于不可缓存的查询,如拼写错误或随机前缀子域名攻击.在实践中,当用域名进行聚合时,我们可以看到最多可以减少为原来的 1/60 的行数,而多个分辨率存储聚合几乎可以抵消行减少.使用多个分辨率和键组合也可以完成聚合,因此聚合在高基数列上甚至可以产生比原始数据更多的行.
由于这些原因,我们首先在 zone 层次上汇总日志,这对于趋势分析来说已经足够,但是对于具体原因分析来说则太过粗糙.例如,我们正在调查其中一个数据中心的流量短暂爆发.具有未聚合的数据使我们能够将问题缩小到特定 DNS 查询,然后将查询与错误配置的防火墙规则相关联.像这样的情况下,只有汇总日志就有问题,因为它只聚合一小部分请求.
所以我们开始研究几个 OLAP 系统.我们研究的第一个系统是 Druid.我们对前端(Pivot 和以前的 Caravel)是如何切分数据的能力印象很深刻,他使我们能够生成具有任意维度的报告. Druid 已经被部署在类似的环境中,每天超过 1000 亿事件,所以我们对它可以工作很有信心,但是在对抽样数据进行测试之后,我们无法证明数百个节点的硬件成本.几乎在同一时间,Yandex 开源了他们的 OLAP 系统 ClickHouse.
ClickHouse
ClickHouse 的系统设计更加简单,集群中的所有节点具有相同的功能,使用 ZooKeeper 进行协调.我们建立了一个由几个节点组成的集群,发现性能相当可观,所以我们继续构建了一个概念验证.我们遇到的第一个障碍是缺少工具和社区规模的规模太小,所以我们钻研了 ClickHouse 设计,以了解它是如何工作的.
ClickHouse 不直接支持 Kafka,因为它只是一个数据库,所以我们使用 Go 写了一个适配器服务.它读取来自 Kafka 的使用 Cap'n Proto 编码的消息,将它们转换为 TSV,并通过 HTTP 接口分批插入 ClickHouse.后来,我们讲 ClickHouse 的 HTTP 接口替换为 GO SQL 驱动,以提高性能.从那以后,我们就开始为该项目提供了性能改进.我们在性能评估过程中学到的一件事是,ClickHouse 写入性能很大程度上取决于批量的大小,即一次插入的行数.为了理解为什么,我们需要进一步了解了 ClickHouse 如何存储数据.
ClickHouse 用于存储的最常见的表引擎是 MergeTree 系列.它在概念上类似于 Google 的 BigTable 或 Apache Cassandra 中使用的 LSM 算法,但它避免了中间内存表,并直接写入磁盘.这使得写入吞吐量非常出色,因为每个插入的批次只能通过 "主键" 进行排序,压缩并写入磁盘以形成一个段.没有内存表也意味着他仅仅追加数据,并且不支持数据修改或删除.当前删除数据的唯一方法是按日历月份删除数据,因为段不会与月份边界重叠. ClickHouse 团队正在积极致力于使这个功能可配置.另一方面,这使得写入和段合并无冲突,因此吞吐量与并行插入的数量成线性比例关系,直到 I/O 跑满.但是,这也意味着它不适合小批量生产,这就是为什么我们依靠 Kafka 和插入器服务进行缓冲的原因. ClickHouse 在后台不断合并,所以很多段将被合并和写多次(从而增加写放大),太多未合并的段将触发写入限流,直到合并完成.我们发现,每秒钟每张表的插入一次效果最好.
表读性能的关键是索引和数据在磁盘上的排列.无论处理速度有多快,当引擎需要从磁盘扫描太多数据时,这都需要大量时间.ClickHouse 是一个列式存储,因此每个段都包含每个列的文件,每行都有排序值.通过这种方式,可以跳过查询中不存在的列,然后可以通过向量化执行并行处理多个单元.为了避免完整的扫描,每个段也有一个稀疏的索引文件.鉴于所有列都按 "主键" 排序,索引文件仅包含每第 N 行的标记(捕获行),以便即使对于非常大的表也可以将其保留在内存中.例如,默认设置是每隔 8192 行做一个标记.这种方式只需要 122,070 个标记来具有 1 万亿行的表格进行索引.在这里可以查看 ClickHouse 中的主键,深入了解它的工作原理.
使用主键列查询时,索引返回考虑行的大致范围.理想情况下,范围应该是宽而连续的.例如,当典型用法是为单个区域生成报告时,将区域放在主键的第一个位置将导致按每个区域进行排序,使得磁盘读取单个区域连续,而如果按主要时间戳排序则在生成报告是无法保证连续.行只能以一种方式排序,因此必须仔细选择主键,并考虑典型的查询负载.在我们的例子中,我们优化了单个区域的读取查询,并为探索性查询提供了一个带有采样数据的独立表格.从中吸取的教训是,我们不是试图为了各种目的而优化索引,而是分解差异并多加一些表.
这样专有化设计的结果就是在区域上聚合的表格.由于没有办法过滤数据,因此扫描所有行的查询都要昂贵得多.这使得分析师在长时间计算基本聚合时不那么实际,所以我们决定使用物化视图来增量计算预定义聚合,例如计数器,唯一键和分位数.物化视图利用批量插入的排序阶段来执行生产性工作 - 计算聚合.因此,在新插入的段被排序之后,它也会生成一个表格,其中的行代表维度,而列代表聚合函数状态.聚合状态和最终结果之间的区别在于,我们可以使用任意时间分辨率生成报告,而无需实际存储多个分辨率的预计算数据.在某些情况下,状态和结果可能是相同的 - 例如基本计数器,每小时计数可以通过累计每分钟计数来产生,但是对独特的访问者或延迟分位数求和是没有意义的.这是聚合状态更有用的时候,因为它允许有意义地合并更复杂的状态,如 HyperLogLog(HLL)位图,以便每小时聚合生成每小时独立访问者估计值.缺点是存储状态可能比存储最终值要昂贵的多 - 上述 HLL 状态在压缩时大概有 20-100 字节 / 行,而计数器只有 8 字节(平均压缩 1 个字节).使用这些表可以快速地将整个区域或站点的总体趋势形象化, 并且我们的 API 服务也使用它们做简单查询.在同一位置同时使用增量聚合和没有聚合的数据, 我们可以通过流处理完全简化体系结构.
基础设施和数据整合
我们使用 12 个 6TB 磁盘做 RAID-10,但在一次磁盘故障之后重新进行了评估.在第二次迭代中,我们迁移到了 RAID-0,原因有两个.首先,不能热插拔有故障的磁盘,其次阵列重建花费了数十个小时,这降低了 I/O 性能.更换故障节点并使用内部复制通过网络(2x10GbE)填充数据比等待阵列完成重建要快得多.为了弥补节点故障的可能性较高,我们切换到 3 路复制,并将每个分片的副本分配到不同的机架,并开始规划复制到单独的数据仓库.
另一个磁盘故障突出了我们使用的文件系统的问题.最初我们使用 XFS,但它在复制过程中开始在同一时间从 2 个对等点进行锁定, 从而在完成之前中断了段复制.这个问题表现为大量 I/O 活动,由于损坏的部分被删除,磁盘使用量增加很少,所以我们逐渐迁移到了 ext4,该文件系统就没有这个问题.
数据可视化
当时我们只依靠 Pandas 和 ClickHouse 的 HTTP 接口进行临时分析,但是我们希望使分析和监控更容易. 因为我们知道 Caravel(现在更名为 Superset ),我们就把它和 ClickHouse 进行了整合.
Superset 是一个直观的数据可视化平台,它允许分析人员在不写一行 SQL 的情况下交互地切片和切分数据. 它最初是由 AirBnB 为 Druid 构建和开源的,但是随着时间的推移,它已经通过使用 SQLAlchemy(一种抽象和 ORM)为数十种不同的数据库方言提供了基于 SQL 的后端的支持. 所以我们编写并开源了一个 ClickHouse 方言,并集成到 Superset.
Superset 为我们提供了特别的可视化服务,但是对于我们的监控用例来说,它仍然不够完善. 在 Cloudflare,我们大量使用 Grafana 来可视化所有指标,所以我们将它与 Grafana 集成并进行开源.
它使我们能够用新的分析数据无缝地扩展我们现有的监测仪表板. 我们非常喜欢这个功能,因此我们希望能够为用户提供同样的能力来查看分析数据. 因此,我们构建了一个 Grafana 应用程序,以便可视化来自 Cloudflare DNS Analytics 的数据. 最后,我们在您的 Cloudflare 仪表板分析中提供了它. 随着时间的推移,我们将添加新的数据源,维度和其他有用的方法来显示 Cloudflare 中的数据.
来源: https://sdk.cn/news/8011