阿里巴巴十年技术精华大公开:详解超大规模PB级数据传输系统技术细节
本文为阿里云SLS徐可甲在 GIAC 2022 全球互联网架构大会 分享时的议题内容。
云原生场景下数据总线需求场景及挑战
数据总线简介
数据总线作为大数据架构下的流量中枢,在不同的大数据组件之间承载着数据桥梁的作用。通过数据总线,可以实时接入来自服务器、K8s、APP、Web、IoT/移动端等产生的各类异构数据,进行统一数据管理,进而实现与下游系统的解耦;之后可以异步实现数据清洗、数据分发、实时计算、离线计算等计算过程,进而将结构化后的数据投递到下游的分析、归档系统,进而达到构建清晰的数据流的目的。广义上,数据采集与接入、传输链路、存储队列、消费计算、投递等都属于数据总线的范畴,整体上可以分为采集接入层、管道层、计算层。
通过数据总线,可以轻松达到如下目的:
- 解耦生产者与消费者:消费方完全可以不用感知写入方的任何细节,降低系统对接复杂性,提升系统可靠性。
- 应对流量洪峰,使数据的生产异步于数据的消费,消峰填谷。
- 定义统一格式与操作语义:接入多种异构数据,通过数据处理构建统一的格式。
举一个简单的例子,在计算广告检索系统中,广告的点展数据至关重要。一份点展数据往往会被多方订阅消费,且应用场景各异,有精度到秒级的实时计算业务,也有类似于Hadoop的小时级或天级的批处理任务。如果数据直接对接,就需要考虑各种异常场景,会让系统变得极其复杂。而通过数据总线,可以大大降低系统复杂度,提高系统可靠性,这样可以保证任意一个数据订阅系统即使经历了下线维护或者宕机,也可以在重新上线后从之前的断点处继续进行数据处理。
云原生场景下的技术挑战
面对每天几百亿次读写、近百PB数据流量、万级用户的场景时,构建高可用的数据总线将会是一件非常有挑战的事情。这里简单列举一些场景的流量场景:
- 生产者:因业务促销等活动,流量在几分钟内上涨至原先十几倍或几百倍;
- 消费者:对一种数据同时有几十个订阅者来同时消费;
- 每天有几百个异构数据源接入,方式各不相同,需要大量适配。
经过几十年的飞速发展,整个开发模式、系统架构、部署模式、基础设施等也都经过了几次颠覆性的变革,这些变革带来了更快的开发和部署效率。但随之而来整个的系统与网络环境也更加的复杂、部署模式和运行环境也更加动态和不确定、接入的数据源与数据量大幅增加、流量波动等不确定因素变大、接入难度与原始结构差异化变大,这些都是云原生时代也给数据总线带来的新的要求。
总结下来,云原生时代数据总线的技术挑战可以从采集接入层、管道层、计算层三方面展开。采集接入层重点关注数据源的接入丰富度、接入易用性、资源开销与数据采集可靠性;管道层重点关注网络质量、带宽与水平扩展能力、安全与隔离性、生态丰富度等;计算层重点关注及计算语法能力、流量处理带宽与扩展性等。
开源方案的选择与对比
目前行业上主流的大数据架构,大概可以分为5个部分,其中前3部分构成了数据总线。
- 采集端:承载可观测数据采集及一部分前置的数据处理功能。随着云原生的发展,采集端也需要适应时代潮流,提供对K8s采集的友好支持。常见的采集端有Filebeat、FluentD/Fluent-bIt、Telegraf,以及我们开源的iLogtail。
- 消息队列:采集Agent往往不会直接将采集到的数据发送到存储系统,而是写入消息队列,起到削峰填谷的作用,避免流量洪峰导致存储系统宕机。常见消息队列为Kafka、RabbitMQ等。
-
计算:用于消费消息队列中的数据,经过处理、聚合后输出到存储系统。比较常见的为Flink、Logstash等。值得注意的是,也开始有公司将
iLogtail
开源版作为计算层部署在日志架构中。 - 存储分析引擎:提供采集数据持久化存储能力,并提供查询分析能力。常见的存储分析引擎为Elasticsearch、ClickHouse、Loki以及时序的Prometheus、influxdb等。
- 可视化:借助Kibana和Grafana提供采集数据的可视化能力。
用户可以利用上述的采集端、消息队列、计算三部分提供的开源组件搭建出一套数据总线。虽然基于开源组件搭建数据总线技术上时可行的,但是整体实施复杂度很高,需要维护多套系统进行协同。此外,也无法完全满足云原生上文提到的云原生场景下面临的技术挑战,例如网络质量与地域规划就是一道比较难以逾越的鸿沟。
数据总线整体架构
可观测平台不仅需要解决好数据如何获取、查询的问题,也应该提供具体业务场景的应用能力,帮助客户从碎片化、低信息的数据中挖掘更大的数据价值。一个典型的云原生可观测平台从下至上可以分为四个层面:数据总线、存储分析、工具、应用。其中,数据总线是整个可观测平台的数据基础,为数据分析及上层业务应用提供数据保障。正因为数据总线足够基础,所以在企业数字化过程中必须足够可靠、稳定、确保数据的通畅,并且能够弹性满足流量变化需求。接下来,我们将重点分享阿里云SLS数据总线的架构与实践。
在本文第一部分,我们介绍到一个典型的数据总线可以分为采集接入层、管道层、计算层三个层次,对应的SLS的数据总线的架构也可以类似划分。
- 采集接入层:承载了数据总线所有数据(支持Log、Metric、Trace、Event等)的接入,并且与阿里云服务有深度的集成。接入手段以SDK/API为基础,扩展了端上可观测采集器 iLogtail、数据导入服务、开源标准协议等多种接入方式。
- 管道层:以 LogHub 作为数据总线的核心流量中枢,功能可以完全替代 Kafka。通过 Restful API 对外提供接入服务,通过消费组的方式提供实时消费服务。
- 计算层:作为 LogHub 的下游服务,基于实时消费组,提供了各类数据处理及投递服务;并且生态上支持主流开源流计算对接。
以上,我们对SLS数据总线有了一个整体的认识,接下来我们将从数据接入、流量中枢、数据处理三个维度进行详细介绍。
数据接入技术架构与实践
数据接入能力概览
LogHub 作为数据总线的核心流量中枢,默认提供 HTTP/HTTPS 协议的 API 写入能力,同时,也提供了众多语言的 SDK,用以简化接入场景、增强可靠性,SDK 覆盖从Java、Go、C++等服务端应用,到Android、IOS等移动端场景,甚至 JavaScript 等前端场景。
自研的开源可观测数据采集器 iLogtail承载了服务器场景、容器场景的可观测数据采集能力,覆盖Windows、Linux操作系统及X86、ARM架构。借助于阿里云的优势,无缝支持阿里云上各类主流云服务的日志、指标、安全审计类数据的采集。
对于通用协议等支持也比较丰富,兼容 Syslog,Kafka、Promethous、JDBC、OpenTelemertry 等开源协议;支持众多开源采集工具,例如 Logstash、Fluentd、Telegraf 等。
Producer Library
针对Java、Go等大数据、高并发场景下,我们在SDK基础上提供了Java Producer、Go Producer;针对IoT/嵌入式设备,推出了C Producer。
使用 Producer 相对于直接通过 API 或 SDK 发送数据,提供了更上层的封装,性能、可靠性都有大幅提升。
- 线程安全:Producer 内所有的方法以及暴露的接口都是线程安全的。
- 异步发送:客户端计算与 I/O 逻辑分离。调用 Producer 的发送接口通常能够立即返回,Producer 内部会缓存并合并发送数据,然后批量发送以提高吞吐量。
- 失败重试:对可重试的异常,Producer 会根据用户配置的最大重试次数和重试退避时间进行重试。
- 优雅关闭:用户调用关闭方法进行关闭时,Producer 会将所有其缓存的数据进行发送,防止日志丢失。
- 高性能:通过多线程、缓存策略、批量发送等手段,有效提升发送效率。
可观测数据采集器iLogtail
iLogtail
是数据总线数据接入的重要流量来源,拥有的轻量级、高性能、自动化配置等诸多生产级别特性,可以署于物理机、虚拟机、Kubernetes等多种环境中来采集遥测数据。iLogtail的核心定位是可观测数据的采集器,帮助开发者构建统一的数据采集层,助力可观测平台打造各种上层的应用场景。通过iLogtail
可以很好的解决数据总线数据接入的问题。
得益于阿里巴巴/蚂蚁集团与公有云场景的不断锤炼,iLogtail
和开源Agent(例如Fluentd
、Logstash
、Beats
)相比,性能、资源消耗、可靠性、多租户隔离、K8s支持等硬指标上较为领先,可以满足多种业务场景下的苛刻要求。目前iLogtail
已于2022年6月29日完整开源,吸引了众多开发者的关注,GitHub Star数也于2022年11月7日突破1K。
采集高性能、低开销是iLogtail的核心优势之一。iLogtail在Linux下使用inotify作为文件监控的主要手段,提供了毫秒级延时的数据发现能力,同时为了兼顾不同操作系统以及支持各类特殊采集场景,iLogtail同时使用了轮询作为的数据的发现方式。通过使用轮询与事件并存的混合方式,iLogtail打造了一套兼具性能优势同时不失鲁棒性的文件发现机制。此外,iLogtail 采用了无锁化事件处理模型。与业界其他开源Agent为每个配置分配独立线程/Goroutine读取数据不同,iLogtail数据的读取只配置了一个线程。由于数据读取的瓶颈并不在于计算而是磁盘,单线程足以完成所有配置的事件处理以及数据读取。使用单线程使得iLogtail的事件处理和数据读取都可以在无锁环境下运行,数据结构更加轻量化,从而取得了相对多线程处理更优的性价比。
在生产环境中,一台服务存在数百个采集配置属于常态,每个配置的优先级、日志产生速度、处理方式、上传目的地址等都有可能不同,因此必须有效解决如何隔离各种自定义配置,保证采集配置QoS不因部分配置异常而受到影响的问题。iLogtail采用基于时间片的采集调度、多级高低水位反馈队列、事件非阻塞处理、流控/停采策略以及配置动态更新等多项关键技术,融合实现了兼具隔离性、公平性、可靠性、可控性、性价比五大特性的多租户隔离方案。经历了多年双11流量高峰期的考验,这套方案已经被证明相比其他开源具备较大的稳定性和性价比优势。
数据源的多样性毫不夸张的说可以是数据总线的生命线,否则就是巧妇难为无米之炊。iLogtail通过插件化的设计,突破了单纯文件采集的范畴,有效扩展了上下游生态,使得iLogtail成为真正的可观测采集器。目前iLogtail已经支持了众多数据源的接入,数据源类型覆盖Log、Metric、Trace,数据源除了文件的采集,还包括了标准协议的支持,例如HTTP、Mysql Binlog、Prometheus、Skywalking、Syslog等;iLogtail也通过eBPF支持,实现了无侵入的网路数据采集能力。数据输出生态也从SLS逐步扩展到Kafka、gPRC等,未来通过开源社区共建也会支持ClickHouse、ElasticSearch等。
面对众多异构数据的接入,数据总线一项职责就是通过数据处理构建统一的数据格式。iLogtail也提供了强大的数据处理能力,可以前置完成数据格式规整、数据过滤、上下文关联等。iLogtail整体采用PipeLine的设计,首先通过Input插件采集到数据,会经过采集配置中设定的Processor处理,之后经过Aggregator插件打包,最终通过Flusher发送到存储系统。数据处理环境包含数据切分、字段提取、过滤、数据增强等环节,所有插件可以*组合。
随着云原生落地,Logtail已全面支持Kubernetes,目前支持Docker、Containerd两种主流的容器运行时。iLogtail通过实时监控容器列表并维护容器和日志采集路径映射,结合高效的文件采集能力提供了极致的容器数据采集体验。iLogtail支持使用容器标签、环境变量、K8s标签、Pod名称、命名空间等多种方式进行容器筛选,为用户提供了便利的采集源配置能力。支持DaemonSet、Sidecar、CRD等多种部署方式,为应对不同使用场景提供了灵活的部署能力。此外,对于CICD自动化部署和运维程度要求较高的用户,iLogtail还提供了K8s原生支持,支持通过CRD的方式进行采集配置管理。该方案中,iLogtail K8s新增了一个CustomResourceDefinition扩展,名为AliyunLogConfig。同时开发了alibaba-log-controller用于监听AliyunLogConfig事件并自动创建iLogtail的采集配置,进而完成日志采集工作。
流量中枢技术架构与实践
LogHub作为SLS数据总线的流量中枢,是一个高吞吐的数据通道,支持海量可观测数据的实时接入与消费能力。在可观测数据场景下完全可以Kafka等消息队列产品,并且在性能、易用性和稳定性上更佳。
LogHub可以理解为 append-only 的 log 队列结构,通过多个 shard 组合实现 IO 和存储的水平扩展。并且在队列基础上创建多层索引,可快速定位到每一条数据在队列中的位置,赋予队列模型随机查询能力。
- 弹性: 支持1~512个分区(shard),毫秒级扩容、缩容。
- 高吞吐:单shard支持5MB/sec写入,10MB/sec读取。
- 高容错:单机failover不影响正常写入。
- 去重:支持ExtractlyOnce去重写入。
- 随机查询:1%额外存储的开销,支持任意数据随机查询(1次IO)。
Logstore/Metricstore是SLS可观测数据的采集、存储和查询单元,LogHub作为Logstore/Metricstore的队列模型,提供了数据实时写入和流式消费的能力。同时在此基础上,进行了模型扩展,进而构建出了统一的可观测分析引擎。
- 索引模型:通过创建索引,提供数据Filter能力。查询时,可以根据索引快速定位命中数据后,从队列模型高效选择所需的数据。
- 原始列存模型:针对特定字段列式存储,提供大数据统计分析能力。
- PK(时序)列存模型:针对时序数据的特点,提供了主键排序后再字段列存的模型,有效提升时序数据读取效率。
全球化支持与智能加速
LogHub 是阿里云上的基础设施,借助阿里云等全球化部署优先,与阿里云Region保持同步,这也是其他开源数据总线无法比拟的优势。可以确保全球化业务就近选择Region,也可以满足一些国家或组织相关的法律约束数据不能出境的要求。
LogHub 联合 CDN 推出了全球自动上传加速方案,基于阿里云CDN硬件资源(覆盖2800+节点,70+国家),全球数据就近接入边缘节点,通过内部高速通道路由至LogHub,大大降低网络延迟和抖动。
弹性与保序处理
在生产中我们往往会面临流量峰值和低值的情况,也会遇到因业务层映射不均衡,导致某一个分区(shard)有非常大流量的场景,弹性伸缩(Merge/Split)就是解决该问题的机制。
Shard分裂的原理
Shard分裂操作,是流量扩容的一个重要手段,是把一个readwrite的Shard分裂成两个readwrite的Shard;同时原Shard变成readonly状态,之上的数据可继续被消费读取。
合并操作和分裂操作相反,是把两个相邻的readwrite的Shard合并成一个readwrite Shard,同时原来的两个Shard变成readonly状态。
负载均衡:根据峰值、底值弹性扩容,控制成本。
下图最早只有Shard0提供服务;后随着晚间流量高峰单Shard不足以支撑业务流量,Shard0拆分为了Shard1、Shard2提供服务;等流量再次稳定,出于成本考虑,合并成一个新的Shard3提供服务。
日志保序处理:将不同实例映射到不同分区,调整分区处理能力。
对于一些业务场景有保序的需求,数据写入时往往通过Hash规则,将不同业务的数据映射到固定的Shard上。
下图中的业务场景,Shard1承载了3个DB的流量不堪重负,此时可以通过进行Shard拆分的方式,实现流量的均衡。拆分后,原来的Shard1变成只读,数据仍可被消费,但是不再接收新的数据写入,该由新拆分出的Shard3、Shard4对外提供服务。
那对于Shard调整前后的边界数据,如何保序?Loghub提供了Shard顺序消费的能力,即Shard分裂后,先消费原Shard数据(即Shard1的数据),然后同时消费由该Shard分裂的Shard数据(Hash策略保证同一业务落到一个Shard上)。同理,Shard合并场景,也是先消费原Shard数据,然后消费由原Shard合并后的新Shard数据。当然,对于不严格保序的场景,为了提升消费速率,可以关闭Shard顺序消费功能,以达到所有Shard可以同时消费的目的。
稳定性建设
粗粒度流控:Project级别
Project全局流控最主要的目的是限制用户整体资源用量,在前端就拒绝掉请求,防止流量穿透后端把整个集群打爆。
- 每秒上千个Nginx前端,将各种接收到的Project的流量、请求次数进行汇总,发送至QuotaServer。
- QuotaServer汇总所有来自Nginx的Project统计信息,判断是否超过Quota上限,进而决定是否需要采取限流措施。之后,将超过Quota的Project列表通知到所有的Nginx前端。
- Nginx前端获取禁用Project列表之后,立刻做出反应,拒绝这些Project的请求。
细粒度流控:Shard级别
Shard级别流控则更加精细、语义(错误码)更加明确、可控性更强。
- 每个Shard明确定义处理能力, 如5MB/sec写入,10MB/sec的读取。
- 当Shard队列出现堵塞,根据Shard流量是否超过quota,返回用户是限流还是系统错误(返回的Http错误码是403还是500),同时将Shard限流信息通知QuotaServer。
- QuotaServer接收到限流信息后,可瞬时将限流信息同步至所有的Nginx。
- Nginx端获得Shard的流控信息之后,对Shard进行精确的流控。
数