Files
baim0-learn/elk/15.ELK应用架构性能调优经验分享.md
2024-03-09 13:38:35 +08:00

317 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 15.ELK应用架构性能调优经验分享
# 一、Elasticsearch的优化
## 1.1、JVM内存的优化
**1、堆内存如何配置?**
首先,作为一个JAVA应用,就脱离不开JVM和GC。很多人上手ES的时候,对GC一点概念都没有,然后及直接去网上抄各种JVM“优化”参数,但是效果却不理想,甚至搞出内存溢出这样的问题。可见了解JVM GC的概念和基本工作机制是很有必要的。下面先做一些简单的JVM介绍。
Java中的堆是JVM所管理的最大的一块内存空间,主要用于存放各种类的实例对象。在Java中,堆被划分成两个不同的区域: 新生代 ( Young )、老年代 ( Old )。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
设置堆内存的唯一目的就是创建对象实例,所有的对象实例和数组都要在堆上分配。堆是由垃圾回收(Garbage Collect)来负责的,因此也叫做“GC堆”,垃圾回收采用分代算法,堆由此分为新生代和老年代。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾回收器会自动回收这些不再使用的数据。
了解了堆内存的概念和作用后,下面来说下Elasticsearch 的JVM设置堆内存的方法。
默认情况下,Elasticsearch JVM使用堆内存最小和最大值为1g,可以在Elasticsearch的配置目录下jvm.options文件中找到如下内容:
```plain
-Xms1g
-Xmx1g
```
其中,Xms是设置堆最小内存,Xmx是设置堆最大内存,很明显,默认的这个值太小,我们生产系统环境中必须要修改这个值,修改方法很简单,直接修改jvm.options文件中这两个值即可,例如修改为16g,可以这么写:
```plain
-Xms16g
-Xmx16g
```
然后再次重启Elasticsearch,配置才能生效。
那么问题来了,这个最小堆大小(Xms)和最大堆大小(Xmx)设置多少合适呢,继续往下看。
**2、堆内存的决定因素与配置建议**
堆内存值的设置取决于服务器上可用的无论内存大小。
原则上来说,堆内存设置的越大,Elasticsearch可用的堆就越多,可用于缓存的内存就越多,但不能无限大,太多的堆内存可能会使垃圾回收机制暂停。并且还会浪费大量内存。
> 根据经验,将最小堆大小(Xms)和最大堆大小(Xmx)设置为相同值, 可以防止堆在运行时调整大小,因为这是一个非常消耗性能的过程。
那么设置堆内存多大合适呢?
> 一个经验值是:不超过物理内存的50%,以确保有足够的物理内存留给内核文件系统缓存。最大不超过32GB。
为什么呢,继续往下看。
**3、堆内存为什么不能超过物理机内存的一半**
堆内存对于Elasticsearch来说非常重要,它可以对数据提供快速操作。但是,还有另外一个非常重要的内存使用者:Lucene。
> Lucene是一个开源的全文检索引擎工具包,而elasticsearch底层是基于lucene的,并对其进行了扩展,提供了比Lucene更为丰富的查询语言,以及简单易用的restful api接口、java api接口等,一句话概括就是: Elasticsearch是Lucene面向企业搜索应用的扩展,可极大的缩短研发周期。
Lucene设计的目的是把底层操作系统数据缓存到内存中。而Lucene段(segment)存储在单个文件中。这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些热段(hot segments)保留在内存中,以便更快的访问。这些热段包括倒排索引(用于全文搜索)和文档值(用于聚合)。
由此可知,Lucene的性能依赖于与和操作系统的这种交互。如果把所有可用的内存都给了Elasticsearch的堆,那么Lucene就不会有任何剩余的内存。这将严重影响性能。
> 因此,我们说:建议将可用内存的50%提供给Elasticsearch堆,而将其它50%空闲。请放心,它不会被闲置,因为Lucene正在等待使用这50%的内存资源。
如果不在字符串字段上做聚合操作,则可以考虑进一步降低堆内存。因为堆内存越小,越可以从Elasticsearch(更快的GC)和Lucene(更多内存缓存)中获得更高的性能。
## 1.2、操作系统内存优化
操作系统作为运行Elasticsearch的基础,为保证性能,最好禁用系统的swap的使用,方法如下:
```shell
1、暂时关闭SWAP,重启后恢复。
swapoff -a
2. 永久关闭SWAP
编辑/etc/fstab, 注释掉如下SWAP分区项即可。
#UUID=0b55fdb8-a9d8-4215-80f7-f42f75644f87 none swap sw 0 0
```
如果是混合服务器,不能完全禁用swap的话,可以尝试降低swappiness,该值控制操作系统尝试交换内存的积极性,swappiness=0的时候表示最大限度使用物理内存,然后才是swap空间,swappiness=100的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。linux的基本默认设置为60,具体如下:
```plain
cat /proc/sys/vm/swappiness
```
也就是说,你的内存在使用到100-60=40%的时候,就开始出现有交换分区的使用。
操作系统层面,要尽可能使用内存,就需要对该参数进行调整。
临时调整的方法如下,例如调成10,可以在命令行执行如下:
```plain
sysctl vm.swappiness=10
```
要想永久调整的话,需要将在/etc/sysctl.conf修改,加上:
```plain
cat /etc/sysctl.conf
vm.swappiness=10
```
## 1.3、使用更快的硬件
要保证Elasticsearch的性能,在硬件上肯定是配置SSD硬盘,是最有效果的,如果有多个SSD硬盘,可以配置成RAID 0阵列以获得更佳的IO性能。但是任何一个SSD损坏都有可能破坏index。因此,通常正确的做法是优化单的shard存储性能,然后添加replicat放在不同的节点。同时使用snapshot快照和restore功能去备份 index。
# 二、Logstash性能调优
## 2.1、配置参数调优
可以通过调节logstash配置文件logstash.yml以下配置项,对性能进行调优:
> (1)、pipeline.workers:该参数可控制output或filter插件的工作线程数(只能设置正整数), logstash的grok正则解析极其消耗计算资源,因此filter一般都会存在瓶颈,此时需要增加工作线程,以提高性能。官方建议线程数设置大于CPU核数,如果logstah节点是混用服务器,建议等于或小于CPU数。
> (2)、pipeline.batch.size:设置批量执行event的最大值,该值是用于input的批量处理事件值,再打包发送给filter和output,增加该值可以在一定范围内提高性能,但是需要增加额外的内存开销。
> (3)、pipeline.batch.delay:批量处理事件的最大等待值(input需要按照batch处理的最大值发送到消息队列,但是不能一直等,所以需要一个最大的超时机制)。
## 2.2、Logstash中的JVM配置文件
  Logstash也需要运行在JVM中,对Logstash中的JVM也是需要调优的,可以通过配置logstash的jvm.options文件来针对JVM进行调优。比如内存的最大最小、垃圾清理机制等等。JVM的内存分配不能太大,也不能太小,太大会拖慢操作系统。太小导致无法启动。
