导读:网易从2015年就开始了云原生的探索与实践,作为可观测性的重要一环,日志平台也经历了从主机到容器的演进,支撑了集团内各业务部门的大规模云原生化改造。本文会讲述在这个过程中我们遇到的问题,如何演进和改造,并从中沉淀了哪些经验与最佳实践。
主要内容包括:
- Operator化的⽇志采集
- ⼤规模场景下的困境与挑战
- 开源Loggie的现在与未来
01 云原⽣最初的探索 Operator化的⽇志采集
早期公司内部业务跑在物理机上时,各业务使用的日志采集工具、日志中转和存储、配置下发都比较混乱,选型种类多。公司内基于轻舟平台推进了各服务容器化和云原生化,随着服务不断地迁移到K8s上,我们关于采集K8s容器日志和日志平台方面积累很多实践经验。
云原生日志是什么样的,容器日志采集和主机上服务的日志采集有什么差异?首先Pod在K8s中会频繁地发生迁移,手动去节点上变更配置日志采集配置不太切实际。 同时,云原生环境中日志存储的形式多样,有容器标准输出和HostPath,EmptyDir,PV等。另外,采集日志后,一般会根据云原生环境的Namespace、Pod、Container、Node甚至容器环境变量、Label等维度进行检索和过滤,这些元信息需要在日志采集时被注入。
常见的云原生环境日志采集有以下三种思路:
- 只采集标准输出
日志打印到标准输出的操作虽然符合云原生十二要素,但是很多业务还是习惯输出到日志文件中,而且只采集标准输出日志难以满足一些复杂业务的需求。常规的采集标准输出日志的方式是挂载/var/lib/docker,/var/log/pods下的目录。同时需要注意选型的日志Agent是否支持注入k8s元信息。
- Agent⽇志路径全局挂载
日志可以放在EmptyDir,HostPath或PV存储中,通过DaemonSet方式把日志采集Agent部署到每一台k8s节点上。EmptyDir等存储映射到节点的目录可以通过通配的方式配置到Agent进行采集,由Agent读取配置路径下的日志文件。
但这种通用匹配的方式无法根据服务级别进行单独配置。如果需要配置只采集某些服务的日志、或是某一个应用和其他应用的格式不同,例如某个应用的日志是多行的日志,此时需要单独修改配置,管理成本比较高。
- Sidecar⽅式
每个业务Pod在部署时都增加一个日志采集的Agent SideCar,可以通过容器平台或k8s webhook的方式把日志采集Agent注入到Pod内。Agent可以通过挂载相同的HostPath或EmptyDir的方式采集到业务容器的日志。
这种方式的弊端是对Pod侵入性强,Pod数量多时资源消耗较大。
对比DaemonSet和SideCar两种部署方式,DaemonSet在稳定性、侵入性、资源占用方面有优势,但隔离性、性能方面SideCar方式更好。我们实践中会优先使用DaemonSet方式采集日志。如果某个服务日志量较大、吞吐量较高,可以考虑为该服务的Pod配置Sidecar方式采集日志。
针对日志采集方面的痛点,我们提出的第一个初步方案是:自研的Ripple Operator配合开源的日志采集客户端Filebeat进行业务日志采集。
部署方面,FileBeat、Ripple通过SideCar方式部署在一起,Ripple监听k8s。在使用方面,用户通过CRD,一个CRD可以表示一个服务的日志采集配置,Ripple可以和k8s交互感知到Pod生命周期。Ripple可以下发配置到Agent,自动添加Pod相关元信息并注入到日志中。存储方面HostPath、EmptyDir、PV、Docker Rootfs都可以支持。
图中日志采集的架构先在网易严选进行了应用, 后面逐渐地被公司内绝大多数部门参考落地,具体架构会根据实际环境有所区别。首先日志平台下发配置,Ripple监听到K8s的CRD配置变动后,把配置下发给Filebeat。Filebeat把采集到的日志发送到中转机,中转机发送给Kafka,Flink消费Kafka数据进行处理后转发给后端日志存储。
02 进⼊深⽔区 ⼤规模场景下的困境与挑战
随着接入的业务越来越多,1.0版本的日志架构逐渐暴露了一些问题:超大规模下系统的稳定性问题、日志平台性能问题、排障困难和排障需求不断增多、维护人力成本直线升高等。
在前面介绍的架构中,Filebeat会把每个服务的日志发送给中转机。但是Filebeat是一个单一的队列架构,在中转机的阻塞会影响整个节点的Filebeat日志上报。
我们尝试把Filebeat改造成多队列的模式增强隔离性,但是由于Filebeat本身的设计架构局限,重构的效果和运行状态都不理想,同时与开源版本的维护和升级存在很大困难。
Filebeat只支持单个Output,当用户需要把日志发送到多个Kafka集群时,只能在同一个节点上部署多个Filebeat。导致运维成本高、计算资源消耗大、水平扩展困难。
Filebeat设计之初是为了解决Logstash的问题,作为轻量级的日志采集端运行,不适合作为中转。Logstash在日志切分、解析等方面表现出色,但是问题在于性能较弱。虽然Flink性能和表现出色,但我们的很多场景只需要简单的日志切分,Flink一般依赖流式处理基础平台的支持,同时在交付过程中用户要额外付出Flink的机器和运维成本,所以我们需要一个更轻量、成本更低的方案。
此外,日志采集过程的可观测性和稳定性也很重要。在用户使用过程中经常会遇到日志没有按预期采集、日志采集延迟、日志丢失、日志量增长速度过快磁盘爆满等问题,Filebeat没有提供这些完善等监控指标,同时还需要额外部署prometheus exporter。
我们以一个线上问题为例。线上某集群经常遇到磁盘使用量短时间内暴增到90%以上并触发报警的情况。实际登录节点排查问题后,发现报警的原因在于下游Kafka吞吐量不够,一些日志文件没有完全采集完毕,Filebeat进程默认保留文件句柄直到文件采集完毕,由于文件句柄没有释放,底层的文件无法被真正删除。但在这种情况下,Filebeat缺乏输入延迟、发送延迟和堆积情况等指标,影响稳定性。
在一个大流量的节点,我们发现Filebeat发送数据到下游Kafka时整体数据处理速度不理想,于是进行了很多优化的尝试,比如换SSD的Kafka集群、改Kafka配置、优化Filebeat配置等调优。我们发现在Filebeat不打开数据压缩的情况下,最大数据发送速度达到80MB/s后很难再有提升,打开数据压缩后Filebeat的CPU的消耗又暴增。调整Kafka或Filebeat参数、机器等配置的优化效果不明显。
那么除了Filebeat外,其他的日志采集工具能不能满足需求?当前主流的日志采集Agent对容器化场景下的日志采集支持不太理想,大部分开源日志采集工具仅支持到容器标准输出的采集,对云原生下日志采集也没有整体解决方案。
03 新的征程 开源Loggie的现在和未来
基于Filebeat和现有主流数据采集工具的情况,我们决定自研日志采集Agent——Loggie。它是基于Golang的轻量级、⾼性能、云原⽣⽇志采集、聚合Agent,⽀持多Pipeline和组件热插拔,整体架构如图。
我们加强了Agent的监控和稳定性,可以利用Monitor EventBus上报Agent运行情况,同时暴露了可调用的Restful API和Prometheus拉取监控数据的接口。配置管理方面,目前使用K8S进行配置下发和管理,后续会接入更多配置中心供用户管理配置。
Loggie提供了一栈式日志解决方案,新Agent不再分开维护Agent和中转机。作为一个可插拔组件,Agent既可以作为日志采集端使用,也可以作为中转机使用,同时支持日志中转、过滤、解析、切分、⽇志报警。Loggie同时提供了云原生日志采集完整解决方案,比如通过CRD的形式利用K8s的能力下发配置、部署等。基于我们长期的大规模运维日志收集服务的经验,Loggie沉淀了全⽅位的可观测性、快速排障、异常预警、⾃动化运维能⼒的功能。Loggie基于Golang开发,性能非常好,它资源占⽤小、吞吐性能优异、开发效率高维护成本低。
- ⼀栈式⽇志解决⽅案
Agent既可以作为日志采集客户端,也可以作为中转机使用。在日志中转方面,我们通过实现Interceptor模块逻辑实现日志的分流、聚合、转存、解析、切分等。通过K8s的CRD可以下发日志采集Agent配置。新的日志采集架构整体维护成本更低。
在采用Loggie之前我们有两种报警方案:第一种是基于ElastAlert轮询Elasticsearch进行日志报警,该方案存在配置下发需要手动配置、告警接入不能自动化和高可用不完善等问题;第二种是基于Flink解析关键字或是对日志进行正则匹配进行报警,在前面我们已经介绍过,对于整体日志采集服务而言Flink是比较重量级的。
在采用Loggie后,我们可以使用logAlert Interceptor,在日志采集过程中就可以通过关键字匹配识别info或error进行报警。也可以通过单独部署Elasticsearch Source的Loggie Aggregator对Elasticsearch进行轮询,把信息发送到Prometheus进行报警。新版日志报警架构更轻量,维护起来也更简单。
在项目开发方面,我们秉承微内核、插件化、组件化的原则,把所有组件抽象为component。开发者通过实现生命周期接口,可以快速开发数据发送端逻辑、数据源读取逻辑、处理逻辑和服务注册逻辑。这样的设计让需求能更快更灵活地实现,大大了日志采集侧的开发效率。
基于灵活高效的Agent数据源设计,我们可以配置kubeEvent source采集K8S Event数据作为监控报警的补充。相比重新实现一个采集K8S Event项目,只需要在Loggie上实现对应的source,其他如队列缓存、解析日志、发送日志的逻辑都可以复用。
- 云原⽣的⽇志形态
用户通过配置LogConfig CRD,可以指定采集Pod、节点、集群日志信息。图中的例子是通过labelSelector匹配要采集的Pod,并配置容器内日志路径;通过Sink CRD和Interceptor CRD可以配置日志发送和日志处理、解析方式。
通过CRD的方式打通日志处理链路,用户可以通过配置CRD定义日志中转、聚合和发送的方案,可以配置日志的解析、限流、报警规则,在进行私有化部署和将日志服务平台化时大大降低了配置管理的复杂度,一旦有配置变更,k8s可以快速地把配置同步到日志采集服务中。
在k8s中我们提供了多种部署方案, 作为Agent部署时,我们提供了DaemonSet或SideCar方式进行部署。作为中转节点部署时可以作为StatefulSet部署。日志采集的架构上更加灵活,可以直接发送到ES,也可以发送给Kafka再交给中转节点,部署方便。
- ⽣产级特性
我们为日志采集Agent添加了完善的指标,包括日志采集进度、长期采集未完成、采集发送延迟、FD数量、输出QPS和其他服务级别指标,提供了原生Prometheus格式的接口、RestAPI和指标发送Kafka等方式暴露指标。
日志采集服务大规模部署的稳定性和排障两方面有很大提升。我们通过独立各个采集任务Pipeline增强服务隔离性,还可以通过Interceptor提供了QPS限制,定时清理日志防止磁盘写满,增加了日志文件突发增长检测和合理的Fd保留机制等。
- 资源与性能
我们利用Filebeat进行了基准测试,在处理单⾏、单⽂件、相同发送并发度、⽆解析的场景下,发送日志⾄Kafka。
测试结果上看Loggie性能大幅度提升,消耗CPU为Filebeat的1/4、吞吐量为Filebeat的1.6-2.6倍,且极限吞吐量与Filebeat的80MB/s瓶颈提升到200MB/s以上。
04 Loggie开源计划
Loggie已经开源,下面是我们项目推进的RoadMap和正在做的事,欢迎感兴趣的开发者一起参与项目。
Loggie项目地址:https://github.com/loggie-io/...
分享嘉宾:傅轶 网易数帆 轻舟日志平台负责人、架构师
目前专注网易数帆轻舟云原生日志平台研发,致力于云原生技术及其生态体系建设和商业化落地,对Kubernetes、Serverless、可观测性等有较深入研究,具有丰富的云原生分布式架构设计开发经验与项目实践。
编辑整理:张德通 Treelab
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。