在生产环境中,我们为了更好的服务于业务,通常会通过优化的手段来实现服务对外的性能最大化,节省系统性能开支;关注我的朋友们都知道,前段时间一直在搞ELK,同时也记录在了个人的博客篇章中,从部署到各个服务应用的采集都做了详细的介绍,但是并没有关于ELK方面的优化,那么,我们对于这些日志分析平台,我们如何去优化呢?优化的手段又有哪些呢?下面请听我娓娓道来~
【ES优化】
ES在前面的部署环节(https://www.cnblogs.com/bixiaoyu/p/9460554.html)已经简单了提到调优,但是不全;Elasticsearch作为数据持久化存储环节,主要就是接受采集端发送过来的数据,执行写磁盘,建立索引库,最后将结构化的数据存储到ES集群上,这是ES所需要完成的工作
1.1:JVM内存的优化
首先我们需要了解什么是jvm内存?作用是什么?
jvm内存其实就是java内存堆,也是jvm需要管理的最大的一块内存空间,主要就是存放各种类型的实例对象;在java中,堆的概念被划分为,新生代和老年代,这样更有利于jvm管理内存堆中的对象,分配和回收
我们设置堆内存主要就是创建实例对象,让所有对象实例和数据都在堆上进程分配,可以动态的分配内存大小;
-Xms1g #设置堆最小的内存
-Xmx1g #设置堆最大的内存
如何设置最合理呢?
首先我们要知道堆内存设置的越大,ES可用的堆就越大,同时呢,可用的缓存空间就越大,但是不能无限大,因为这样会浪费大量的内存,太多的堆内存可能会系统垃圾回收机制异常;
优化准则:
将最小堆(xms)和最大堆(xmx)设置为相同值即可,这样可以防止内存堆运行的有所变动;
内存堆的值不要超过系统物理内存的50%(可以等于实际物理内存的一半),以确保有足够的物理内存给内核文件系统使用;
ES堆内存大小为什么不能超过物理 内存的50%?
除了堆内存设置过大会造成资源浪费之后,还有一个原因,
堆内存对于ES来说是个不可缺少的部分,能够对提高数据的执行效率,还有一个内存使用者,那就是是-lucene
Lucene是一个开源的全文检索引擎工具 ,而我们的ES底层是基于Lucene来实现的丰富的检索功能;Lucene的性能依赖于操作系统之间的交互,如何说我们把可用的内存都给了ES的话,那么Lucene还有剩余的内存空间吗?这将会严重的影响性能;因此,我们最多只能将50%的可用内存资源分配给ES堆内存,剩下的50%留给Lucene了
ps:这里注意一下,我们的Luceen使用的是物理内存剩余的50%,它并不使用堆内存;切记不要与ES堆内存混淆
1.2:ES所在操作系统的内存优化
可通过禁用swap·分区,如果是混合服务器的话可通过减低swap分区的使用积极性;
/dev/mapper/centos-swap swap swap defaults 0 0 #进入/etc/fstab/.将其注释,永久生效;临时生效直接swapoff -a即可
降低swao分区使用积极性,这句话是什么意思呢?首先我们要知道,系统的内存使用空间到达一定的阀值时候,便会占用swap空间,这个时候我们是可以控制这个阀值的;swappiness=0表示最大限度使用物理内存,也就是说,当物理内存使用100%之后,才去使用swap交换分区;
如何设置呢?
比如说,我们现在需要设置系统内存大小阀值,当物理内存使用90%的时候,只剩10%的物理内存,再去使用swap空间
100-10=90%
# vim /etc/sysctl.conf
vm.swappiness = 10
#修改之后执行sysctl -p生效
#cat /proc/sys/vm/swappiness
10
1.3:·硬件优化(硬盘类型/raid类型)
服务器硬盘选用SSD硬盘,配置成raid 0阵列以获得更佳的IO性能;
【Logstash优化】
logstash.yml配置优化:
1)pipline.workers:控制output或filter插件的工作线程数(只能设置为正整数),因为logstash中的grok正则及其消耗系统计算字眼,同时filte也会存在瓶颈,此时增加工作线程,以提高性能
2)pipeline.batch.size:批量执行event的最大值,该值用于input批量处理事件值,再打包发送给filter和output.可以提高性能,但是会增加额外的内存开销
3)pipeline.batch.delay:批量处理事件的最大等待值(input需要按照batch处理的最大发送到消息队列,需要设置一个超时事件)
Logstash同样运行在JVM内存中,关于jvm内存的配置原则不在述说和,和上述ES一样;
堆内存一般要求初始值和最大值设置一致,防止动态调整堆内存大小的消耗;jvm内存的分配设置太大会拖慢系统,浪费资源,设置太小的话Logstash无法启动
【Kafka的性能优化】
既然我们在ELK中用到了Kafka,那么优化也是必须的,先来回顾一下,kafka是一个高吞吐分布式消息系统,并且提供了持久化,高性能主要表现在以下两点:
第一,磁盘的连续读写性能远远高于随机读写
第二:拆分一个topic主题分配多个partition分区,这样可以提供并发和吞吐量;
另外,我们的kafka消息读写为什么这么高效?原因何在?
我们要知道linux系统内核为文件设置一个缓存机制,所有对文件读写的数据内容都会存在着缓存中,称之为:page cache(页缓存)
缓存 机制:
当一个文件发生读操作时,系统会先去page cache页缓存中读取,如果找到,便会直接返回,没有缓存中没有需要读取的数据内容,那么会去磁盘中读取,此时系统写入一份到缓存中。,最终返回数据;
当有写操作时,亦是如此,数据会首先写入缓存并进行标识,等待批量保存到文件系统,减少了磁盘的操作次数和系统额外开销
我们的kafka就是依赖于这种机制,数据的读写交互便是在缓存中完成接力,不会因为kafka写入磁盘数据影响吞吐量,这就是为什么kafka非常高效的根本原因
降低文件系统页面缓存
主要针对于下面两个参数
vm.dirty_background_ratio: #指定了当文件系统缓存页数量达到系统内存的百分比阀值的时候,便会触发pdflush/flush/kdmflush后台运行写进程,将一定的缓存数据写入磁盘中
vm.dirty_ratio: #指定了当文件系统缓存页熟练达到系统设定的百分比阀值时候,为了保证避免数据丢失,系统不得不开始处理缓存页面,在这个过程中,可能很多应用会因为系统刷新内存数据,导致应用IO进程阻塞;这个时候呢,系统就会转入同时处理页缓存和堵塞应用
ps:建议将vm.dirty_background_ratio设置为5%,vm.diry_ratio设置为10%;根据不同环境,需要进行测试而定
topic的拆分:
kafka读写单位是partition,将一个topic分配到多个partition可以提高系统的吞吐量,但前提是将不同的partition分配到不同的磁盘上,如果多个partition位于一个磁盘上就会出现多个进程同时对磁盘上多个文件进行读写,这样造成了磁盘的频繁调度,破坏了磁盘读写的连续性
如何实现将不同的partition分配到不同的磁盘上呢?
我们可以将磁盘上的多个目录配置到broker的log.dirs上
# vim /usr/local/kafka/config/server.properties
log.dirs=/disk1/logs,/disk2/logs/,/disk3/logs #kafaka在新建partition时,会将partition分布在paritition最少的目录上面,因此,不能将同一个磁盘上的多个目录设置到logs.dirs上
kafka配置参数优化:
num.network.threads=3 #broker处理消息的最大线程数
num.io.threads=8 #broker处理磁盘IO的线程数
一般num.network.threads主要就是处理网络IO,读写缓冲区数据,基本没有IO等待,配置线程数量为CPU核数n+1
num.io.threads主要进行磁盘IO操作,高峰期可以能有些等待,因此配置较大一点,配置线程数量为CPU核数的2~3倍即可
日志保留策略优化:
kafka被打量的写入日志消息后,会生成打量的数据文件,也就是日志消息,这样会占用大量的磁盘空间。
减少日志保留时间,通过log.retention.hours设置,单位是小时
log.retention.hours=72 #保留日志数据的时间范围,过后便会删除
段文件大小优化
段文件配置大小为1GB,这样有利于快速的回收磁盘空间,重启kafka加载也会更快,如果说文件过小,那么文件数量就会较多,kafka启动的时候回单线扫描(log.dir)下的所有文件,文件较多启动较慢,会影响性能,
log.segment.bytes=1073741824 #段文件最大大小,超过该阀值,会自动创建新的日志段
Logs数据文件写盘策略优化
为了大幅度提高producer写入吞吐量,需要制定定期批量写入文件磁盘的计划
每当producer写入10000条消息事,便会将数据写入磁盘,
#log.flush.interval.messages=10000 #强行将数据刷新到磁盘之前所能接受的消息数
#log.flush.interval.ms=1000 #在强制刷新之前,消息可以停留在日志中最长的时间(单位毫秒,每间隔1秒时间,刷数据到磁盘中)
【Filebeat优化】
还记得我们为什么要使用filebeat采集日志数据吗?那是因为Logstash功能虽然强大,但是它依赖于java,在海量日志环境中,logstash进程会消耗更多的系统资源,这将严重的影响业务系统的性能,而我们说的filebeat是基于go语言,没有任何依赖,配置简单,占用系统资源少,比logstash更加的轻量级;但是有点还是需要注意。在日志量比较大的情况下或者日志异常突发时,filebeat也会占用大量的系统内存开销,所以说这方面的优化,也是至关重要的
内存优化,Filebeat内存收到两种模式的限制,一种是内存模式,第二种是文件缓存模式,任选其一即可
queue.mem: events: 4096 #表示队列可以存储的事件数量。默认值是4096个事件。 flush.min_events: 512 #发布所需的最小事件数量。 默认值是0,表示可以直接输出发布事件,而无需额外的等待时间。 如果设置为非0,必须等待,在满足指定的事件数量后才能输出发布事件。 flush.timeout: 5s #表示最早的可用事件在队列中等待的最长时间,超过这个时间,立即输出发布事件,默认值是0s,表示立即可以输出发布事件
配置含义:该队列能够存储4096个事件数量,如果超过512个可用的事件则在队列中等待5秒之后,将事件转发至output输出
文件缓存模式调优
此模式可以限制最大的使用内存
ueue.spool: file: path: "${path.data}/spool.dat" #Spool file的路径 size: 512MiB #Spool file的大小,也就是缓冲区的大小。 page_size: 16KiB #文件的页面大小。默认值为4096(4KiB)。 write: buffer_size: 10MiB #写缓冲区大小。一旦超过缓冲区大小,就刷新写缓冲区。 flush.timeout: 5s #写缓冲区中最旧事件的最长等待时间。如果设置为0,则在write.flush.events或write.buffer_size满足时写入缓冲区仅刷新一次。 flush.events: 1024 #缓冲事件的数量。一旦达到上限,就刷新写缓冲区。
文件系统资源的优化:
fliebeat对日志的采集有一个弊端,那就是只要发现日志就会坚持把日志收集完,否则的话就会永久锁住文件句柄不放手,就算日志文件被删除,也不会放手,这就导致了文件系统大量的文件句柄被filebeat占用,导致收集日志异常,故此对其进行优化
1)close_inactive:1m #表示没有新日志采集后,多长时间关闭文件句柄;(也就是说无数据采集时候,等待多长时间便会自动关闭文件句柄),这里设置1分钟
2)close_timeout:3h #限定的数据传输时间,这里是指传输了三小时就强行关闭文件句柄,该配置解决了文件句柄耗尽的问题,但也存在着数据丢失的风险,需要综合考虑
3)clean_inactive:72h #表示多久会清理一次文件描述符在registry文件,默认值0表示不清理,如果不清理,registry会变大,带来性能问题
4)ignore_older:70h #设置了clean_inactive,就需要设置ignore_older,并且保证该值小于clean_inactive
[小结]
关于ELK综合方面的优化,也就介绍这么多了,其实ELK的优化方面很少,我个人觉得已经足够了,主要就是针对不同的环境和业务需求进行调参,调整适合自己的才是最好的,当然前提是你要知参数的各个含义;优化也是一个综合的技术,
无论什么服务,我们能做到的优化点无非就是硬件,系统以及服务配置的调参;逐步测试,一步步达到最优的状态;这是进行优化的基本策略和思路(ps:本章可能还有很多优化策略没有写到,欢迎大佬填坑补充~)