可以在Logstash的配置目录下jvm.options文件中找到如下内容:
```plain
-Xms1g
-Xmx1g
```
堆内存对logstash来说也是非常重要的,一般要求将初始值和最大值设置为一致,这样可以防止动态调整堆内存大小的消耗,堆内存的大小一般设置为物理内存的一半即可,最大不超过32GB,jvm调有的方式没有固定的准则,一个通用的方法是依次增加堆内存大小,然后监控性能变化,如果性能有提升,再次重复前面的操作,这样反复测试,一直达到最优为止。
要修改jvm大小,直接修改jvm.options文件中这两个值即可,例如修改为16g,可以这么写:
```plain
-Xms16g
-Xmx16g
```
然后再次重启logstash,配置就生效了。
# 三、kafka性能调优
kafka是一个高吞吐量分布式消息系统,并且提供了持久化。其高性能有两个重要特征,分别是:
> (1)磁盘连续读写性能远远高于随机读写的特点
> (2)通过将一个topic拆分多个partition,可提供并发和吞吐量。
要充分发挥kafka的性能,就需要满足以上这两个条件,常见的调优方式有如下几个方面。
## 3.1、内存优化
这里首先介绍一个名词page cache, Linux系统内核为文件系统文件设置了一个缓存,对文件读、写的数据内容都缓存在这里。这个缓存就称为 page cache(页缓存)。在命令行输入free,或者“cat /proc/meminfo”,输出的"Cached"部分就是Page Cache。
> 当发生一个文件读操作时,系统会首先在Page Cache中查找,如果找到,就直接返回了,没有的话就会从磁盘读取文件,然后写入Page Cache,再读取,最终返回需要的数据。当写操作发生时,系统只是将数据写入Page Cache中,并将该页置上dirty标志。写入Page Cache的数据会被定期批量保存到文件系统,减少了磁盘的操作次数,减少系统开销。
这就是文件系统的读、写机制,而kafka是重度依赖底层操作系统提供的Page Cache功能的。也就是说kafka一开始是把数据写到PageCache的,如果消费者一直在消费,而且速度大于等于kafka的生产者发送数据的速度,那么消费者会一直从Page Cache读取数据,读、写都在内存中完成接力,根本没有磁盘访问,不会因为kafka写入磁盘的操作影响吞吐量。这也是kafka非常高效的根本原因。
因此,对内存的调优,主要是对linux系统下内存的优化,这里需要关注两个操作系统参数:
> (1)、vm.dirty\_background\_ratio:这个参数指定了当文件系统缓存脏页(Page Cache中的数据称为脏页数据)数量达到系统内存百分之多少时(默认10%)就会触发pdflush/flush/kdmflush等后台回写进程运行,将一定缓存的脏页异步地刷入磁盘。增减这个值是最主要的调优手段。
>
> (2)vm.dirty\_ratio:这个参数则指定了当文件系统缓存脏页数量达到系统内存百分之多少时(默认20%),系统不得不开始处理缓存脏页(因为此时脏页数量已经比较多,为了避免数据丢失需要将一定脏页刷入磁盘);在此过程中很多应用进程可能会因为系统刷新内存数据到磁盘而发生IO阻塞。
这两个系统参数对应的文件为:
```plain
vm.dirty_background_ratio:/proc/sys/vm/dirty_background_ratio
vm.dirty_ratio:/proc/sys/vm/dirty_ratio
```
建议将vm.dirty\_background\_ratio设置为5%,vm.dirty\_ratio设置为10%。具体的设置根据不同环境,需要进行测试、再测试。
最后,kafka虽然也是java应用,但是它需要的堆内存比较小(因为数据不放入JVM中),因此,建议保留物理内存的60%以上给操作系统,以便保证page cache的分配。
## 3.2、topic的拆分
kafka读写的单位是partition,将一个topic拆分为多个partition可以提高系统的吞吐量,但是有一个前提条件就是:不同的partition要分布在不同在磁盘上。如果多个partition位于一个磁盘上就会出现多个进程同时对一个磁盘的多个文件进行读写,使得操作系统对磁盘的读写进行频繁的调度,破坏了磁盘读写的连续性。
要将不同的partition要分布在不同在磁盘上,可以将磁盘的多个目录配置到broker的log.dirs,例如:
```ruby
log.dirs=/disk1/logs,/disk2/logs,/disk3/logs
```
kafka会在新建partition的时候,将新partition分布在partition最少的目录上,因此,一般不能将同一个磁盘的多个目录设置到log.dirs。
## 3.3、配置参数优化
> 网络和IO线程配置优化
配置参数:
num.network.threads:Broker处理消息的最大线程数
num.io.threads:Broker处理磁盘IO的线程数
优化建议:
一般num.network.threads主要处理网络io,读写缓冲区数据,基本没有io等待,配置线程数量为cpu核数加1
num.io.threads主要进行磁盘io操作,高峰期可能有些io等待,因此配置需要大些。配置线程数量为cpu核数2倍,最大不超过3倍.
> 日志保留策略配置优化
当kafka server的被写入海量消息后,会生成很多数据文件,且占用大量磁盘空间,如果不及时清理,可能磁盘空间不够用,kafka默认是保留7天。
优化建议:
减少日志保留时间,建议三天或者更多时间。通过log.retention.hours来实现,例如:
```plain
log.retention.hours=72
```
> 段文件大小优化
段文件配置1GB,有利于快速回收磁盘空间,重启kafka加载也会加快,相反,如果文件过小,则文件数量比较多,kafka启动时是单线程扫描目录(log.dir)下所有数据文件),文件较多时性能会稍微降低。可通过如下选项配置段文件大小:
```plain
log.segment.bytes=1073741824
```
> log数据文件刷盘策略优化
为了大幅度提高producer写入吞吐量,需要定期批量写文件,优化建议如下:
```plain
每当producer写入10000条消息时,刷数据到磁盘。可通过如下选项配置:
log.flush.interval.messages=10000
```
```plain
每间隔1秒钟时间,刷数据到磁盘。可通过如下选项配置:
log.flush.interval.ms=1000
```
> replica复制配置优化
kafka集群下每个follow都会从leader拉取消息进行同步数据,follow同步性能由下面这几个参数决定:
```css
num.replica.fetchers:拉取线程数
replica.fetch.min.bytes:拉取最小字节数
replica.fetch.min.bytes:拉取最大字节数
replica.fetch.wait.max.ms:最大等待时间,表示follow拉取的频率
```
优化建议:
> num.replica.fetchers: 配置多个拉取线程数可以提高follower的I/O并发度,单位时间内leader持有更多请求,相应负载会增大,因而需要根据机器硬件资源做权衡
> replica.fetch.min.bytes=1: 默认配置为1字节,否则读取消息不及时
> replica.fetch.max.bytes= 5 _1024_ 1024: 默认为1MB,这个值太小,5MB为宜,根据业务情况进行调整。
> replica.fetch.wait.max.ms: follow拉取频率,需要根据日志输出速度进行设置,如果拉取频率过高,会导致cpu飙升,这是因为在leader无数据需要同步时,由于拉取频率过高,而出现挤压大量无效请求的情况。
# 四、filebeat性能调优
filebeat作为日志采集agent,是需要部署到生产服务器上的,因此对filebeat的配置和调优至关重要,不合理的设置会导致filebeat占用过量的系统资源,还会影响生产服务器的正常运行。
要合理配置filebeat,就需要理解filebeat的工作机制以及运行原理,与logstash相比,filebeat虽然占用系统资源很少,但是在日志量比较大的情况下或者日志异常突发时,filebeat也会占用大量系统内存开销,因而,合理设置filebeat的配置参数十分重要。
filebeat在没有日志可采集情况下,的确不会占用太大的内存开销(一般在100M左右),但在有大量的日志需要采集时,filebeat的内存占用是没有固定值的,根据我们的使用经验,如果出现单条日志大于50KB,并且有瞬间爆发量的时候, filebeat的内存占用将会大于300MB,甚至,如果出现了极端情况,例如单条日志>10M时,即使filebeat会截断到10M,那么filebeat的内存占用也会达到20GB,这就很可怕了。
## 4.1、内存优化
Filebeat内存限制,有两种模式,一种是内存模式,一种是文件缓存模式,实际应用中任选其一即可。
**(1)内存模式**
需要注意的是,所有事件(events)都是保存在内存中的,此模式只能限制事件数,无法限制最大使用内存,这有可能会因为日志长度导致内存使用暴增,相关配置如下:
```makefile
queue.mem:
events: 4096 #表示队列可以存储的事件数量。默认值是4096个事件。
flush.min_events: 512 #发布所需的最小事件数量。 默认值是0,表示可以直接输出发布事件,而无需额外的等待时间。 如果设置为非0,必须等待,在满足指定的事件数量后才能输出发布事件。
flush.timeout: 5s #表示最早的可用事件在队列中等待的最长时间,超过这个时间,立即输出发布事件,默认值是0s,表示立即可以输出发布事件
```
上面这对配置表示,如果512个事件可用或者最旧的可用事件在队列中等待5秒,则就将事件转发到输出。
**(2)文件缓存模式**
此模式可以限制最大使用内存,在filebeat6.3版本以后可以使用此功能,相关配置如下:
```perl
queue.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 #缓冲事件的数量。一旦达到上限,就刷新写缓冲区。
```
上面这个配置的含义如下:
> Spool file将所有事件存储在磁盘的缓冲区中(内存中),如果已写入10MiB内容或1024个事件,则刷新写入缓冲区。如果最旧的可用事件在写缓冲区中等待5秒,则也将刷新写入缓冲区。
除了内存使用优化,filebeat还可以设置CPU最大使用核数,通过如下选项即可定义:
```css
max_procs:4
```
这个表示使用4个逻辑CPU。
## 4.2、文件系统资源优化
filebeat对日志的采集是贪婪的,只要发现日志就会坚持把日志采集完,否则会永久持有文件句柄不会“放手”,即使文件被删除。因此,filebeat在收集大量日志的时候,如果配置参数设置不当,会导致文件系统大量文件句柄被filebeat占用,导致收集日志异常,因此需要对filebeat采集日志的策略进行优化,常见的优化参数有如下几个:
> (1)close\_inactive: 1m
>
> 表示没有新日志采集后多长时间关闭文件句柄,默认5分钟,设置成1分钟,加快文件句柄关闭;
>
> (2)close\_timeout: 3h
>
> 表示传输了3h后在没有传输完成的话就强行关闭文件句柄,这个配置项是解决文件句柄耗尽的问题。但是开了这个配置项会有丢数据的风险。需要综合考虑。
>
> (3)clean\_inactive: 72h
>
> 这个配置项表示多久清理一次文件描述在registry文件,默认值是0表示不清理,不清理的意思是采集过的文件描述在registry文件里永不清理,在运行一段时间后,registry会变大,可能会带来性能问题。
>
> (4)ignore\_older: 70h
> 上面设置了clean\_inactive,就需要设置ignore\_older,且要保证ignore\_older < clean\_inactive。
关于filebeat的优化,就介绍这么多,其实已经足够了,优化是个综合技术,没有拿来主义,只能在一些经验的基础上,结合自己的业务场景,根据自己的日志特点设定合适的值,然后逐步测试,再测试,一步一步达到一个最优状态,这是进行优化的基本策略和思路。