日志监控方案
日志对我们进行系统故障定位具有关键的作用。我们的框架代码、系统环境及业务逻辑中一般都会产出一些日志,我们通常会把这些日志记录后统一收集起来,方便在需要的时候进行查询检索。ELK是目前开源领域比较流行且成熟的一站式日志解决方案。
日志采集方案
日志采集的代理端(Agent)其实就是一个将数据从源端投递到目的端的程序。我们会使用一个具备数据订阅功能的中间件作为日志采集、分析、存储的中间管道,来实现解耦的功能。目前业界比较流行的日志采集解决方案主要有Flume、Logstash、FileBeat和Fluentd
等。
Flume
Flume是一个高可用的、高可靠的、分布式的海量日志采集、聚合和传输系统。Flume支持在日志系统中定制各类数据发送方,它可以收集数据。Flume提供对数据进行简单处理并写入各种数据接收方(如文本 、 HDFS 、 HBase 等 ) 的 能 力 。 Flume 的 核 心 是 把 数 据 从 数 据 源(Source)收集过来,再将收集的数据送到指定的目的地(Sink)。
为了保证输送过程一定成功,在送到目的地之前,会先缓存数据到管道(Channel),待数据真正到达目的地后,Flume再删除缓存的数据,整个流程如下图所示。
Flume的数据流由事件(Event)贯穿始终,事件是将传输的数据进行封装而得到的,是Flume传输数据的基本单位。如果是文本文件,则事件通常是一行记录。事件携带日志数据,并且携带头信息,这些事件由Agent外部的数据源生成,当Source捕获事件后会进行特定的格式化,然后Source会把事件推入(单个或多个)Channel中。Channel可以看作一个缓冲区,它将保存事件直到Sink处理完该事件。Sink负责持久化日志或者把事件推向另一个Source。
Logstash
Logstash是一个分布式日志收集框架,开发语言是JRuby,经常与Elasticsearch、Kibana配合使用,组成著名的ELK技术栈。
Logstash非常适合做日志数据的采集,既可以采用ELK组合使用,也可以作为日志收集软件单独出现。Logstash单独出现时可将日志存储到多种存储系统或临时中转系统,如MySQL、Redis、Kafka、HDFS、Lucene、Solr等,并不一定是Elasticsearch。
Logstash的设计非常规范,它有三个组件。因为架构比较灵活,所以如果不想用Logstash存储,也可以对接到Elasticsearch,这就是前面所说的ELK了。Logstash的采集过程如下图所示。
FileBeat
FileBeat和Logstash一样,都属于日志收集处理工具,它是基于原先Logstash的源码改造出来的。与Logstash相比,FileBeat更加轻量,占用资源更少。FileBeat涉及两个组件:探测器(Prospector)和采集器(Harvester)。FileBeat用来读取文件并将事件数据发送到指定的输出。FileBeat的工作流程是这样的:开启FileBeat时,它会启动一个或多个探测器去检测你设置的日志路径或日志文件,在定位到每一个日志文件后,FileBeat启动一个采集器。每个采集器读取一个日志文件的新内容并把数据发送到libbeat,libbeat会集合这些事件并将汇总的数据发送到你设置的外部接收程序中。下面是FileBeat的官方示意图。
Fluentd
业界一直采用ELK来管理日志,众所周知,Logstash是一个具有实时渠道能力的数据收集引擎,但和Fluentd相比,它在效能上的表现略逊一筹,故而逐渐被Fluentd取代,ELK也随之变成EFK,同时,Fluentd已经加入CNCF云原生成员。
Fluentd是一个开源的数据收集器,专为处理数据流设计,使用JSON数据格式。它采用插件式的架构(几乎所有源和目标存储都有插件),具有高可扩展性、高可用性,同时实现了高可靠的信息转发。Flueted由三部分组成,如下图所示。
● Input:负责收集数据或者主动抓取数据,支持Syslog、HTTP、File tail等。
● Buffer:负责数据获取的性能和可靠性,也有文件或内存等不同类型的Buffer可以配置。
● Output:负责输出数据到目的地,例如文件。
ELK日志的解决方案
ELK简介
ELK是软件集合Elasticsearch、Logstash、Kibana的简称,由这三个软件及其相关的组件可以打造大规模日志实时处理系统。ELK已经成为目前最为流行的集中式日志解决方案,在最简单的ELK解决方案中只有Logstash通过输入插件从多个数据源获取日志,再经过过滤插件进行数据架构处理,然后数据输出存储到Elasticsearch,在通过Kibana展示,下面是ELK最典型的架构图。
这种架构适用于简单场景,适合初学者搭建使用。而在前文的日志采集方案中,我们知道Logstash的采集存在性能瓶颈,所以在日志采集端通常会使用FileBeat作为日志采集Agent。下面简单介绍另一种ELK的日志改进解决方案——FileBeat+ELK,流程如下图所示。
● FileBeat:获取服务器上指定路径的日志文件,并将这些日志转发到Logstash实例以进行处理。设计FileBeat是为了提高可靠性和降低延迟。在微服务所在服务器上部署FileBeat,主要用来对微服务日志文件进行采集,将采集到的数据输出到指定文件或者队列服务器。
● Logstash:可以作为服务端的数据处理管道,从多个源中提取数据,对其进行转换,然后将其存储到Elasticsearch。简单来说就是日志的收集、分析、过滤工具。从文件系统或者服务器队列进行聚合、过滤,并输出到Elasticsearch存储。
● Elasticsearch:它是一个开源分布式搜索引擎,通常作为日志的存储服务器,提供收集、分析、存储数据三大功能。
Kibana:它是一个基于Web的图形界面,读取Elasticsearch上的集群数据,展示Web查询页面,提供历史日志数据查询,用于搜索、分析和展示存储在Elasticsearch中的日志数据。
ELK在微服务架构中的局限
传统的ELK解决方案的最大优势就是对日志全流程支持,从日志采集、存储到展示的所有环节都是“开箱即用”的,但是在实际微服务项目使用过程中依然存在一些问题。
● 每个服务器如果想享受ELK日志解决方案,都需要在宿主机上安装一个代理客户端,而像Logstash这样的采集器,本身比较占用内存,会存在与宿主机应用抢占资源的问题。
● ELK的日志采集机制是使用Agent从磁盘读取增量的日志文件,
但是磁盘读取会遇到资源消耗增大、日志读取缓慢等问题。
● 每部署一套新的服务,都需要部署相关的Agent,对Agent后期的升级及配置环境运维都将存在一系列问题。
Spring Boot的日志解决方案
针对上述ELK的这些问题,我们有必要在以Spring Boot框架为主的微服务应用系统中,采用更加高效、简易、轻量级的日志解决方案。
我们知道,Spring Boot采用了LogBack作为默认的文件系统,而LogBack有非常强大的Appender机制,可以将日志动态输出到指定的Appender上,这样在日志采集时就不需要为每一个微服务都安装一个Agent了,同时日志不需要再落盘就可以通过第三方消息中间件将日志异步转发出去,避免了为每一台宿主机都安装LogAgent采集进程;同时可以使用定制化的LoggerAppender,通过Nexus私服发布更新,应用系统可以在编译期完成日志LogAppender的升级,避免了为每一套应用程序修改升级Agent代码。同时对于Logger append的Sink写入端可以选择Kafka或者Redis这样高性能的中间件,作为高并发日志系统的缓存,避免对ELK服务稳定性造成冲击。下面是基于Spring Boot采集日志、归集日志、存储日志的改良的解决方案架构图。
自定义Appender的配置加载
下面是在Logback.XML中自定义的Appender实现,使用了异步的Appender。这样,服务在调用LogBack打印日志时,不会阻塞当前应用代码继续执行正常逻辑。
自定义Appender的实现
根据上面Logback.XML的配置,我们配置了一个Kafka类型的Sink输出Appender:MqAppender,具体的代码实现如下。
在这个自定义Appender类中,有两个核心方法:init方法和append方法。init方法的主要作用是完成当前应用需要的资源初始化工作;append方法则是日志拦截方法,BaseMqProducer类为初始化时构造的Kafka-Producer客户端对象,会调用send方法将构造好的日志消息发送到Kafka中间件。需要注意的是,在append方法中,需要过滤掉Kafka自身的日志输出,以免形成死循环。因为篇幅所限,我们仅就Append思路进行分享,具体Kafka的BaseMqProducer实现方法就不再赘述。
日志消费服务
日志消费服务的主要作用是将Kafka收集的日志根据Topic和日志消息负载信息(PayLoad)分发到不同的Elasticsearch的Index中。下面是日志消费服务对Kafka客户端的自动配置代码。
下 面 是 Kafka 的 日 志 消 费 代 码 , 利 用 @KafkaListener 注 解 对TopicPattern下的日志做数据消费,MqHandler可以做消息过滤、预警、聚合、数据加工等工作,最终将消息发送给Elasticsearch存储引擎。
日志存储
对 于 日 志 存 储 , Spring Boot 2.2.0 已 经 兼 容 Elasticsearch7.x,可以直接引入Elasticsearch的Maven依赖。日志的实体类定义代码如下所示。
下面是持久化实现逻辑。
日 志 展 示 需 要 启 动 Kibana , 默 认 地 址 是 http://127.0.0.1 :5601。浏览Kibana界面,Kibana会自动检测Elasticsearch中是否存在该索引名称,通过Filter搜索框可以检索日志,如下图所示。