最佳实践:【解构云原生】基于Filebeat的日志采集服务设计与实践

优采云 发布时间: 2020-08-30 15:44

  【解构云原生】基于Filebeat的日志采集服务设计与实践

  本文由作者授权发布,未经许可,请勿转载。

  作者:傅轶,网易杭州研究院云计算技术部中级研制工程师

  一、背景云原生技术大潮早已将至,技术变迁迫在眉睫。

  在这股技术时尚之中,网易推出了轻舟微服务平台,集成了微服务、Service Mesh、容器云、DevOps等组件,已经广泛应用于公司内部,同时也支撑了好多外部顾客的云原生化整修和迁移。

  

  在这其中,日志是平常很容易被人忽略的一部分,却是微服务、DevOps的重要一环。没有日志,服务问题排查无从谈起,同时日志的统一采集也是好多业务数据剖析、处理、审计的基础。

  但是在云原生容器化环境下,日志的采集又显得有点不同。

  二、容器日志采集的疼点传统主机模式对于传统的物理机或则虚拟机布署的服务,日志采集工作清晰明了。

  业务日志直接输出到宿主机上,服务运行在固定的节点上,手动或则拿自动化工具把日志采集agent布署在节点上,加一下agent的配置,就可以开始采集日志了。同时为了便捷后续的日志配置更改,还可以引入一个配置中心,用来下发agent配置。

  Kubernetes环境而在Kubernetes环境中,情况就没那么简单了。

  一个Kubernetes node节点上有好多不同服务的容器在运行,容器的日志储存方法有好多不同的类型,例如stdout、hostPath、emptyDir、pv等。由于在Kubernetes集群中常常存在Pod主动或则被动的迁移,频繁的销毁、创建,我们没法象传统的方法一样人为给每位服务下发日志采集配置。另外,由于日志数据采集后会被集中储存,所以查询日志时,可以按照Namespace、Pod、Container、Node,甚至包括容器的环境变量、Label等维度来检索、过滤很重要。

  以上都是有别于传统日志采集配置方法的需求和疼点,究其原因,还是由于传统的方法并非针对Kubernetes设计,无法感知Kubernetes,更难以和Kubernetes集成。

  经过近来几年的迅速发展,Kubernetes早已不仅仅是容器编排的事实标准,甚至可以被觉得是新一代的分布式操作系统。在这个新型的操作系统中,controller的设计思路驱动了整个系统的运行。controller的具象解释如下图所示:

  

  由于Kubernetes良好的可扩展性,Kubernetes设计了一种自定义资源CRD的概念,用户可以自己定义各类资源,并利用一些framework开发controller,使用controller将我们的期望弄成现实。

  基于这个思路,对于日志采集来说,一个服务须要采集哪些日志,需要什么样的日志配置,是用户的期望,而这一切,就须要我们开发一个日志采集的controller去实现。

  

  三、探索与构架设计有了前面的解决思路,除了开发一个controller,剩下的就是围绕着这个思路的一些选型剖析。

  日志采集agent选型日志采集controller只负责对接Kubernetes,生成采集配置,并不负责真正的日志采集。目前市面上的日志采集agent有很多,例如传统ELK技术栈的Logstash,CNCF已结业项目Fluentd,最近推出不久的Loki,还有beats系列的Filebeat。 下面简单剖析一下。

  1.Logstash基于JVM,分分钟显存占用达到几百MB甚至上GB,有点重,首先被我们排除。

  2.Fluentd背靠CNCF看着不错,各种插件也多,不过基于Ruby和C编撰,对于轻舟团队的技术栈来说,还是使人止于观望。虽然Fluentd还推出了纯粹基于C语言的Fluentd-bit项目,内存占用太小,看着非常诱惑,但是使用C语言和不能动态reload配置,还是难以令人亲近。

  3.Loki推出的时间不久,目前功能有限,而且一些压测数据表明性能不太好,暂时观望。

  4.Filebeat和Logstash、Kibana、Elasticsearch同属Elastic公司,轻量级日志采集agent,推出就是为了替换Logstash,基于Golang编撰,和轻舟团队技术栈完美契合,实测出来性能、资源占用率各方面都比较优秀,于是成为了轻舟日志采集agent第一选择。

  agent集成方法对于日志采集agent,在Kubernetes环境下通常有两种布署形式。

  1.一种为sidecar的形式,即和业务Container布署在同一个Pod里,这种方法下,Filebeat只采集该业务Container的日志,也只需配置该Container的日志配置,简单、隔离性好,但最大的问题是, 每个服务都要有一个Filebeat去采集,而一个节点上一般有很多的Pod,内存等开支加上去不容豁达。

  2.另外一种也是最常见的每位Node上布署一个Filebeat容器,相比而言,内存占用要小好多,而且对Pod无侵入性,比较符合我们的常规使用方法。同时通常使用Kubernetes的DaemonSet布署,无需传统的类似Ansible等自动化运维工具,部署运维效率大大提高。所以我们优先使用Daemonset布署Filebeat的形式。

  整体构架选择Filebeat作为日志采集agent,集成了自研的日志controller后,从节点的视角,我们看见的构架如下所示:

  

  1.日志平台下发具体的CRD实例到Kubernetes集群中,日志controller Ripple则负责从Kubernetes中List&Watch Pod和CRD实例。

  2.通过Ripple的过滤、聚合最终生成一个Filebeat的input配置文件,配置文件里描述了服务的采集Path路径、多行日志匹配等配置,同时就会默认把诸如PodName、Hostname等配置到日志元信息中。

  3.Filebeat则按照Ripple生成的配置,自动reload并采集节点上的日志,发送至Kafka或则Elasticsearch等。

  由于Ripple*敏*感*词*了Kubernetes风波,可以感知到Pod的生命周期,不管Pod销毁还是调度到任意的节点,依然才能手动生成相应的Filebeat配置,无需人工干预。

  Ripple能感知到Pod挂载的日志Volume,不管是docker Stdout的日志,还是使用HostPath、EmptyDir、Pv储存日志,均可以生成节点上的日志路径,告知Filebeat去采集。

  Ripple可以同时获取CRD和Pod的信息,所以不仅默认给日志配置加上PodName等元信息外,还可以结合容器环境变量、Pod label、Pod Annotation等给日志打标,方便后续日志的过滤、检索查询。

  除此之外,我们还给Ripple加入了日志定时清除,确保日志不遗失等功能,进一步提高了日志采集的功能和稳定性。

  四、基于Filebeat的实践功能扩充通常情况下Filebeat可满足大部分的日志采集需求,但是依然防止不了一些特殊的场景须要我们对Filebeat进行多样化开发,当然Filebeat本身的设计也提供了良好的扩展性。

  Filebeat目前只提供了象Elasticsearch、Kafka、Logstash等几类output客户端,如果我们想要Filebeat直接发送至其他前端,需要多样化开发自己的output。同样,如果须要对日志做过滤处理或则降低元信息,也可以自制processor插件。

  无论是降低output还是写processor,Filebeat提供的大体思路基本相同。一般来讲有3种形式:

  1.直接fork Filebeat,在现有的源码上开发。output或则processor都提供了类似Run、Stop等的插口,只须要实现该类插口,然后在init方式中注册相应的插件初始化方式即可。当然,由于Golang中init方式是在import包时才被调用,所以须要在初始化Filebeat的代码中自动import。

  2.复制一份Filebeat的main.go,import我们自研的插件库,然后重新编译。本质上和形式1区别不大。

  3.Filebeat还提供了基于Golang plugin的插件机制,需要把自研的插件编译成.so共享链接库,然后在Filebeat启动参数中通过-plugin指定库所在路径。不过实际上一方面Golang plugin还不够成熟稳定,另一方面自研的插件仍然须要依赖相同版本的libbeat库,而且还须要相同的Golang版本编译,坑可能更多,不太推荐。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线