elasticsearch 集群配置

集群脑裂问题:

discovery.zen.minimum_master_nodes参数对于集群的可靠性来说,是非常重要的。这个设置可以预防脑裂问题,也就是一个集群中存在两个master。

如果因为网络的故障,导致一个集群被划分成了两片,每片都有多个node,以及一个master,那么集群中就出现了两个master了。但是因为master是集群中非常重要的一个角色,主宰了集群状态的维护,以及shard的分配,
因此如果有两个master的化,可能会导致破坏数据。

那么那个参数的作用,就是告诉es直到有足够的master候选节点时,才可以选举出一个master,否则就不要选举出一个master。这个参数必须被设置为集群中master候选节点的quorum数量,也就是大多数。
至于quorum的算法,就是:master候选节点数量 / 2 + 1。

比如我们有10个节点,都能维护数据,也可以是master候选节点,那么quorum就是10 / 2 + 1 = 6。

如果我们有三个master候选节点,还有100个数据节点,那么quorum就是3 / 2 + 1 = 2

如果我们有2个节点,都可以是master候选节点,那么quorum是2 / 2 + 1 = 2。此时就有问题了,因为如果一个node挂掉了,那么剩下一个master候选节点,是无法满足quorum数量的,也就无法选举出新的master,集群就彻底挂掉了。
此时就只能将这个参数设置为1,但是这就无法阻止脑裂的发生了。

2个节点,discovery.zen.minimum_master_nodes分别设置成2和1会怎么样

综上所述,一个生产环境的es集群,至少要有3个节点,同时将这个参数设置为quorum,也就是2。discovery.zen.minimum_master_nodes设置为2,如何避免脑裂呢?

那么这个是参数是如何避免脑裂问题的产生的呢?比如我们有3个节点,quorum是2.现在网络故障,1个节点在一个网络区域,另外2个节点在另外一个网络区域,不同的网络区域内无法通信。这个时候有两种情况情况:

(1)如果master是单独的那个节点,另外2个节点是master候选节点,那么此时那个单独的master节点因为没有指定数量的候选master node在自己当前所在的集群内,因此就会取消当前master的角色,尝试重新选举,但是无法选举成功。
然后另外一个网络区域内的node因为无法连接到master,就会发起重新选举,因为有两个master候选节点,满足了quorum,因此可以成功选举出一个master。此时集群中就会还是只有一个master。

(2)如果master和另外一个node在一个网络区域内,然后一个node单独在一个网络区域内。那么此时那个单独的node因为连接不上master,会尝试发起选举,但是因为master候选节点数量不到quorum,
因此无法选举出master。而另外一个网络区域内,原先的那个master还会继续工作。这也可以保证集群内只有一个master节点。

综上所述,通过在elasticsearch.yml中配置discovery.zen.minimum_master_nodes: 2,就可以避免脑裂问题的产生。

但是因为es集群是可以动态增加和下线节点的,所以可能随时会改变quorum。所以这个参数也是可以通过api随时修改的,特别是在节点上线和下线的时候,都需要作出对应的修改。而且一旦修改过后,这个配置就会持久化保存下来。

PUT /_cluster/settings
{
    "persistent" : {
        "discovery.zen.minimum_master_nodes" : 2
    }
}

 

shard recovery配置以及集群重启时的无意义shard重分配问题

在集群重启的时候,有一些配置会影响shard恢复的过程。首先,我们需要理解默认配置下,shard恢复过程会发生什么事情。如果我们有10个node,每个node都有一个shard,可能是primary shard或者replica shard,你有一个index,有5个primary shard,每个primary shard有一个replica shard。如果我们将整个集群关闭了进行一些维护性的操作,比如给机器安装新的磁盘之类的事情。当我们重启集群的时候,肯定节点是一个接一个的启动的,可能会出现5个节点先启动了,然后剩下5个节点还没启动。也许是因为剩下的5个节点没来得及启动,或者是因为一些原因耽搁了,总之不管是什么原因,就是现在只有5个节点是在线的。这5个节点会通过gossip协议互相通信,选举出一个master,然后组成一个集群。他们会发现数据没有被均匀的分布,因为有5个节点没有启动,那么那5个节点上的shard就是不可用的,集群中就少了一半的shard。此时在线的5个node就会将部分replica shard提升为primary shard,同时为每个primary shard复制足够的replica shard。最后,可能剩下的5个节点加入了集群。但是这些节点发现本来是他们持有的shard已经被重新复制并且放在之前的5个node速度回当了,此时他们就会删除自己本地的数据。然后集群又会开始进行shard的rebalance操作,将最早启动的5个node上的shard均匀分布到后来启动的5个node上去。

