容器日志采集利器:Filebeat深度剖析与实践

本文探讨了Filebeat如何发现并采集日志,确保数据不丢失,以及在故障后如何恢复。同时,文章介绍了如何处理Filebeat资源占用问题,以及在Docker和Kubernetes环境中的配置。此外,还讨论了自定义开发Filebeat output和processor的方法,包括直接修改源码、复制main.go以及使用Golang Plugin机制。
摘要由CSDN通过智能技术生成

640?wx_fmt=jpeg

在云原生时代和容器化浪潮中,容器的日志采集是一个看起来不起眼却又无法忽视的重要议题。对于容器日志采集我们常用的工具有Filebeat和Fluentd,两者对比各有优劣,相比基于Ruby的Fluentd,考虑到可定制性,我们一般默认选择Golang技术栈的Filbeat作为主力的日志采集Agent。
相比较传统的日志采集方式,容器化下单节点会运行更多的服务,负载也会有更短的生命周期,而这些更容易对日志采集Agent造成压力,虽然Filebeat足够轻量级和高性能,但如果不了解Filebeat的机制,不合理的配置Filebeat,实际的生产环境使用中可能也会给我们带来意想不到的麻烦和难题。
整体架构

640?wx_fmt=png


日志采集的功能看起来不复杂,主要功能无非就是找到配置的日志文件,然后读取并处理,发送至相应的后端如Elasticsearch、Kafka等。
Filebeat官网有张示意图,如下所示:
640?wx_fmt=png
针对每个日志文件,Filebeat都会启动一个harvester协程,即一个goroutine,在该goroutine中不停的读取日志文件,直到文件的EOF末尾。一个最简单的表示采集目录的input配置大概如下所示:
 
 
  1. filebeat.inputs:

  2. - type: log

  3. # Paths that should be crawled and fetched. Glob based paths.

  4. paths:

  5. - /var/log/*.log


不同的harvester goroutine采集到的日志数据都会发送至一个全局的队列queue中,queue的实现有两种:基于内存和基于磁盘的队列,目前基于磁盘的队列还是处于alpha阶段,Filebeat默认启用的是基于内存的缓存队列。
每当队列中的数据缓存到一定的大小或者超过了定时的时间(默认1s),会被注册的client从队列中消费,发送至配置的后端。目前可以设置的client有Kafka、Elasticsearch、Redis等。
虽然这一切看着挺简单,但在实际使用中,我们还是需要考虑更多的问题,例如:
  • 日志文件是如何被Filbebeat发现又是如何被采集的?

  • Filebeat是如何确保日志采集发送到远程的存储中,不丢失一条数据的?

  • 如果Filebeat挂掉,下次采集如何确保从上次的状态开始而不会重新采集所有日志?

  • Filebeat的内存或者CPU占用过多,该如何分析解决?

  • Filebeat如何支持Docker和Kubernetes,如何配置容器化下的日志采集?

  • 想让Filebeat采集的日志发送至的后端存储,如果原生不支持,怎样定制化开发?


这些均需要对Filebeat有更深入的理解,下面让我们跟随Filebeat的源码一起探究其中的实现机制。
一条日志是如何被采集的

640?wx_fmt=png


Filebeat源码归属于beats项目,而beats项目的设计初衷是为了采集各类的数据,所以beats抽象出了一个Libbeat库,基于Libbeat我们可以快速的开发实现一个采集的工具,除了Filebeat,还有像Metricbeat、Packetbeat等官方的项目也是在beats工程中。
如果我们大致看一下代码就会发现,Libbeat已经实现了内存缓存队列MemQueue、几种output日志发送客户端,数据的过滤处理processor等通用功能,而Filebeat只需要实现日志文件的读取等和日志相关的逻辑即可。
从代码的实现角度来看,Filebeat大概可以分以下几个模块:
  • input:找到配置的日志文件,启动harvester

  • harvester:读取文件,发送至spooler

  • spooler:缓存日志数据,直到可以发送至publisher

  • publisher:发送日志至后端,同时通知registrar

  • registrar:记录日志文件被采集的状态


找到日志文件
对于日志文件的采集和生命周期管理,Filebeat抽象出一个Crawler的结构体,在Filebeat启动后,crawler会根据配置创建,然后遍历并运行每个input:
 
 
  1. for _, inputConfig := range c.inputConfigs {

  2. err := c.startInput(pipeline, inputConfig, r.GetStates())

  3. }


在每个input运行的逻辑里,首先会根据配置获取匹配的日志文件,需要注意的是,这里的匹配方式并非正则,而是采用linux glob的规则,和正则还是有一些区别。
 
 
  1. matches, err := filepath.Glob(path)


获取到了所有匹配的日志文件之后,会经过一些复杂的过滤,例如如果配置了excludefiles则会忽略这类文件,同时还会查询文件的状态,如果文件的最近一次修改时间大于ignoreolder的配置,也会不去采集该文件。
读取日志文件
匹配到最终需要采集的日志文件之后,Filebeat会对每个文件启动harvester goroutine,在该goroutine中不停的读取日志,并发送给内存缓存队列MemQueue。
在(h *Harvester) Run()方法中,我们可以看到这么一个无限循环,省略了一些逻辑的代码如下所示:
 
 
  1. for {

  2. message, err := h.reader.Next()

  3. if err != nil {

  4. switch err {

  5. case ErrFileTruncate:

  6. logp.Info("File was truncated. Begin reading file from offset 0: %s", h.state.Source)

  7. h.state.Offset = 0

  8. filesTruncated.Add(1)

  9. case ErrRemoved:

  10. logp.Info("File was removed: %s. Closing because close_removed is enabled.", h.state.Source)

  11. case ErrRenamed:

  12. logp.Info("File was renamed: %s. Closing because close_renamed is enabled.", h.state.Source)

  13. case ErrClosed:

  14. logp.Info("Reader was closed: %s. Closing.", h.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值