1. 采集代理发展回顾
iLogtail 作为一款开创性的轻量级日志采集器,历经 13 载风雨,始终致力于高效地从多元化的数据源中萃取、处理可观测信息,并无缝传输至阿里云日志服务或各类日志分析平台。今年,适逢 iLogtail 开源两周年的里程碑时刻,我们将回顾 iLogtail 的技术演进之路,领略其不断突破边界、引领可观测采集未来的创新力量。
1.1 采集代理发展简史
早在 2013 年,iLogtail 的第一个版本就随着飞天 5K 系统上线。飞天5K项目是一个调度 5000 台计算机合力为一个超级计算机的分布式操作系统项目,当时研发 iLogtail 的目的也很简单,将分散在几千台机器上的日志数据统一收集到一个中心数据仓库中,方便查阅和分析。在这一阶段,iLogtail 的技术投入重点是基础日志采集,典型的技术功能点有通过 inotify 感知日志变更实现日志实时采集、飞天日志解析完成结构化、实时将日志发送到远端存储、基础的自监控日志上报等。
时间来到 2015 年,阿里巴巴开始推动集团业务上云,大家所知道的 2019 年双 11 宣布核心系统全面上云,其实这一过程从 2015 年就已经开始了。在这一过程中,iLogtail 的用户从阿里云扩展到整个集团,对其处理能力的丰富程度和稳定性就提出了更高的要求,iLogtail 也在这种背景下打磨出了一身过硬的本领,比如帮助故障隔离的高低水位反馈队列、防止日志丢失的 checkpoint 机制、多租户管理能力以及更丰富的日志处理能力。
来到 2017 年,随着 SLS 的正式商业化和 ACK 服务上线,iLogtail 的用户数呈现几何级增长,新的需求如雨后春笋般涌现,同时 iLogtail 的使用场景也从主机扩展到了容器。为了适应新环境的变化,iLogtail 演化出了 Go 插件系统,在这一子系统的加持下,iLogtail 迅速支持了容器日志采集、K8s 元信息自动打标等功能,同时也开始往时序、追踪数据的接入开始迈进。
在 2022 年,iLogtail 正式向外界完整开源,并且将版本提升到 1.0.0,这也标志着 iLogtail 的技术成熟,从一个单一的日志采集器蜕变为功能完整的可观测数据采集器。1.0 系列实现了对常见容器运行时的完整支持,适用于云原生环境,同时在开源社区同学的努力下,丰富了其对下游生态的输出支持,同时紧跟时代步伐,增加了对可观测性数据的第四根柱子——Profiling 的数据接入支持。
2024 年,在 iLogtail 开源两周年之际,iLogtail 发布了 2.0.0 版本,该版本结合社区贡献、顺应市场变化在易用性、性能和可靠性方面,相对 1.0 系列版本均有显著提升。
同样是这段时间,如果我们将视线放眼全球,那么我们可以看到,iLogtail 的发展历史也是云计算发展历史的一个缩影。
2011 年,Hadoop 1.0.0 版本正式发布,它标志着大数据技术的成熟和广泛应用。同年,Hadoop 技术栈中为采集大数据而生的 Flume 也正式开源。
2013 年,具备更灵活的数据采集和处理能力的 Logstash 发布了其 1.0.0 版本。
2014 年,Docker 1.0 发布,标志着容器时代的序幕拉开。
2015 年,Kubernetes 1.0.0 的发布正式开启了计算范式的革命,使容器和微服务成为现代 IT 基础设施的重要组成部分。
2016 年,在这样的背景下,与 K8s 有着更友好集成的 Beats 和 Fluentd 开源并发布了他们的 1.0.0 版本。
2017 年,K8s 生态中指标采集的事实标准 Prometheus 发布了 2.0 版本,这一发布标志着 K8s 监控系统的成熟。
2017-2018 年,阿里云、亚马逊 AWS、微软 Azure 相继发布了各自基于 K8s 的商业化容器计算产品,而谷歌也正式将其容器计算产品更名为 GKE。这些事件标志着 K8s 这一容器编排引擎的成熟和大规模使用。
2019 年,耳熟能详的可观测数据采集代理 FluentBit、Vector 和 OpenTelemetry Collector 均在此年发布了 1.0.0 版本。其中 FluentBit 主打高性能和低资源开销的日志采集、Vector 则具备更强的指标采集能力和处理编排能力、而 OpenTelemetry Collector 主打生态,数据源最为丰富,并且致力于推动可观测数据的标准化。
1.2 采集代理技术趋势
总结 iLogtail 的发展史和业界主流采集代理的发展历程,不难发现采集代理发展的技术趋势,主要有以下六点:
1)高性能
从采集器使用的编程语言可以看出,年代比较久远的采集器,如 Flume、Fluentd,大多采用更侧重开发效率的 Java、Ruby 等语言编写,而近些年现代化的采集器,如 FluentBit、iLogtail、Vector,都使用 C、C++、Rust 等更侧重执行性能的语言编写,这体现出可观测行业的逐步成熟,对高性能低成本的追求更加极致。
2)高可靠
不难发现,现代的采集代理均具备内存上限控制、反压、磁盘缓冲队列等有助于提高系统整体稳定性和保证数据不丢失的技术。而 iLogtail 由于诞生于阿里内部的复杂多租环境,需要保证租户间的故障隔离,因此还具备多租隔离的能力。
3)灵活性
现代化的采集代理都使用了模块化和插件化的设计思路,可以灵活地对采集进行扩展和二次开发。例如,iLogtail 就支持 C++ 和 Go 两种语言编写插件,而 FluentBit 则通过 WebAssembly 技术实现了任意语言插件开发的支持。灵活性的另一方面体现在数据处理上,每个采集代理都具备各具特色的数据处理编程语言。例如 FluentBit 使用 SQL 进行流处理,iLogtail 推出 SPL 非结构化流处理语言,Vector 使用 VRL 作为转换数据的主要手段,而 OpenTelemetry 则使用 OTTL。
4)云原生支持
现代的采集代理都必须与 K8s 系统有良好的结合能力,不仅需要能自动进行服务发现,而且要能利用好 K8s 的元信息进行采集目标筛选、自动打标等。部分采集代理通过官方或者第三方提供了 CRD 和 Operator 能力,可以通过云原生的方式自动化管理代理部署和采集配置。
5)全能化
iLogtail 和 FluentBit 均是从 Log 作为主要场景研发,拓展出 Metric 和 Trace 采集能力,而 OpenTelemetry 则是以 Trace 见长,同时发展出 Log、Metric、Profiling 采集能力。因此,现代的采集器一定是全能的,单一全能采集代理就能取代数个传统功能单一的采集代理的工作。同时,我们也可以看到 eBPF、Instrument 等新型的采集技术也不断融合到采集代理中。
6)智能化
随着 AI 时代的奇点临近,客户对采集代理智能化的期盼愈发强烈。很多厂商已经喊出了“免配置”、“端到端自动化”、“采集即治理”等口号,因此可以断定,未来的采集器一定是智能化的,具备自动发现、自动打标、自动配置等能力。
2. 技术进化详解
iLogtail 也是跟随市场和时代发展的大趋势进行技术演进的,接下来本文将通过四条线索来介绍 iLogtail 的进化历程。
2.1 从日志到通用可观测
在最初的日子里 iLogtail 只有日志采集的单一功能,在这一阶段,iLogtail 的技术投入主要在解决如何将日志实时、稳定地采集上来,主要难点在于适应不同的日志轮转策略、不同的实例流量特征、各种网络异常或发现下游异常的情况等。随着云原生、 K8s 的兴起,iLogtail 需要支持容器内标准输出和容器内文件的采集。起初 iLogtail 完全使用 C++ 编写,缺乏容器发现能力,只能与容器产品绑定,由容器产品提供容器元信息。但不久后 iLogtail 就使用 CGO 接口研发了 Go 插件系统,并通过 Go 插件打通与本地 docker.sock 的通信机制补齐了容器发现这一短板。
如图所示,Go 插件流水线主要由 Input、Processor、Aggregator、Flusher 4 个部分组成,Processor 的输入不仅可以来自 Go Input,也可以来自 C++ 文件,Flusher 不仅可以输出到 SLS,而且已经具备输出到第三方存储的能力。Go 插件系统的引入,为 iLogtail 后续引入新数据源打开了新世界的大门,在随后的版本中 system-journald、windows event、mysql binlog 等日志数据源被迅速引入,iLogtail 在日志数据源丰富度上得到了极大的加强。
不只是日志(Log),在 Go 插件系统的帮助下,iLogtail 逐步往所有类型可观测数据采集迈进,逐步引入了指标(Metric)、追踪(Trace)和剖面(Profile)的采集支持。而开源后,在社区成员的共同努力下,Go Flusher 往第三方存储输出的潜能被完全释放,支持了如 Kafka、ES、CK 等热门存储。如今 iLogtail 已经具备超过 40 种采集插件、10 余种输出插件并且完全兼容 OpenTelemetry 协议输入输出。
那么在 iLogtail 插件生态繁荣的背后,我们也不断在演进支撑插件的 Go 插件框架。其中最值得一提的是我们对数据结构通用化的改造。由于历史原因,Go 插件框架内部使用的数据结构是一个 Log 的 protobuf,虽然它可以很高效地表达日志数据,但对于其他数据的表达却显得生搬硬套,这也导致了开发指标、追踪、剖面等插件十分拧巴,数据处理效率也比较低下。因此在社区 PMC 成员刘浩杨的推动下,我们将 Go 的流水线升级到了 V2 版本,使用了 PipelineEvent 这一通用的可观测数据结构,具体的 LogEvent、MetricEvent、SpanEvent 从它进行派生,它们各自可以为自身的数据特点进行优化,表达更加自然,处理起来也更加高效。
在 iLogtail 2.0 版本中,不仅是 Go 插件系统,iLogtail 的 C++ 插件系统也完成了通用化数据结构改造,至此指标、追踪、剖面数据都可以流畅地进行表达,甚至可以派生出新的数据结构,整个插件框架完成了从日志到通用可观测数据底座的转型。
2.2 从可扩展到可编程
从上面的介绍中可以看到,iLogtail 的可扩展性并不是第一天就设计好的。原来的 iLogtail 更侧重于性能的极致性,很多内存复用、免拷贝的特性都是基于单体架构、固定流程优化的。但这种架构难以适应数据处理高频的需求变化,耦合的流程意味着所有数据都必须经过固定的处理步骤,使得每增加一个处理步骤都会使已有的 Parse 流程更加臃肿。为了彻底解决这个问题,iLogtail 需要对处理流水线进行模块化重构,以支持处理插件的灵活组合和自由编排,并能通过新增插件达到处理能力可扩展的目的。
在引入 Go 插件框架后,其实 iLogtail 已经解决了部分可扩展的问题,但因为跨语言通信带来的损耗和 Go 本身的内存固定开销,所以带来可扩展性的同时也牺牲了部分采集代理的性能和资源开销。在 iLogtail 2.0 中,我们对 C++ 部分的流水线也进行了模块化改造,改造后的流水线可以对 input、processor、flusher 进行插件扩展,并且支持插件的灵活编排和组合。C++ 流水线的改造,使得 iLogtail 原来的性能和资源占用优势得到了进一步的巩固,在可扩展和高性能之间再也不用妥协。
此外,在引入模块化的 C++ 流水线的同时,我们对 C++ 和 Go 之间的数据传递链路进行了扩展,优化了他们的互操作性。在 2.0 版本中,C++ 处理流水线终于可以与 Go 处理流水线进行串联执行,而在后续版本中我们即将支持 Go 输入插件与 C++ 处理流水线串联执行,使得任意输入都可以享受到 C++ 处理的高性能。
在可扩展的基础上,iLogtail 顺应时代发展的大趋势,进一步发展了 SPL 可编程数据处理的功能。SPL 进一步降低了数据处理的门槛,提供了统一的数据处理逻辑表达。从上图的示例中可以看到,我们日志采集到的数据大部分情况下是非结构化的,而真正方便做日志查询分析的日志应该是结构化的,因此最大化日志的价值要求我们能够高效地探索非结构化的日志数据,并能将其结构化,而这正是 SPL 能够提供的核心价值。通过图中的案例,我们可以了解 SPL 是如何工作的。在这段 SPL 中,我们完成了日志的正则提取、时间提取、JSON 解析和字段筛选,如果要完成同样的工作,使用插件的话就需要编写 4 个插件的配置。因此,在复杂数据处理场景下,SPL 在表达的流畅性和精简性上更胜一筹。
对于 iLogtail 的使用者来说,可以根据自身的需求自由地选择数据处理的方式,下表总结了两种可编程引擎的差异。
2.3 从行业最快到超越自我
“iLogtail 在采集速率上优势明显。”这句话不是黄婆卖瓜,自卖自夸,而是来自《性能与可靠的超强碰撞!第三方测评开源日志采集器》。从测评节选的表格中可以看到,在同样的输入条件下,iLogtail 的采集速率明显领先于其他主要的开源采集代理。即使同样折算到单核,iLogtail 也具有明显优势。
这和从第一天起,iLogtail 就把性能作为首要设计目标之一是分不开的。iLogtail 在技术选型上采用了单线程事件驱动,并且结合基于时间片调度、无锁化、数据流处理免拷贝等技术,使 iLogtail 在具备高性能的同时资源占用也极低。
但正如埃隆·马斯克所说:“评价任何技术的正确方式不是和竞品比较,而是和物理学极限进行比较。”因此 iLogtail 并没有不思进取,安于现状,而是在 2.0 中实现了对自我性能的超越。
在 2.0 版本中,我们对内存管理进行了进一步优化,使用 Memory Arena 技术减少内存的分配次数,并在新模型的数据结构中大量使用 Zero Copy 技术减少内存拷贝。如图中左下所示,读取数据后 iLogtail 会尽可能复用读取分配的内存,尽可能减少内存的再分配。因此 iLogtail 2.0 不仅在可扩展性上超越了 1.x 版本,而且在采集的性能上也毫不妥协。在日志多行切分的能力上,iLogtail 2.0 对状态机进行了重构,支持复杂模式,不限于行首正则、行尾正则、行中正则和它们的组合,并且支持多种格式的嵌套,如 containerd 换行+内容多行。不仅切分能力更强,改进的匹配算法在部分数据集上提供了相比原来最高 7 倍的性能提升。
在 iLogtail 2.0 中还对核心功能进行了 C++ 重写,如数据过滤、脱敏等,而容器标准输出采集则是它们的典型代表,全新的容器标准输出采集插件,不仅在大数据量、快速轮转时稳定性更强,并且实测有最高 3 倍的性能提升,资源占用更是只有原来的 N 分之一。从右下图,可以明显地看到新老采集插件的性能差距(上面 2 条线分别为新插件采集 containerd 和 docker 标准输出到表现)。
同时,社区的参与和贡献对于 iLogtail 的持续增强起着至关重要的作用。社区成员孙宇持续为 Kafka 相关的插件贡献优化,社区成员梁若羽为 Elastic Search Flusher 引入批量发送性能优化,社区成员陈柄畅帮助 iLogtail 升级到 Go 1.19 并加入了内存参数优化的机制。正是这样的社区活跃度和各成员的专业贡献,使得iLogtail在性能提升和稳定性增强方面不断取得显著的进步。
2.4 从繁花乱眼到条理自现
配置结构优化
由于历史发展的局限性,原来 iLogtail 的配置文件仅仅考虑了有限的几种日志采集场景,而随着功能点演化,iLogtail 的配置就如图中最左侧所示越来越臃肿,由于缺乏结构和可扩展考虑,配置的嵌套越来越深、而平铺的字段也无法表达数据处理逻辑的先后顺序。这样的配置文件显然是难以使用的,因此在 iLogtail 开源时,我们使用了适配器模式,从表面上将 iLogtail 的配置文件改成了结构化的 YAML 格式。但当时一些根本性的问题还没有得到解决,导致例如 accelerate 插件是不能和其他插件组合使用的,处理插件的一些参数仍然平铺。
而在 iLogtail 2.0 中这些遗留问题都得到了彻底的解决,如图中最右侧所示,原生的结构化配置摒弃了原有的种种限制,inputs、processors、flushers 井然有序,每个 processor 独立明确,可以非常清晰地表达整个数据采集的 pipeline。这种配置方式的扩展性极强,当需要添加新的采集插件时,不会干扰原有的配置架构,只需无缝集成即可投入使用。
代码结构优化
iLogtail 的代码库已经有十多年的历史,初期单体架构的设计和持续功能的叠加已经使得 iLogtail 的代码结构变得复杂难懂。一个负责读取数据的 Reader 既要作为数据输入,又要负责数据处理,对其方法的调用点遍布读取、处理、发送等各个阶段,零散的调用点使得代码的逻辑关系难以分辨,对其一处的改动往往牵一发而动全身,这种难以理解、无法扩展的问题已经影响到了 iLogtail 的发展。
为了让项目焕发生机,iLogtail 2.0 对代码结构整体进行了重构,将其拆分为 Pipeline 核心计算框架和 Plugin 计算插件两大部分,并依次对代码目录进行了调整。Pipeline 计算框架主要负责配置管理、资源分配、任务调度、数据路由、重试反馈等,而 Plugin 计算插件则负责具体的数据读取、数据处理和数据发送工作。经过重构后的代码结构清晰、结构有条,能够更好地帮助开发者理解 iLogtail 的代码。新的 C++ 插件体系顶层都扩展自 Plugin 接口,随后细分为 Input、Processor、Flusher 接口,接口清楚、层次清晰,我们希望更多的开发者能够参与 C++ 插件和核心代码的开发,进一步壮大社区发展。
自监控接口优化
虽然在 iLogtail 诞生之初开始就已经具备了一定的自监控能力,但其具有一定的局限性。从下图中我们就可以看到,其上报 Profile 的接口是专为日志单一场景设计的,字段也是固定的。在代码细节上,存储指标的是一个大 Map,由于多个线程都会往里面写入指标,因此每次调用都需要加锁,发送读取也不例外,这就导致自监控的性能不高。到 iLogtail 2.0,我们演进出了一套全新的灵活、可扩展、无锁化自监控框架。这套框架采用以下技术解决扩展性和性能问题:首先,该框架允许插件在初始化时注册自己的指标到自监控框架中,这使得任何新开发的插件都可以接入自定义的指标。其次,自监控框架使用了原子变量、读写分离、预定义标签等技术,使得指标框架的接口大部分都是无锁的,提高了整体性能。
社区贡献外部仓库集成方案
已经有许多开发者基于 iLogail 开发了自己的可观测采集代理版本,在二次开发的过程中,存在如何将私有仓库的代码与开源代码联合编译的问题。如果处理不当,可能导致代码冲突,难以跟上社区节奏向后演进。为了解决这一问题,社区成员朱舜佳提出了一种外部仓库集成方案。这一方案的大致思路是在 Go 部分,使用 plugins.yml 和 external_plugins.yml 两个文件在预编译阶段注册插件。
其中 iLogtail 主仓库维护plugins.yml,并忽略external_plugins.yml,而私有仓库则维护自己的 external_plugins.yml 版本。在预编译时,预编译脚本会根据环境变量自动合并两个文件的内容,生成完整的 go.mod 和 Go 源码,然后进行完整代码的编译。在 C++ 部分,与私有仓库的融合则是通过 CMakeList.txt 中的自定义变量进行实现,有兴趣的同学可以参阅社区文档
经过上述一系列关键点的优化改造,iLogtail 的开发者体验得到了显著提升,不仅大大提升了开发效率,同时也为社区的持续繁荣和发展奠定了坚实的基础。
3. 总结与展望
在自然界的演化史中,每一个物种的进化都是对其生存环境变化的响应。这种适应性的进化使生物能在竞争激烈的生态中占据一席之地。同理,iLogtail 的技术进化也是对信息时代日益变化的数字环境的一种智慧适应。它不仅仅是技术的简单迭代,而是一场深思熟虑的适应过程,确保在可观测数据采集的广阔领域中保持领先。
iLogtail 的发展历程,如同在自然界中那些经过无数世代演化而趋于完善的生物一样,它通过不断地自我优化和调整来应对外界的挑战。iLogtail 从一个初级的日志采集工具起步,经历了从主机时代到云原生时代的转变,受阿里双11、企业级云服务等场景的历练,逐步蜕变为全能的端上可观测数据创新平台。在开源的平台上,iLogtail 与不同基因的开发者相遇,碰撞出火花,接触到更广泛的应用场景,从而进一步加速了其在可靠性、高性能和灵活性方面的技术进化。
如同自然界,环境的变化是无止境的,iLogtail 的进化之路也将永无休止。iLogtail 已升级为 LoongCollector,面对未来,它必须坚持"适者生存"的原则,继续其进化之旅,引进更先进的技术,以在数据采集和分析的激烈竞争中立于不败之地。这种进化不仅将使 LoongCollector 在技术上保持领先,也将推动整个可观测领域向前发展。让我们共同期待,LoongCollector 如同生物界中那些坚韧的物种,能在数字化时代的广阔舞台上绽放光彩,带领我们一起进入一个更智能、更高效的未来。
推荐阅读:《iLogtail 开源两周年:感恩遇见,畅想未来》
作者: 余韬(迅飞)