在这个过程中,这些shard重新复制,移动,删除,再次移动的过程,会大量的耗费网络和磁盘资源。对于数据量庞大的集群来说,可能导致每次集群重启时,都有TB级别的数据无端移动,可能导致集群启动会耗费很长时间
。但是如果所有的节点都可以等待整个集群中的所有节点都完全上线之后,所有的数据都有了以后,再决定是否要复制和移动shard,情况就会好很多。

所以现在问题我们已经知道了,那么我们就可以配置一些设置来解决这个问题。首先我们需要设置一个参数,gateway.recover_after_nodes: 8。这个参数可以让es直到有足够的node都上线之后,再开始shard recovery的过程。所以这个参数是跟具体的集群相关的,要根据我们的集群中节点的数量来决定。此外,还应该设置一个集群中至少要有多少个node,等待那些node的时间:gateway.expected_nodes: 10,gateway.recover_after_time: 5m。经过上面的配置之后,es集群的行为会变成下面这样,等待至少8个节点在线,然后等待最多5分钟,或者10个节点都在线,开始shard recovery的过程。这样就可以避免少数node启动时,就立即开始shard recovery,消耗大量的网络和磁盘资源,甚至可以将shard recovery过程从数小时缩短为数分钟。

 

1、jvm gc

jvm使用垃圾回收器来释放掉不用的内存,千万不要去调节默认的垃圾回收行为。es默认用的垃圾回收器是CMS。CMS回收器是并发式的回收器,能够跟应用程序工作线程并发工作,最大程度减少垃圾回收时的服务停顿时间。但是CMS还是会有两个停顿阶段,同时在回收特别大的heap时也会有一些问题。尽管有一些缺点,但是CMS对于要求低延时请求响应的软件来说,还是最佳的垃圾回收器,因此官方的推荐就是使用CMS垃圾回收器。

有一种最新的垃圾回收器叫做G1。G1回收器可以比CMS提供更少的回收停顿时间,而且能够这对大heap有更好的回收表现。它会将heap划分为多个region,然后自动预测哪个region会有最多可以回收的空间。通过回收那些region,就可以最小化停顿时长,而且可以针对大heap进行回收。听起来还挺不错的,但是不幸的是,G1还是比较年轻的一种垃圾回收器,而且经常会发现一些新的bug,这些bug可能会导致jvm挂掉。lucene的测试套件就检查出来了G1的一些bug。因此es官方不推荐现在使用G1垃圾回收器,

2、threadpool

每个人都很喜欢去调优线程池,而且大部分人都特别喜欢增加线程池的线程数量,无论是大量的写入,还是大量的搜索,或者是感觉服务器的cpu idle空闲率太高,都会增加更多的线程。在es中,默认的threadpool设置是非常合理的,
对于所有的threadpool来说,除了搜索的线程池,都是线程数量设置的跟cpu core一样多的。如果我们有8个cpu core,那么就可以并行运行8个线程。那么对于大部分的线程池来说,分配8个线程就是最合理的数量。

不过搜索会有一个更加大的threadpool,一般被配置为:cpu core * 3 / 2 + 1。

也许我们会觉得有些线程可能会因为磁盘IO等操作block住,所以我们需要更多的线程。但是在es中这并不是一个问题,大多数的磁盘IO操作都是由lucene的线程管理的,而不是由es管理的,因此es的线程不需要关心这个问题。
此外,threadpool还会通过在彼此之间传递任务来协作执行,我们不需要担心某一个网络线程会因为等待一次磁盘写操作,而导致自己被block住,无法处理网络请求。网络线程可以将那个磁盘写操作交给其他线程池去执行,然后自己接着回来处理网络请求。

其实我们的进程的计算能力是有限的,分配更多的线程只会强迫cpu在多个线程上下文之间频繁来回切换。一个cpu core在同一时间只能运行一条线程,所以如果cpu要切换到另外一个线程去执行,需要将当前的state保存起来,然后加载其他的线程进来执行。如果线程上下文切换发生在一个cpu core内,那么还好一些,但是如果在多个cpu core之间发生线程上下文切换,那么还需要走一个cpu core内部的通信。这种线程上下文切换会消耗掉很多的cpu资源,对于现在的cpu来说,每次线程上下文切换,都会导致30微秒的时间开销,所以宁愿将这些时间花费在任务的处理上。

很多人会将threadpool大小设置为一些很愚蠢的数值,在一个8核的机器上,可能运行了超过60,100,甚至1000个线程。这么多的线程会导致cpu资源利用率很低。所以下次如果我们要调节线程池的话,记住,千万别这么干。如果一定要调节线程数量,也得记住要根据你的cpu core数量来调节,比如设置为cpu core的两倍,如果设置的再多,那么就是一种浪费了。

 


除了之前讲解的一些配置,根据你的集群环境特殊的配置,我们这一讲来讲解最重要的内存的分配,提出一些问题,生产环境部署es,不可避免要回答一个问题,比如我的机器上有64G的内存,或者32G的内存,那么一般来说我应该分配多少个G的内存给es的jvm heap

1、jvm heap分配

es默认会给jvm heap分配2个G的大小,对于几乎所有的生产环境来说,这个内存都太小了。如果用这个默认的heap size,那么生产环境的集群肯定表现不会太好。

有两个方式来调节es中的jvm heap size。最简单的就是设置环境变量,ES_HEAP_SIZE。当es进程启动的时候,会读取这个环境变量的值,然后设置为jvm的heap size。举例来说,可以这样来设置:export ES_HEAP_SIZE=10g。
此外,还可以在启动es进程的时候,传递一个jvm的option,比如:ES_JAVA_OPTS="-Xms10g -Xmx10g" ./bin/elasticsearch,但是要注意-Xms和-Xmx最小和最大堆内存一定设置的一样,避免运行过程中的jvm heap resize,那会是一个非常耗时的过程。

在新版本的es中,比如es 5.x里面,一般推荐在jvm.options文件里面去设置jvm相关的参数。

2、将机器上少于一半的内存分配给es

一个常见的问题就是将es进程的jvm heap size设置的过于大了。比如我们有一台64G的机器,可能我们甚至想要给es jvm size设置64G内存。但是这是错误的。大家可能会觉得说,直接将机器上的可用的内存都分配给es jvm heap,性能是绝对高的,
因为大量的数据都可以缓存在内存里面。

虽然heap对于es来说是非常重要的,jvm heap被es用来存放很多内存中的数据结构来提供更快的操作性能。但是还有另外一个内存的用户,那就是lucene。lucene的设计就是要使用底层的os filesystem cache来缓存数据结构。lucene的segment是保存在单独的文件中的。因为这些segment是不可变的,所以这些文件实际上也从来不会改变。这样的话,就可以更好的缓存这些文件,底层的os cache会将hot segment驻留在内存中以供更快的访问。这些segment包括了倒排索引(为了全文检索)以及正排索引(为了聚合操作)。lucene的性能是严重依赖于底层的os的,但是如果我们给了过多的内存到es的jvm heap,那么就没有足够的内存留给lucene。这会极大的影响性能。

这里想告诉大家的是,就是说,es的性能很大的一块,其实是由有多少内存留给操作系统的os cache,供lucene去缓存索引文件,来决定的。所以说lucene的os cache有多少是非常重要的。

一般建议的是,将50%的内存分配给es jvm heap,然后留50%的内存给os cache。留给os cache的内存是不会不使用的,lucene会将剩下的内存全部用光,用来cache segment file。如果我们没有对任何分词的text field进行聚合操作,
那么我们就不需要使用fielddata,我们甚至可以考虑给os cache更多的内存,因为fielddata是要用jvm heap。如果我们给jvm heap更少的内存,那么实际上es的性能反而会更好,因为更多的内存留给了lucene用os cache提升索引读写性能,
同时es的jvm heap的gc耗时会更少。

es部署的机器上,内存是如何分配的,如何使用的,如何决定我们的操作系统的,我们该如何给jvm和os cache分配内存

3、不要给jvm分配超过32G内存

还有另外一个原因不要将过多的内存分配给es的jvm heap。如果heap小于32G的化,jvm会用一种技术来压缩对象的指针,object pointer。在java中,所有的对象都会被分配到heap中,然后被一个pointer给引用。object pointer会指向heap中的对象,
引用的是二进制格式的地址。

对于32位的系统来说,jvm最大的heap size就是4G,解释一下,32位,0和1值,0和1在32位的组合是2^32次方的字节,除以1024就是多少k,再除以1024就是多少mb,再除以1024就是多少gb,最后算下来就是4G。对于64位的系统来说,heap size可以更大,
但是64位的object pointer会耗费更多的空间,因为object pointer更大了。比浪费更多内存空间更恶劣的是,过大的object pointer会在cpu,main memory和LLC、L1等多级缓存间移动数据的时候,吃掉更多的带宽。

所以jvm用了一种技术,叫做compressed oops来解决object pointer耗费过大空间的问题。这个技术的核心思想是,不要让object pointer引用内存中的二进制地址,而是让object pointer引用object offset。这就意味着32位的pointer可以引用400万个对象,
而不是400万字节。这也意味着,使用32位的pointer,最大的heap大小可以到32G。此时只要heap size在32G以内,jvm就会自动启用32位的object pointer,因为32位的对象指针,足够引用32G的内存了,就可以用32位的pointer替代64位的pointer。
但是32位的pointer比64位的pointer可以耗费更少的内存耗费。

如果你给jvm heap分配的内存小于32G,此时jvm会自动使用32位的object pointer,同时是让pointer指向对象的offset,32位的object pointer就足以引用32G的内存,同时32位的pointer占用的内存空间很少,对cpu和memory之间移动数据的带宽开销也很少。
这个过程就叫做compressed oops。

但是一旦我们越过了32G这个界限,就是给jvm heap分配了超过32G的内存,比较坑了。就没有办法用32位的pointer+引用object offset的模式了,因为32位的pointer最多引用32G的内存,超过了32G,就没法用32位pointer。不用32位pointer,
就只能用64位pointer,才能引用超过32G的内存空间。此时pointer就会退回到传统的object pointer引用对象的二进制地址的模式,此时object pinter的大小会急剧增长,更多的cpu到内存的带宽会被占据,更多的内存被耗费。
实际上,不用compressed oops时,你如果给jvm heap分配了一个40~50G的内存的可用空间,实际上被object pointer可能都要占据十几G的内存空间,可用的空间量,可能跟使用了compressed oops时的32GB内存的可用空间,20多个G,几乎是一样的。

因此,即使我们有很多内存,但是还是要分配给heap在32GB以内,否则的话浪费更多的内存,降低cpu性能,而且会让jvm回收更大的heap。

综上所述,如果你给jvm heap分配超过32G的内存,实际上是没有什么意义的,因为用64位的pointer,1/3的内存都给object pointer给占据了,这段内存空间就浪费掉了。还不如分配32G以内,启用compressed oops,可用空间跟你分配50个G的内存,是一样的。

所以也正是因为32G的限制,一般来说,都是建议说,如果你的es要处理的数据量上亿的话,几亿,或者十亿以内的规模的话,建议,就是用64G的内存的机器比较合适,有个5台,差不多也够了。给jvm heap分配32G,留下32G给os cache。


4、在32G以内的话具体应该设置heap为多大?

这个是根据具体情况而定的,不是固定死的,根据不同的jvm和平台而变。一般而言,将jvm heap size设置为31G比较安全一些。主要是要确保说,你设置的这个jvm heap大小,可以让es启用compressed oops这种优化机制。
此外,可以给jvm option加入-XX:+PrintFlagsFinal,然后可以打印出来UseCompressedOops是否为true。这就可以让我们找到最佳的内存设置。因为可以不断调节内存大小,然后观察是否启用compressed oops。

举例来说,如果在mac os上启动一个java 1.7,同时将heap size设置为32600mb,那么compressed oops是会开启的;但是如果设置为32766m,compressed oops就不会开启。相反的是,使用jdk 1.8的化,分配32766m,compressed oops是会开启的,
设置为32767m,就不会开启。所以说,这个东西不是固定的。根据不同的操作系统以及jvm版本而定。

在es启动日志中,我们可以查看compressed oops是否开启,比如下面的字样:[2015-12-16 13:53:33,417][INFO ][env] [Illyana Rasputin] heap size [989.8mb], compressed ordinary object pointers [true]。

