Elasticsearch中拥有大量的自定义配置项,除了以下一些官方不能统一进行配置的选项(与环境,使用者情况有关),大多数最好不要自行配置,因为会引起很多不必要的麻烦(不好排查),并且es的很多默认项的已经是比较优的配置(包括性能方面)。
Elasticsearch有两个主要的配置文件:elasticsearch.yml用于elasticsearch主配置(还有jvm.options),log4j2.properties为其日志文件。
Elasticsearch有两个主要的配置文件:
用于elasticsearch主配置(还有jvm.options)若使用Debian或RPM安装,该文件位于/etc/elasticsearch/目录下;
log4j2.properties为其日志文件,位于es的安装目录下的config文件夹下,如:$ES_HOME/config/
启动时可以在配置文件中进行加载,也可以动态进行参数设置,如下是设置path.conf属性:
./bin/elasticsearch -Epath.conf=/path/to/my/config/
配置文件为YAML格式,索引可以使用一下两种方式进行设置,如:
path:
data: /var/lib/elasticsearch
logs: /var/log/elasticsearch
或者
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
使用环境变量替换配置中的值,如:
node.name: ${HOSTNAME}
network.host: ${ES_NETWORK_HOST}
当在配置中使用${prompt.text} 或 ${prompt.secret} 设置某一属性值时,如下(节点名称):
node:
name: ${prompt.text}
Enter value for [node.name]:
Es使用Log4j 2作为日志记录,使用log4j2.properties进行日志配置,但是es使用了三个属性配置方式暴露日志文件的配置(日志配置到哪个位置吗,文件的名称等),分别为:${sys:es.logs.base_path}, ${sys:es.logs.cluster_name}, and ${sys:es.logs.node_name}。由于一般建议配置日志node.name属性,所以一直配置说明如下(比如):
若path.logs配置为默认值/var/log/elasticsearch,集群名称属性cluster.name配置为production,则按照日志模板${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log,生产的日志为/var/log/elasticsearch/production.log
可以加载多个配置文件(在这种情况下,它们将被合并),只要它们被命名为log4j2.properties并es的配置目录下;这对于暴露额外记录者的插件非常有用。
更多详细陪参见log4j2。
有四种配置日志级别的方法,每种方法都有适合使用的情况:
service elasticsearch start -E logger.org.elasticsearch.transport=trace
配置格式为
logger.org.elasticsearch.transport: trace
动态地调整活动集群上的日志级别时,可以使用该方式,格式为:
PUT /_cluster/settings
{
"transient": {
"": ""
}
}
PUT /_cluster/settings
{
"transient": {
"logger.org.elasticsearch.transport": "trace"
}
}
使用场景:对日志记录器进行细粒度控制时,这是最合适的(例如,您希望将日志记录器发送到另一个文件,或者以不同的方式管理日志记录器;这是一个罕见的用例),格式如下:
logger..name =
logger..level =
logger.transport.name = org.elasticsearch.transport
logger.transport.level = trace
默认情况下的日志基本为error,但是某些情况下(如es版本升级时)需要查看更多的日志基本信息,可以查看已经弃用的warm基本日志,则可以将log4j2.properties文件中的基本进行修改,如下:
logger.deprecation.level = warn
并且默认情况下,已经为弃用日志级别开启了滚动策略,在日志到达1G之后进行滚动和压缩,并保留最大的5个日志文件(4个卷日志和活动日志)。
配置es的集群名称,默认是elasticsearch,es会自动发现在同一网段下的es,如果在同一网段下有多个集群,就可以用这个属性来区分不同的集群,入elasticsearch_production。
配置集群节点名,若不配置默认随机指定一个name列表中名字,该列表在es的jar包中config文件夹里name.txt文件中,其中有很多作者添加的有趣名字。该名字最好自行配置,建议自行配置如:node-1等。
指定该节点是否有资格被选举成为master node,默认是true,es是默认集群中的第一台机器为master,如果这台机挂了就会重新选举master。
指定该节点是否存储索引数据,默认为true。
Master node(主节点): node.master:true node.data:false
Data node(数据节点): node.master:false node.data:true
Client node(客户端节点): node.master:false node.data:false
设置默认索引分片个数,默认为5片,一般使用默认即可。
设置默认索引副本个数,默认为1个副本,一般使用默认即可,即5个主分片5个副本分片。
以下三个配置项:可以配置多个位置,并且配置多个位置拥有更好的性能和安全性。并且自定义配置路径可以避免重新安装 Elasticsearch的时候不小心把安装目录覆盖了,照成不必要的影响。
配置索引分片数据的位置
安装插件的位置
设置日志文件的存储路径,默认是es根目录下的logs文件夹。存储了大量我们关系的生产日志数据。
在比较早的版本中使用配置参数bootstrap.mlockall,设置为true来锁住内存。详细参见配置中的swapping模块。
集群选举最小的最小节点数,为防止脑裂的设置参数。由于网络的不确定性,可能存在一个集群存在两个或者多个主节点(拥有最高权限,可以决定新的索引的创建,分片是如何移动等信息),则会照成数据的不完整等情况。
该参数的设置一般为集群中的可选择为主节点的节点数决定,与数据节点的个数无关,即与每个节点的node.master和node.data参数设置有关,计算公式如下:
( master 候选节点个数 / 2) + 1
由于该字段设置的重要性,所以可以在集群运行中动态进行设置,这将成为一个永久的配置,并且无论你配置项里配置的如何,这个将优先生效。当你添加和删除 master 节点的时候,你需要更改这个配置。设置方式如下:
PUT /_cluster/settings
{
"persistent" : {
"discovery.zen.minimum_master_nodes" : 2
}
}
节点ip设置(并且设置后将会从development模式转换为production模式)
端口设置http端口范围[9200-9300),一般使用默认9200。http端口可用于http集群插件的使用,但是同时需要配置以下项:
设置节点间交互的tcp端口,即节点通信端口,默认是9300。node-to-node端口设置范围[9300-9400)
Es的Discover模块提供了单播和组播的方式发现集群中的其他节点以组成集群,但是生产环境中应该只能使用单播发现的方式,所以5.x后es只提供了插件方式的组播发现方式。为防止其他不可控的因素将节点加点到集群,可以设置单播发现的集群列表单,可以配置ip或端口(默认9200)。
对应某些敏感的信息,完全依赖于文件系统的权限来保护它们的值是不够的,需要使用elasticsearch的elasticsearch-keystore工具的引入。引入后所有的命令都需要使用该用户权限执行。
工具的创建(开启):
./bin/elasticsearch-keystore create
敏感的字符串设置,比如云插件的身份验证凭证:
bin/elasticsearch-keystore add the.setting.name.to.set
移除:
bin/elasticsearch-keystore remove the.setting.name.to.remove
在之前的版本中,将某些启动配置设置为warm级别很容易被忽略,为了确保这些信息被关注,现在需要在启动时进行检查。并且很多生成情况下遇到的很多奇怪的问题都是因为下面第五点中的重要启动配置而照成,所以需要特别关注。很多检查在development模式下仅为warm级别,但是在production模式下,检查不通过则不允许启动。以下是一些在两种模式下都需要检查的设置。
集群节点的http通信和节点间通信使用http.host和transport.host在es的配置文件中进行配置,并且也是通过判断通信方式是否通过外网接口进行暴露,以判断es是否为生产模式状态。默认情况下,当下载了Es包进行安装则为开发模式,该模式可以方便的使用single-node discovery。
有时候需要将传输绑定到一个外部接口,以测试他们对传输客户机的使用情况。并且节点将自行选择主节点,并且不会与任何其他节点一起连接集群。则需要在配置文件中添加:
discovery.type:single-node
有时候需要在生成环境中只启动单节点(这种情况下,可以将IP设置为外网或内网接口暴露但是一定需要将single-node开启),则需要开启强制不检查启动的开关。可以在jvm.options中进行配置:
es.enforce.bootstrap.checks:true
还可以使用环境变量ES_JAVA_OPTS中增加-Des.enforce.bootstrap.checks=true进行设置。
如果JVM的Heap的初始值和最大堆大小是不相等的,那么当JVM堆在系统使用期间被调整时,它很容易出现停顿。为了避免出现该情况,需要将两个值设置一致。如果bootstrap.memory_lock 设置为true,则不允许内存交换,在该情况下,如果初始堆大小不等于最大堆大小,那么在重新调整大小之后,就不是所有的JVM堆都被锁定在内存中了。
在Unix(Linux)中一切都是一个文件,而Es需要大量的文件描述符,因为es的shard是由大量的文件(document)组成。所以es启动前需要检查系统配置的文件描述符号大小。
当Jvm在进行GC操作时,会触及堆的每一个页面。如果这些页面中的任何一个被交换到磁盘上它们就必须被交换到内存中。为了防止涉及的内存与磁盘之前的转换,需要将堆内存进行锁定(不予许转换为磁盘形式),锁定方式有很多种,一种是使用jvm锁定内存。但是不同的操作系统是不同的,即mlockall (Unix) 和virtual lock (Windows),并且该操作需要特殊的执行权限。需要在es中设置bootstrap.memory_lock属性的设置需要检查权限等,所以mlockall 设置时,可能导致es不能正常启动。
Elasticsearch将请求分为不同的阶段,并将不同的阶段任务交给不同的线程池完成。所以在es内部需要创建不同种类的线程池,并且在Linux系统中必须配置,以允许es进程能够创建至少2048个线程。可以在/etc/security/limits.conf配置中,使用nproc 进行配置(当然让该用户拥有root权限)。
Elasticsearch和Lucene利用mmap将索引的部分映射到Elasticsearch的地址空间。这样就可以将某些索引数据从JVM堆中保留下来,但在内存中可以快速访问,则Elasticsearch需要无限的地址空间。最大的虚拟内存检查要求Elasticsearch进程具有无限的地址空间,并且只在Linux上进行检查。可以在/etc/security/limits.conf 配置 as 为 unlimited进行设置,或者也可以给用户root权限。
Ealsticsearch的分片是由大量的文件组成的,并且有些文件可能会超过千兆字节,所以在Es运行时可能会因为需要创建或写的文件过大而失败,所以启动时需要检查系统是否设置创建大文件的权限。可以通过/etc/security/limits.conf 配置的fsize 为 unlimited进行设置,当然还是可以给用户root权限。
在Linux系统下,Elasticsearch同样需要创建足够多(至少262,144)的memory-mapped(mmap)空间,可以使用vm.max_map_count 通过 sysctl进行配置。
OpenJDK提供了两种jvm,分别为client和server的jvm,由于两者需要做的事和性能要求是完全不同的,即提供的字节码也是不同的。所以在启动时需要检查一定为server类型的jvm。
Serial的垃圾回收器主要用户单核cpu或者比较小的堆(heap)的服务器,但是对于Elasticsearch来说这是致命的,确保你没有配置显示的配置serial的垃圾回收器如:-XX:+UseSerialGC,相对而言使用默认的CMS垃圾回收器对于Elasticsearch是比较友好的。
Elasticsearch被安装在不同的操作系统上,而操作系统会安装不同的System call filters系统调用过滤器,以防止安装的程序(现为Elasticsearch)执行forking 相关的操作,建立代码执行攻击的防御机制。如在Centos 6中由于没有seccomp ,所以直接导致es不能正常的启动。可以使用bootstrap.system_call_filter 为 false进行设置。
说明:Seccomp(secure computing)是Linux kernel(自从2.6.23版本之后)所支持的一种简洁的sandboxing机制。它能使一个进程进入到一种“安全”运行模式,该模式下的进程只能调用4种系统调用(system calls),即read(), write(), exit()和sigreturn(),否则进程便会被终止。
OnError 和OnOutOfMemoryError配置运行Jvm在遭遇到大量的错误或者内存溢出时能正常的运行,但是默认情况下操作系统的系统调用过滤器是开启的,而两者配置是不相容的,索引Es启动时需要检查两者不能同时进行配置。当然8u92版本jdk后使用ExitOnOutOfMemoryError。
OpenJDK 提供了比较新的snapshots (快照)版本,这不适用于生产环境,所以要求在生产上使用合适的版本以通过该检查。
使用JDK 8自带的HotSpot JVM的早期版本有一些问题,在启用G1GC收集器时可能会导致索引损坏,这也是致命的,所以需要检查这些版本中没有开启G1GC垃圾回收器。
最理想的情况是将Elasticsearch部署在单独的服务器上,但是非要和其他服务一起部署则要求es能够分配到足够多的资源。并且在开发模式和生成模式下的日志基本是完全不一样的,则下面的配置的重要性则尤为重要,可能直接影响到Es集群的性能。并且下面的配置完全针对于Centos系统。
配置系统的unlimit限制属性,可以使用临时设置(会话范围内有效),当然该配置一般需要root权限,所以一般配置步骤如下(可以使用ulimit -a进行查询):
-- 使用elasticsearch用户进行操作
sudo su
ulimit -n 65536
su elasticsearch
elasticsearch - nofile 65536
// 可以修改为 * - nofile 65536
如果使用rpm或者Debian 安装es,则可以使用systemd方式进行配置,limit配置在/usr/lib/systemd/system/elasticsearch.service中作为默认配置生效。但是可以修改或添加文件/etc/systemd/system/elasticsearch.service.d/override.conf(可以使用sudo systemctl edit elasticsearch命令修改),配置如下:
[Service]
LimitMEMLOCK=infinity
并且执行以下操作以生效:
sudo systemctl daemon-reload
jvm参数最好在jvm.options配置中进行设置,设置规则为在设置参数前加上-符号。
堆内存大小设置可以使用ES_HEAP_SIZE方式,或者在启动时使用(如:./bin/elasticsearch -Xmx10g -Xms10g),当前建议使用jvm.options方式进行设置,如:
-Xms2g
-Xmx2g
默认情况下,5.x版本的Elasticsearch的默认最大和最小内存都为2g,但是在生成环境下该参数的设置尤为重要,但是一般需要遵守以下几个设置原则:
一个常见的问题是给 Elasticsearch 分配的内存 太 大了。 假设你有一个 64 GB 内存的机器, 天啊,我要把 64 GB 内存全都给 Elasticsearch。因为越多越好啊!
当然,内存对于 Elasticsearch 来说绝对是重要的,它可以被许多内存数据结构使用来提供更快的操作。但是说到这里, 还有另外一个内存消耗大户 非堆内存 (off-heap):Lucene。
Lucene 被设计为可以利用操作系统底层机制来缓存内存数据结构。 Lucene 的段是分别存储到单个文件中的。因为段是不可变的,这些文件也都不会变化,这是对缓存友好的,同时操作系统也会把这些段文件缓存起来,以便更快的访问。
Lucene 的性能取决于和操作系统的相互作用。如果你把所有的内存都分配给 Elasticsearch 的堆内存,那将不会有剩余的内存交给 Lucene。 这将严重地影响全文检索的性能。
标准的建议是把 50% 的可用内存作为 Elasticsearch 的堆内存,保留剩下的 50%。当然它也不会被浪费,Lucene 会很乐意利用起余下的内存。
如果你不需要对分词字符串做聚合计算(例如,不需要 fielddata )可以考虑降低堆内存。堆内存越小,Elasticsearch(更快的 GC)和 Lucene(更多的内存用于缓存)的性能越好。
这里有另外一个原因不分配大内存给 Elasticsearch。事实上 , JVM 在内存小于 32 GB的时候会采用一个内存对象指针压缩技术。
在 Java 中,所有的对象都分配在堆上,并通过一个指针进行引用。 普通对象指针(OOP)指向这些对象,通常为CPU 字长 的大小:32位或64位,取决于你的处理器。指针引用的就是这个OOP值的字节位置。
对于 32 位的系统,意味着堆内存大小最大为4 GB。对于64位的系统, 可以使用更大的内存,但是64位的指针意味着更大的浪费,因为你的指针本身大了。更糟糕的是, 更大的指针在主内存和各级缓存(例如LLC,L1等)之间移动数据的时候,会占用更多的带宽。
Java 使用一个叫作 内存指针压缩(compressed oops)的技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示 偏移量 。这意味着 32 位的指针可以引用 40亿个 对象 , 而不是40 亿个字节。最终, 也就是说堆内存增长到32 GB的物理内存,也可以用32位的指针表示。
一旦你越过那个神奇的 ~32 GB 的边界,指针就会切回普通对象的指针。 每个对象的指针都变长了,就会使用更多的 CPU内存带宽,也就是说你实际上失去了更多的内存。事实上,当内存到达40–50 GB的时候,有效内存才相当于使用内存对象指针压缩技术时候的32 GB内存。
这段描述的意思就是说:即便你有足够的内存,也尽量不要 超过 32 GB。因为它浪费了内存,降低了 CPU的性能,还要让GC应对大内存。
那么怎么知道有没有超越该临界值,可以在日志中查找以下信息:
heap size [1.9gb], compressed ordinary object pointers [true]
既然要求堆内存为少于物理内存的一半,并且不超过阈值,而且在改范围内越大越好,那么知道系统的阈值是多少就显得特别重要,一般来说只要不超过26G就在阈值以下,但是有的阈值可以到达30g,所以需要显示的告诉我们系统的阈值。可以通过在配置中增加以下:
-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
大多数操作系统都尽量使用尽可能多的内存来存储文件系统缓存,并在超过一定值时自然的交换未使用的应用程序内存。这可能导致JVM堆的某些部分,甚至它的可执行页面被交换到磁盘(个人理解为当查过一定设置值时,内存中的数据被持久化或者说序列化),这样的方式对于一般的程序来说是很好的默认方式,但是对于需要性能的Elasticsearch来说是致命的。
应该不惜一切代价避免内存交换,它可以导致垃圾收集持续数分钟,而不是毫秒,并且可能导致节点响应缓慢,甚至断开与集群的连接。在es集群中的影响比操作系统杀死节更致命。
以下有三种方式用于完全禁用或者将内存交换控制在一定范围内:
由于操作系统中可能不止es服务,该操作为系统级别的禁止内存交换,会对所有的应用生效,临时禁用可以使用以下命令:
sudo swapoff -a
完全禁用需要注释掉/etc/fstab文件中将所有包含swap的行(但是我并没有在我的服务器上找到该配置)。
这降低了内核交换的倾向,并且不应该在正常情况下导致交换,同时仍然允许整个系统在紧急情况下交换。设置方式如下sysctl 中这样配置(设置为0等可能会有问题):
vm.swappiness = 1
使用 mlockall 在 Linux/Unix 操作系统中禁用内存交换,在elasticsearch.yml中增加配置(该配置启用后mlockall 会试图分配更多内存,mlockall可能会导致JVM或shell会话退出。我有遇到这样的问题,就是服务启动后通过service elasticsearch status查看是ok的,过几秒钟就发现没有启动起来):
bootstrap.memory_lock: true
可以通过以下方式查看是否启动禁用内存交换(false标识没有启动):
GET _nodes?filter_path=**.mlockall
不能正常启动有以下两个主要原因:
使用其他方式安装就不进行说了,有以下两种方式进解决:1、在/etc/security/limits.conf中增加配置MAX_LOCKED_MEMORY 为 unlimited 2、systemd方式,在中配置LimitMEMLOCK为infinity(最后使用该配置成功,生产环境中/etc/fstab下没有swap相关配置项,/etc/security/limits.conf中配置也不生效)详细如下:
查询结果,在kibana的DevTools中查询
GET _nodes?filter_path=**.mlockall
结果为:
{
"nodes": {
"tXfBk9rwQzi8pPhrFGlxJw": {
"process": {
"mlockall": true
}
},
"_EuJzUMlSCqwiHDXLh6aDA": {
"process": {
"mlockall": true
}
}
}
}
可以通过使用esjava选用环境变量指定一个新的临时目录来解决这一问题,如下:
export ES_JAVA_OPTS="$ES_JAVA_OPTS -Djava.io.tmpdir=/path/to/temp/dir"
./bin/elasticsearch
Elasticsearch使用大量的文件描述符或文件句柄。耗尽文件描述符可能是灾难性的,并且很可能导致数据丢失。一般需要设置65536或者更大,若使用RPM或者 Debian安装包则默认已经将该参数调整为65536.调整方式:将/etc/security/limits.conf配置文件的nofile 参数调整为 65536,可以使用es的Node Stats进行查看,如下:
GET _nodes/stats/process?filter_path=**.max_file_descriptors
结果如下:
{
"nodes": {
"tXfBk9rwQzi8pPhrFGlxJw": {
"process": {
"max_file_descriptors": 65536
}
},
"_EuJzUMlSCqwiHDXLh6aDA": {
"process": {
"max_file_descriptors": 65536
}
}
}
}
在默认情况下,Es使用mmapfs目录来存储它的索引,操作系统默认的mmap计数限制可能过低,这可能导致内存异常。当然RPM和Debian软件包将自动配置此设置,不需要进一步的配置。可以使用以下方式临时设置:
sysctl -w vm.max_map_count=262144
当然一般需要在配置文件/etc/sysctl.conf修改vm.max_map_count属性的值,并且需要长期服务器并且使用sysctl vm.max_map_count命令查询。
Elasticsearch的各种类型的操作需要交给不同的线程池完成,要求能够在需要的时候创建新的线程,至少2048。可以使用ulimit -u 2048临时设置,若需要永久调整,需要也在配置文件/etc/security/limits.conf.中将nproc参数修改为不小于 2048
那么具体的线程应该设置多大,这个也是需要关注的,描述如下:
许多人 喜欢 调整线程池。 无论什么原因,人们都对增加线程数无法抵抗。索引太多了?增加线程!搜索太多了?增加线程!节点空闲率低于 95%?增加线程!
Elasticsearch 默认的线程设置已经是很合理的了。对于所有的线程池(除了 搜索 ),线程个数是根据 CPU 核心数设置的。 如果你有 8 个核,你可以同时运行的只有 8 个线程,只分配 8 个线程给任何特定的线程池是有道理的。
搜索线程池设置的大一点,配置为 int(( 核心数 * 3 )/ 2 )+ 1 。
你可能会认为某些线程可能会阻塞(如磁盘上的 I/O 操作),所以你才想加大线程的。对于 Elasticsearch 来说这并不是一个问题:因为大多数 I/O 的操作是由 Lucene 线程管理的,而不是 Elasticsearch。
此外,线程池通过传递彼此之间的工作配合。你不必再因为它正在等待磁盘写操作而担心网络线程阻塞, 因为网络线程早已把这个工作交给另外的线程池,并且网络进行了响应。
最后,你的处理器的计算能力是有限的,拥有更多的线程会导致你的处理器频繁切换线程上下文。 一个处理器同时只能运行一个线程。所以当它需要切换到其它不同的线程的时候,它会存储当前的状态(寄存器等等),然后加载另外一个线程。 如果幸运的话,这个切换发生在同一个核心,如果不幸的话,这个切换可能发生在不同的核心,这就需要在内核间总线上进行传输。
这个上下文的切换,会给 CPU 时钟周期带来管理调度的开销;在现代的 CPUs 上,开销估计高达 30 μs。也就是说线程会被堵塞超过 30 μs,如果这个时间用于线程的运行,极有可能早就结束了。
人们经常稀里糊涂的设置线程池的值。8 个核的 CPU,我们遇到过有人配了 60、100 甚至 1000 个线程。 这些设置只会让 CPU 实际工作效率更低。
所以,下次请不要调整线程池的线程数。如果你真 想调整 , 一定要关注你的 CPU 核心数,最多设置成核心数的两倍,再多了都是浪费。
Es的启动方式有很多种,当然停止的方式也相对应,但是建议使用service elasticsearch stop进行停止。但是当服务运行的时候可以回遇到一些不期而遇的问题导致服务不可用,那么服务本身会尝试将错误日志进行记录,下面是常见的错误的其编码对比: