■ 河南 许红军
编者按:ELK由Elasticsearch、Logstash和Kibana等部分组件组成,可以将各种日志进行过滤和集中化存放,便于用户对其进行实时检索和分析。Elasticsearch是实时的分布式搜索和分析引擎,支持群集和分片功能。Logstash是轻量级的开源的日志收集处理框架,Kibana是开源的数据分析可视化平台,可以为对得到的日志数据进行搜索、汇总和多维度分析。
对于实际的ELK群集来说,其实际上是分层部署的,大体分为数据采集层、消息队列层、数据分析层、数据持久化存储层和数据查询显示层。在数据采集层部署的是Web服务器,在其上安装了Filebeat组件,用来收集Web日志信息。使用Filebeat的优点在于降低了收集日志对业务系统资源的消耗。这些Filebeat组件实际上是作为第一级的Logstash使用,其将收集到的日志信息发送给Kafka消息队列。
在消息队列层部署了Kafka和Zookeeper群集,用来管理消息队列,这可以保证ELK收集日志数据的安全性和稳定性,降低了网络闭塞和丢失数据的可能。在数据分析层部署了Logstash服务器,从消息队列层实时拉取原始日志,按照预设规则对其进行分析和过滤,之后将其发送给Elasticsearch集群。
在数据持久化层部署Elasticsearch集群,用来对接收的日志进行结构化处理和存储操作,即在接收到logstash发送的数据后,执行写磁盘、建索引库等操作。因为Elasticsearch要存储和索引大量数据,将其配置成群集模式,从整体上提高了ELK的高效性、扩展性和吞吐量能力。在数据查询显示层上部署了Kibana服务器,根据Elasticsearch集群存储的日志信息,提供可视化数据查询和展示服务。
使用Filebeat这一文本日志收集器,可以从目标服务器上收集各种日志信息,并将其发送给Logsta、Elasticsearch、Kafka等对象。Filebeat具有易于使用,消耗资源较少以及功能实用等特点。Filebeat主要由Prospector(探测器)和Harvester(收集器)组成。Filebeat作为日志采集工具,部署到生产服务器上,如果对其配置不当,就会造成其占用过多的系统资源,影响生产服务器的正常运行。
因为在日志量很大或者日志突发异常等情况下,Filebeat必然会占用大量的系统资源。例如,其当单条日志过大时,Filebeat可能会占用几百兆甚至十几GB的内存。Filebeat提供了内存模式和文件缓存模式两种内存限制方法,可以根据实际情况进行选择。一般来说,使用内存模式即可。进入“/opt/app/filebeat”目录,打开其中的“filebeat.yml”文件,可以修改其配置信息,这里假设Filebeat安装在“/opt/app/filebeat”目录中。
对于内存模式来说,所有事件(Events)都是保存在内存中的,该模式只能限制事件数,无法限制最大使用内存,这意味着可能会因为日志长度导致内存使用大为增加。在配置文件中的“queue.mem:”栏中的“events:4096”行中可以修改队列可以存储的事件数量。默认为4096个事件。
在“flush.min_ events:512”栏中可以修改发布所需的最小事件数量,默认值为0,表示无需额外的等待时间就可以直接输出发布事件。这里设置为“512”,表示必须满足指定的事件数量后才能输出发布事件。
在“flush.timeout:5s”行中可以设置最早的可用事件在队列中等待的最长时间,默认为0 s,这里设置为5 s。超过这个时间立即输出发布事件。对于文件缓存模式来说,可以限制最大使用内存量。在配置文件中的“queue.spool:”小节的“file:”栏下的中“path:"${path.data}/spool.dat"”栏中可以设置Spool file的保存位置,在“size:512MiB”栏中可以修改缓冲区大小。在“page_size:16KiB”栏中可以设置文件的页面大小,默认为4 KiB。在“write:”栏下的“buffer_size:10MiB”栏中可以设置写缓冲区大小,如果超过该值就刷新写缓冲区。在“flush.timeout:5s”栏中可以设置写缓冲区中最早事件的最长等待时间,默认为0 s,意味着write.flush.events或write.buffer_size满足时写入缓冲区且仅刷新一次。
在“flush.events:1024”栏中可以修改缓冲事件的数量,如果超过该值就刷新写缓冲区。上述配置的含义是将所有的事件存储到磁盘的缓冲区中,如果写入10 MiB内容或达到1024个事件,则刷新写入缓冲区。如果最早的可用事件在写缓冲区中等待5 s,也会刷新写入缓冲区。在“max_procs:4”栏中可以设置Filebeat可以使用的CPU最大核心数量,这里为4个CPU核心。
当Filebeat在收集日志时,会占用文件系统的大量文件句柄。这很可能导致日志日志收集异常故障,因此必须对Filebeat的日志收集策略进行优化处理。在配置文件中的“close_inactive:1m”行可以修改采集不到新日志后,多长时间关闭文件句柄,默认5 min,这里将其设置为1 min,来加快文件句柄关闭操作。
在“close_timeout:3h”栏中可以修改在多长时间没有传输完成的话,就强行关闭文件句柄,可以解决文件句柄耗尽的问题,这里设置为3 h。注意,这可能存在丢失数据的风险。在“clean_inactive:72h”栏中可以设置经过多长时间,才清理存储在registry文件中的文件描述信息,采集过的日志文件会在registry保存描述信息。默认值为0表示不清理,这会导致registry体积过大影响性能。启用了该项后必须启用“ignore_older:70h”项,而且要保证前者的值要大于后者。
Filebeat将日志实时的传输到kafka集群中,kafka消息队列可以对其进行缓冲和存储,即Filebeat作为生产者将日志推送到kafka集群。Kafka是高吞吐量的分布式发布/订阅消息系统,能够在生产者与消费者之间建立通信的桥梁,其实质上是解决了在不同系统中如何传递消息的问题。
Kafka提供了持久化、高性能等特征,具有磁盘连续读写性能远远高于随机读写的特点,可以将将一个Topic拆分多个Partition来提高并发和吞吐量。
对于Linux来说,针对文件系统设置了Page Cache,将文件读写的数据缓存起来。执行“free”命令,会显示Page Cache的容量。当读取文件时,会先在Page Cache中查找,如果没有找到才会从磁盘读取文件。当写文件时,系统先将数据写入Page Cache中,并将该页打上Dirty标志,并会定期批量将Page Cache的数据保存到文件系统中。对于Kafka来说,非常依赖底层系统提供的Page Cache功能。
即Kafka先将数据写到PageCache的,如果消费者一直在消费,而且速度大于等于kafka的生产者发送数据的速度,那么消费者就会一直从Page Cache读取数据,读写操作都在内存中完成的,并不涉及到磁盘访问,所以Kafka具有非常高效的运作能力。因此,对内存进行优化,对于保证Kafka的运行就很重要了。对于Page Cache进行优化,主要包含“vm.dirty_background_ratio”和“vm.dirty_ratio”参数。
执行“vim/etc/sysctl.conf”命令,在该文件中添加“vm.dirty_background_ratio=5”,“vm.dirty_ratio=10”行。
前者指定了当文件系统缓存脏页数量达到系统内存百分之多少时(默认10 %)就会触发pdflush等后台回写进程运行,将一定缓存的脏页异步地刷入磁盘。
后者指定了当文件系统缓存脏页数量达到系统内存百分之多少时(默认20 %),系统必须开始处理缓存脏页,因为此时脏页数量已经比较多,为了避免数据丢失需要将一定脏页刷入磁盘。因为Kafka需要的堆内存比较小,所以可以将60 %以上的物理内存给系统使用,便于分配Page Cache。
对于Kafka来说,读写的单位是Partition分区,将一个Topic拆分为多个Partition可以有效提高系统的吞吐量,但是不同的partition要分布在不同在磁盘上。否则将破坏磁盘读写的连续性。
进入“/opt/app/Kafka/config”目录,打开“server.properties”文件,可以对Kafka的配置进行调整。
例如,在“log.dirs”参数中设置Kafka保存数据的目录。例如,将其修改为“log.dirs=/xxx/logs,/yyy/logs,/zzz/logs”,将磁盘的多个目录配置到Broker主机的不同位置。其中的“xxx”等表示具体的磁盘名称。
这样,当Kafka新建Par tition时,就会将其分布在Partition最少的目录上。在“num.network.threads”行中可以设置Broker处理消息的最大线程数,在“num.io.threads”栏中可以设置Broker处理磁盘IO的线程数。在优化时可以将前者设置为CPU的数量加上1,对于后者可以设置为CPU数量的2到3倍。在“log.retention.hours=72”栏中可以设置日志保留时间,例如将其设置为3天,避免大量日志消耗磁盘空间。在“log.segment.bytes=2147483648”栏中可以修改段文件大小,这有利于快速回收磁盘空间,这里设置为2 GB。
在“log.flush.interval.messages=10000”栏中可以修改当Producer写入多少条消息时,刷数据到磁盘,这里设置为10000。在“log.flush.interval.ms=1000”栏中设置每隔多长时间刷数据到磁盘,这里为1 s。
在“num.replica.fetchers”栏中设置拉取线程数,将其增加可以提高follower的I/O并发度,单位时间内leader持有更多请求。在“replica.fetch.min.bytes”栏中设置拉取最小字节数。在“replica.fetch.max.bytes”栏中设置拉取最大字节数,前者设置为1,后者设置为5比较合适。在“replica.fetch.wait.max.ms”栏中最大等待时间,表示follow拉取的频率,如果拉取频率过高会导致CPU占用率过高。
当Filebeat将数据推送到kafka后,kafka不会主动将数据推送到logstash,Logstash必须主动从kafka集群拉取日志信息,因此Logstash相当于Kafka的消费者。
对于数据收集层来说,部署了Logstash服务器,其主要实现日志的收集和分析工作,主要实现数据的接收、过滤分析和转换操作、并将其输出到指定的位置。
进入“/opt/app/logsta sh/”目录,在其中打来名为“logstash.yml”的配置文件,在“pipeline.workers”栏中设置控制Output或Filter插件的工作线程数。
因为Logstash的GROK正则解析非常消耗计算资源,所以增加Filter插件的工作线程数,可以有效以提高性能。例如,将该线程数设置大于或者等于CPU的核心数。在“pipeline.batch.size”栏中设置批量执行Event的最大值,适当增加该值,可以在一定程序上提高其性能。
当然,这需要消耗一定的内存资源。在“pipeline.batch.delay”栏中可以设置批量处理事件的最大等待值,Input插件需要按照批量执行事件最大值,来将数据发送到消息队列,因此需要为其设置最大超时时间。
打开Logstash配置目录中的“jvm.options”文件,在其中的“-Xms1g”,“-Xmx1g”中显示JVM堆内存的最大值和最小值均为1 GB。这是默认的配置,可能无法满足实际的需要。
因此,一般将其设置为实际物理内存的一般即可,两者大小要保持一致。当然,如果堆内存设置的太大,会对系统正常运行造成不利影响,如果设置的太小,也会造成其性能降低。例如,将其设置为“-Xms16g”,“-Xmx16g”,建议最大不要超过32 GB,对于JVM堆内存的调试需要依次添加,最终找到最合适的内存量为止。
对于Elasticsearch来说,其本身作为Java应用,需要使用到JVM堆内存,对其进行优化调整,可以保证Elasticsearch顺畅运作。在Java中堆被划分成两个新生代和老年代区域,这样便于JVM更好的管理堆内存中的对象。JVM堆内存的重要作用就是创建对象实例,所有的对象实例和数组都要在堆上分配。堆是由垃圾回收GC机制负责,垃圾回收采用分代算法,JVM堆可以动态地分配内存大小,GC会自动回收不再使用的数据。
进入Elasticsearch安装路径下的“config”目录,打开其中的“jvm.options”文件,显示其默认的JVM堆内存最大值和最小值均为1GB,这样的配置肯定不符合实际要求,可以根据实际情况进行调整。
注意,需要将两者的值设置为相同,可以避免无谓的性能消耗。一般来说,将堆内存设置为物理内存的一般即可,不要超过32 GB。
为了保证Elasticsearch的性能,最好禁用系统的SWAP交换分区的使用,执行“swapoff -a”命令禁用该功能。
执行“cat/proc/sys/vm/swappiness”命令,可以查看swappiness参数值,该值控制操作系统尝试交换内存的积极性。如果其值为0,表示最大限度使用物理内存,如果为100表示积极的使用Swap交换分区。系统默认配置为60,表示当内存使用到40%的时候,开始出现有交换分区。可以执行“sysctl vm.swappiness=10”命令,降低该参数的值,让系统尽可能的使用使用内存。