5、对于有1TB内存的超大内存机器该如何分配?

如果我们的机器是一台超级服务器,内存资源甚至达到了1TB,或者512G,128G,该怎么办?首先es官方是建议避免用这种超级服务器来部署es集群的,但是如果我们只有这种机器可以用的话,我们要考虑以下几点:

(1)我们是否在做大量的全文检索?考虑一下分配4~32G的内存给es进程,同时给lucene留下其余所有的内存用来做os filesystem cache。所有的剩余的内存都会用来cache segment file,而且可以提供非常高性能的搜索,
几乎所有的数据都是可以在内存中缓存的,es集群的性能会非常高

(2)是否在做大量的排序或者聚合操作?聚合操作是不是针对数字、日期或者未分词的string?如果是的化,那么还是给es 4~32G的内存即可,其他的留给es filesystem cache,可以将聚合好用的正排索引,doc values放在os cache中

(3)如果在针对分词的string做大量的排序或聚合操作?如果是的化,那么就需要使用fielddata,这就得给jvm heap分配更大的内存空间。此时不建议运行一个节点在机器上,而是运行多个节点在一台机器上,
那么如果我们的服务器有128G的内存,可以运行两个es节点,然后每个节点分配32G的内存,剩下64G留给os cache。如果在一台机器上运行多个es node,建议设置:cluster.routing.allocation.same_shard.host: true。
这会避免在同一台物理机上分配一个primary shard和它的replica shard

6、swapping

如果频繁的将es进程的内存swap到磁盘上,绝对会是一个服务器的性能杀手。想象一下,内存中的操作都是要求快速完成的,如果需要将内存页的数据从磁盘swap回main memory的化,性能会有多差。如果内存被swap到了磁盘,
那么100微秒的操作会瞬间变成10毫秒,那么如果是大量的这种内存操作呢?这会导致性能急剧下降。

因此通常建议彻底关闭机器上的swap,swapoff -a,如果要永久性关闭,需要在/etc/fstab中配置

如果没法完全关闭swap,那么可以尝试调低swappiness值,这个值是控制os会如何将内存swap到磁盘的。这会在正常情况下阻止swap,但是在紧急情况下,还是会swap。一般用sysctl来设置,vm.swappiness = 1。
如果swappiness也不能设置,那么就需要启用mlockall,这就可以让我们的jvm lock住自己的内存不被swap到磁盘上去,在elasticsearch.yml中可以设置:bootstrap.mlockall: true。


1、系统的重要配置

理想情况下,es应该单独在一个服务器上运行,能够使用服务器上的所有资源。为了达到上述目标,我们需要配置操作系统,来允许用户运行es并且获取比默认情况下更多的资源。

在生产环境中下面的一些设置必须配置一下:

(1)禁止swapping
(2)确保拥有足够的虚拟内存
(3)确保拥有足够的线程数量

默认情况下,es会假设你是在开发模式下运行的。如果上面的任何配置没有正确的设置,那么会输出一些warning到日志文件中,但是我们还是可以启动es进程的。但是如果我们配置了网络设置,比如network.host,es会认为我们是运行在生产环境中的,然后就会将上述warning升级为exception。这些exception会阻止我们的es节点启动。这是一个重要的安全保障措施来确保我们不会因为错误的配置了es server,而导致数据丢失。

2、配置系统设置

在/etc/security/limits.conf中,可以配置系统设置,也可以用ulimit临时配置系统设置,在linux操作系统中,ulimit可以用来临时的改变资源限制。通常需要用root权限来设置ulimit。举例,如果要设置file descriptor为65536,可以用如下的命令来设置:ulimit -n 65536  ,但是在linux操作系统中,实际上永久性的资源限制可以通过编辑/etc/security/limits.conf文件来设置。比如要设置file descriptor,可以再limits.conf中加入下面的行:elasticsearch - nofile 65536 , 在下一次elasticsearch用户开启一个新的会话时就会生效

设置jvm option

一般建议通过jvm.options配置文件来设置es的jvm option。默认的地址是config/jvm.options,每行是一个jvm argument,此外,如也可以通过ES_JAVA_OPTS环境变量来设置jvm option,比如下面的命令:export ES_JAVA_OPTS="$ES_JAVA_OPTS -Djava.io.tmpdir=/path/to/temp/dir"

3、禁止swapping

大多数操作系统都会使用尽量多的内存来进行file system cache,并且尽量将不经常使用的java应用的内存swap到磁盘中去。这会导致jvm heap的部分内存,甚至是用来执行代码的内存页被swap到磁盘中去。swapping对于性能来说是非常差劲的,为了es节点的稳定性考虑,应该尽量避免这种swapping。因为swapping会导致gc过程从毫秒级变成分钟级,在gc的时候需要将内存从磁盘中swapping到内存里,特别耗时,这会导致es节点响应请求变得很慢,甚至导致es node跟cluster失联。在一个弹性的分布式系统中,让操作系统kill掉某一个节点,是很高效的。三种方法可以disable swapping。

推荐的option是彻底禁用swap,如果做不到的化,也得尽量最小化swappiness的影响,比如通过lock memory的方法。

(1)禁用所有的swapping file

通常来说,es进程会在一个节点上单独运行,那么es进程的内存使用是由jvm option控制的。可以使用下面的命令临时性禁止swap:swapoff -a, 要永久性的禁止swap,需要修改/etc/fstab文件,然后将所有包含swap的行都注释掉

(2)配置swappiness

另外一个方法就是通过sysctl,将vm.swappiness设置为1,这可以尽量减少linux内核swap的倾向,在正常情况下,就不会进行swap,但是在紧急情况下,还是会进行swap操作。sysctl -w vm.swappiness=1

(3)启用bootstrap.memory_lock

最后一个选项,就是用mlockall,将es jvm进程的address space锁定在内存中,阻止es内存被swap out到磁盘上去。在config/elasticsearch.yml中,可以配置:bootstrap.memory_lock: true, GET _nodes?filter_path=**.mlockall,通过这行命令可以检查mlockall是否开启了,如果发现mlockall是false,那么意味着mlockall请求失败了。会看到一行日志,unable to lock jvm memory。最大可能的原因,就是在linux系统中,启动es进程的用户没有权限去lock memory,需要通过以下方式进行授权:ulimit -l unlimited, /etc/security/limits.conf,memlock设置为unlimited, 另外一个原因可能是临时目录使用noexec option来mount了。可以通过指定一个新的临时目录来解决, export ES_JAVA_OPTS="$ES_JAVA_OPTS -Djava.io.tmpdir=/path/to/temp/dir",当然也可以通过在jvm.options文件中来设置java.io.tmpdir

4、虚拟内存

es使用hybrid mmapfs / niofs目录来存储index数据,操作系统的默认mmap count限制是很低的,可能会导致内存耗尽的异常。需要提升mmap count的限制:sysctl -w vm.max_map_count=262144, 如果要永久性设置这个值,要修改/etc/sysctl.conf,将vm.max_map_count的值修改一下,重启过后,用sysctl vm.max_map_count来验证一下数值是否修改成功,es同时会用NioFS和MMapFS来处理不同的文件,我们需要设置最大,这样我们才能有足够的虚拟内存来给mmapped文件使用,可以用sysctl来设置:sysctl -w vm.max_map_count=262144。还可以再/etc/sysctl.conf中,对vm.max_map_count来设置。

5、设置线程的数量

es用了很多线程池来应对不同类型的操作,在需要的时候创建新的线程是很重要的。要确保es用户能创建的最大线程数量至少在2048以上。可以通过ulimit -u 2048来临时设置,也可以在/etc/security/limits.conf中设置nproc为2048来永久性设置。

 

 


1、bootstrap check

es的新版本中引入了bootstrap check,也就是启动时检查。这些启动时检查操作,会检查许多es和系统的设置,将这些配置的值跟es期望的安全值去进行比较。如果es在development mode下,那么失败的检查仅仅在日志中打印warning。如果es运行在生产模式下,任何启动时检查的失败都会导致es拒绝启动。

2、development mode vs. production mode

默认情况下,es绑定到localhost hostname,来进行http和内部通信。这对于下载es并简单试用一下,包括日常的开发,都是非常方便的,但是对于生产环境是不行的。如果要组件一个es集群,es实例必须能够通过内部通信协议互相连通,
所必须绑定通信到一个外部的接口上。因此如果一个es实例没有绑定通信到外部接口(默认情况下),那么就认为es是处于开发模式下。反之,如果绑定通信到外部接口,那么就是处于生产模式下。可以通过http.host和transport.host,单独配置http的传输。这就可以配置一个es实例通过http可达,但是却不触发生产模式。因为有时用户需要将通信绑定到外部解耦来测试client的调用。对于这种场景,es提供了single-node恢复模式(将discovery.type设置为single-node),配置过后,一个节点会选举自己作为master,而且不会跟其他任何节点组成集群。,如果在生产模式下运行一个single node实例,就可以规避掉启动时检查(不要将通信绑定到外部接口,或者将通信绑定到外部接口,但是设置discovery type为single-node)。在这种场景下,
可以设置es.enforce.bootstrap.checks为true(通过jvm参数来设置),来强制bootstrap check的执行。

3、heap size check

如果jvm启动的时候设置的初始队大小和最大堆大小不同,可能会导致es运行期间的暂停,因为jvm堆在系统运行期间可能会改变大小。为了避免这种jvm resize导致的es进程暂停,建议启动jvm时,将初始堆大小和最大堆大小设置的相等。除此之外,如果bootstrap.memory_lock被启用了,jvm会在启动期间锁定jvm的初始大小。如果要通过heap size check,就必须合理设置heap size。默认情况下,es的jvm堆的最小和最大大小都是2g。如果在生产环境中使用,应该配置合理的heap size确保es有足够的堆内存可以使用。在jvm.options中设置的Xms和Xmx会用来分配jvm堆内存。

这些设置的值依赖于服务器上可用的总内存大小。下面是一些最佳实践的建议:

(1)将heap的最小和最大大小设置为一样大
(2)es有更多的heap大小,就有更多的内存用来进行缓存,但是过大的jvm heap可能会导致长时间的gc停顿
(3)不要设置最大heap size超过物理内存的50%,很专业昂才能给核心的file system cache留下足够的内存
(4)不要将Xmx设置超过32GB,否则jvm无法启用compressed oops,将对象指针进行压缩,确认日志里有heap size [1.9gb], compressed ordinary object pointers [true]
(5)更好的选择是,heap size设置的小于zero-based compressed oops,也就是26GB,但是有时也可以是30GB。通过-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompressedOopsMode开启对应,
确认有heap address: 0x000000011be00000, size: 27648 MB, zero based Compressed Oops,而不是heap address: 0x0000000118400000, size: 28672 MB, Compressed Oops with base: 0x00000001183ff000
(6)在jvm.options文件中,可以通过如下方式来配置heap size   -Xms2g   -Xmx2g

(7)也可以通过ES_JAVA_OPTS环境变量来设置heap size  , ES_JAVA_OPTS="-Xms2g -Xmx2g"

4、file descriptor check

file descriptor是unix操作系统的一种数据结构,用来track打开的文件。在unix操作系统中,所有东西都是file。比如,file可以是物理文件,虚拟文件,或者网络socket。es需要大量的file descriptor,比如说每个shard都由多个segment和其他文件组成,还有跟其他节点之间的网络通信连接。因为es要使用大量的file descriptor,所以如果file descriptor耗尽的话,会是一场灾难,甚至可能会导致数据丢失。尽量给es的file descriptor提升到65536,甚至更高。可以在/etc/security/limits.conf中,设置nofile为65536,GET _nodes/stats/process?filter_path=**.max_file_descriptors, 可以用上面这行代码检查每个node上的file descriptor数量,lucene会使用大量的文件,同时es也会使用大量的socket在节点间和client间进行通信,这些都是需要大量的file descriptor的。但是通常来说,现在的linux操作系统,都是给每个进程默认的1024个file descriptor的,这对于一个es进程来说是远远不够的。我们需要将es进程的file descriptor增加到非常非常大,比如说65535个。一般需要根据我们的操作系统的文档来查看如何设置file descriptor。然后可以直接对es集群查看GET,来确认file descriptor的数量:

{
  "cluster_name": "elasticsearch",
  "nodes": {
    "nLd81iLsRcqmah-cuHAbaQ": {
      "timestamp": 1471516160318,
      "name": "Marsha Rosenberg",
      "transport_address": "127.0.0.1:9300",
      "host": "127.0.0.1",
      "ip": [
        "127.0.0.1:9300",
        "NONE"
      ],
      "process": {
        "timestamp": 1471516160318,
        "open_file_descriptors": 155,
        "max_file_descriptors": 10240, 
        "cpu": {
          "percent": 0,
          "total_in_millis": 25084
        },
        "mem": {
          "total_virtual_in_bytes": 5221900288
        }
      }
    }
  }
}

5、memory lock check

如果jvm进行一个major gc的话,那么就会涉及到heap中的每一个内存页,此时如果任何一个内存页被swap到了磁盘上,那么此时就会被swap回内存中。这就会导致很多的磁盘读写开销,而这些磁盘读写开销如果节省下来,可以让es服务更多的请求。
有很多方法可以配置系统禁止swap。其中一种方法就是让jvm去lock heap内存在物理内存中,设置bootstrap.memory_lock即可。GET _nodes?filter_path=**.mlockall,检查一下,mlockall是否开启,如果是false,那么说明lock memory失败了,而且日志里可能会有unable to lock jvm memory的字样,可能就是因为运行es的用户没有lock memory的权限,此时就需要进行授权/etc/security/limits.conf,设置memlock为unlimited即可完成授权,另外一个原因导致lock memory失败,可能是因为临时目录,/tmp用noexec option来mount了,那么就需要设置ES_JAVA_OPTS,export ES_JAVA_OPTS="$ES_JAVA_OPTS -Djava.io.tmpdir=/path/to/temp/dir",或者在jvm.options中设置这个参数


6、maximum number of thread check

es会将每个请求拆分成多个stage,然后将stage分配到不同的线程池中去执行。在es中有多个线程池来执行不同的任务。所以es会创建许多的线程。最大线程数量的检查会确保说,es实例有权限去创建足够的线程。如果要通过这个检查,
必须允许es进程能够创建超过2048个线程。/etc/security/limits.conf,在这个文件中,用nproc来设置

7、maximum size virtual memory check

es使用mmap来将索引映射到es的address space中,这可以让jvm heap外但是内存中的索引数据,可以有非常告诉的读写速度。因此es需要拥有unlimited address space。最大虚拟内存大小的检查,会要求es进程有unlimited address space。

/etc/security/limits.conf,设置as为unlimited

8、maximum map count check

要高效使用mmap的话,es同样要求创建许多memory-mapped area。因此要求linux内核允许进程拥有至少262144个memory-mapped area,需要通过sysctl设置vm.max_map_count至少超过262144。

9、client jvm check

jvm有两种模式,client jvm和server jvm。不同的jvm会用不同的编译器来从java源码生成可执行机器代码。client jvm被优化了来减少startup time和内存占用,server 
jvm check会确保es没有运行在client jvm下。必须使用server jvm模式来启动es,而server jvm是默认的。

10、use serial collector check

针对不同的工作负载,jvm提供了不同的垃圾回收器。串行化垃圾回收期对于单cpu机器或者小内存,是很有效的。但是对于es来说,用串行化垃圾回收器,会成为一场性能上的灾难。因此这个check会确保es没有被配置使用串行化垃圾回收器。
es默认的就是cms垃圾回收器。

11、system call filter check

es会根据不同的操作系统来安装system call filter,用来阻止执行作为defense机制的fork相关system call,进而避免任意代码执行的攻击。这个check会检查是否允许system call filter,然后安装这些system call filter。
避免bootstrap.system_call_filter设置为false。

12、OnError and OnOutOfMemoryError check

jvm参数,OnError和OnOutOfMemoryError允许在jvm遇到了fatal error或者是OutOfMemoryErro的时候,执行我们预定义的命令。然而,默认情况下,es system call filter是启用的,这些filter是阻止forking操作的。
因此,用OnError和OnOutOfMemroyError和system call filter是不兼容的。这个check会检查,如果启用了system call filter,还设置了这两个jvm option,那么就不能启动。所以不要在jvm option中设置这两个参数。

13、early-access check

jdk提供了early-access快照,为即将到来的版本。这些版本不适合用作生产环境。这个check会检查有没有使用jdk的early-access快照版本。我们应该用jdk稳定版本,而不是试用版本。

14、G1 GC check

jdk 8的jvm早期版本中的g1 gc,有已知的问题可能导致索引破损。在JDK 8u40之前的版本都有这个问题。这个check会检查是否使用了那种早期的JDk版本。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值