本套技术专栏做者(秦凯新)专一于大数据及容器云核心技术解密,具有5年工业级IOT大数据云平台建设经验,可提供全栈的大数据+云原平生台咨询方案,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何学术交流,可随时联系。java
es安装包的目录结构大体以下:node
默认状况下,es进程会绑定在本身的回环地址上,也就是127.0.0.1,而后扫描本机上的9300~9305端口号,尝试跟那些端口上启动的其余es进程进行通讯,而后组成一个集群。这对于在本机上搭建es集群的开发环境是很方便的。可是对于生产环境下的集群是不行的,须要将每台es进程绑定在一个非回环的ip地址上,才能跟其余节点进行通讯,同时须要使用集群发现机制来跟其余节点上的es node进行通讯。linux
elasticsearch.yml
network.host:服务主机ip
http.port:9200
复制代码
在生产环境中的多台机器上部署es集群,就涉及到了es的discovery机制,也就是集群中各个节点互相发现而后组成一个集群的机制,同时discovery机制也负责es集群的master选举。算法
ES是一种peer to peer,也就是p2p点对点的分布式系统架构,不是hadoop生态广泛采用的那种master-slave主从架构的分布式系统。集群中的每一个node是直接跟其余节点进行通讯的,而不是hadoop生态系统中的那种master-slave分布式系统架构。几乎全部的API操做,好比index,delete,search,等等,都不是说client跟master通讯,而是client跟任何一个node进行通讯,那个node再将请求转发给对应的node来进行执行。apache
两个角色,master node,data node。正常状况下,就只有一个master node。master node的责任就是负责维护整个集群的状态信息,也就是一些集群元数据信息,同时在node加入集群或者从集群中下线时,从新分配shard,或者是建立或删除了一个索引。包括每次cluster state若是有改变的化,那么master都会负责将集群状态同步给全部的node。bootstrap
master node负责接收全部的cluster state相关的变化信息,而后将这个改变后的最新的cluster state推进给集群中全部的data node,集群中全部的node都有一份完整的cluster state。只不过master node负责维护而已。其余的node,除了master以外的node,就是负责数据的存储和读写的,写入索引,搜索数据,data node。api
若是要让多个node组成一个es集群,首先第一个要设置的参数,就是cluster.name,多个node的cluster.name若是同样,才知足组成一个集群的基本条件。这个cluster.name的默认值是elasticsearch,在生产环境中,必定要修改这个值,不然可能会致使未知的node无故加入集群,形成集群运行异常。缓存
elasticsearch.yml
cluster.name:cluster-elasticearch-prod
node.name:node-01
复制代码
而es中默认的discovery机制,就是zen discovery机制,zen discovery机制提供了unicast discovery集群发现机制,集群发现时的节点间通讯是依赖的transport module,也就是es底层的网络通讯模块和协议。安全
es默认配置为使用unicast集群发现机制,以让通过特殊配置的节点能够组成一个集群,而不是随便哪一个节点均可以组成一个集群。可是默认配置下,unicast是本机,也就是localhost,所以只能在一台机器上启动多个node来组成一个集群。bash
虽然es仍是会提供multicast plugin做为一个发现机制,可是已经不建议在生产环境中使用了。虽然咱们可能想要multicast的简单性,就是全部的node能够再接收到一条multicast ping以后就当即自动加入集群。可是multicast机制有不少的问题,并且很脆弱,好比网络有轻微的调整,就可能致使节点没法发现对方。
所以如今建议在生产环境中用unicast机制,提供一个es种子node做为中转路由节点就能够了。
给集群规划出专门的master eligible node和data node,
master node,master eligible node,data node
你配置的时候,是配置多个node变成master eligible node,可是只是说,从这些master eligible node选举一个node出来做为master node,其余master eligible node只是接下来有那个master node故障的时候,接替他的资格,可是仍是做为data node去使用的
通常建议master eligible node给3个便可:node.master: true,node.data: false 剩下的node都设置为data node:node.master: false,node.data: true
可是若是一个小集群,就10个之内的节点,那就全部节点均可以做为master eligible node以及data node便可,超过10个node的集群再单独拆分master和data node吧,若是你的节点数量小于10个,小集群,那全部的node,就不要作额外的配置了,master eligible node,同时也是data node
默认状况下,es会将本身绑定到127.0.0.1上,对于运行一个单节点的开发模式下的es是ok的。可是为了让节点间能够互相通讯以组成一个集群,须要让节点绑定到一个ip地址上,非会换的地址,通常会配置:network.host: 192.168.1.10。一旦咱们配置了network.host,那么es就会认为咱们从开发模式迁移到生产模式,同时会启用一系列的bootstrap check。
本套技术专栏做者(秦凯新)专一于大数据及容器云核心技术解密,具有5年工业级IOT大数据云平台建设经验,可提供全栈的数据云平台咨询方案,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何学术交流,可随时联系。
ping是一个node用discovery机制来发现其余node的一个过程。
unicast discovery集群发现机制是要求配置一个主机列表,用来做为gossip(流言式)通讯协议的路由器。这些机器若是经过hostname来指定,那么在ping的时候会被解析为ip地址。unicast discovery机制最重要的两个配置以下所示:
hosts:用逗号分割的主机列表
hosts.resolve_timeout:hostname被DNS解析为ip地址的timeout等待时长
简单来讲,若是要让多个节点发现对方而且组成一个集群,那么就得有一个中间的公共节点,而后不一样的节点就发送请求到这些公共节点,接着经过这些公共节点交换各自的信息,进而让全部的node感知到其余的node存在,而且进行通讯,最后组成一个集群。这就是基于gossip流言式通讯协议的unicast集群发现机制。
当一个node与unicast node list中的一个成员通讯以后,就会接收到一份完整的集群状态,这里会列出集群中全部的node。接着那个node再经过cluster state跟master通讯,而且加入集群中。这就意味着,咱们的unicast list node是不须要列出集群中的全部节点的。只要提供少数几个node,好比3个,让新的node能够链接上便可。若是咱们给集群中分配了几个节点做为专门的master节点,那么只要列出咱们那三个专门的master节点便可。
elasticsearch.yml
discovery.zen.ping.unicast.hosts: ["host1", "host2"]
复制代码
(1)初步配置好后,各个节点,首先经过network.host绑定到了非回环的ip地址,从而能够跟其余节点通讯
(2)经过discovery.zen.ping.unicast.hosts配置了一批unicast中间路由的node
(3)全部node均可以发送ping消息到路由node,再从路由node获取cluster state回来
(4)接着全部node会选举出一个master
(5)全部node都会跟master进行通讯,而后加入master的集群
(6)要求cluster.name必须同样,才能组成一个集群
(7)node.name就标识出了每一个node咱们本身设置的一个名称
复制代码
在ping发现过程当中,为集群选举出一个master也是很重要的,es集群会自动完成这个操做。这里建议设置discovery.zen.ping_timeout参数(默认是3s),若是由于网络慢或者拥塞,致使master选举超时,那么能够增长这个参数,确保集群启动的稳定性。
在完成一个集群的master选举以后,每次一个新的node加入集群,都会发送一个join request到master node,能够设置discovery.zen.join_timeout保证node稳定加入集群,增长join的timeout等待时长,若是一次join不上,默认会重试20次。
若是master node被中止了,或者本身宕机了,那么集群中的node会再次进行一次ping过程,而且选举出一个新的master。若是discovery.zen.master_election.ignore_non_master_pings设置为了true,那么会强制区分master候选节点,若是node的node.master设置为了false,还来发送ping请求参与master选举,那么这些node会被忽略掉,由于他们没有资格参与。
discovery.zen.minimum_master_nodes参数用于设置对于一个新选举的master,要求必须有多少个master候选node去链接那个新选举的master。并且还用于设置一个集群中必须拥有的master候选node。若是这些要求没有被知足,那么master node就会被中止,而后会从新选举一个新的master。这个参数必须设置为咱们的master候选node的quorum数量。通常避免说只有两个master候选node,由于2的quorum仍是2。若是在那个状况下,任何一个master候选节点宕机了,集群就没法正常运做了。
本套技术专栏做者(秦凯新)专一于大数据及容器云核心技术解密,具有5年工业级IOT大数据云平台建设经验,可提供全栈的大数据+云原平生台咨询方案,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何学术交流,可随时联系
es有两种集群故障探查机制,第一种是经过master进行的,master会ping集群中全部的其余node,确保它们是不是存活着的。第二种,每一个node都会去ping master node来确保master node是存活的,不然就会发起一个选举过程。
有下面三个参数用来配置集群故障的探查过程:
ping_interval:每隔多长时间会ping一次node,默认是1s
ping_timeout:每次ping的timeout等待时长是多长时间,默认是30s
ping_retries:若是一个node被ping多少次都失败了,就会认为node故障,默认是3次
复制代码
master node是集群中惟一一个能够对cluster state进行更新的node。master node每次会处理一个集群状态的更新事件,应用此次状态更新,而后将更新后的状态发布到集群中全部的node上去。每一个node都会接收publish message,ack这个message,可是不会应用这个更新。若是master没有在discovery.zen.commit_timeout指定的时间内(默认是30s),从至少discovery.zen.minimum_master_nodes个节点获取ack响应,那么此次cluster state change事件就会被reject,不会应用。
可是一旦在指定时间内,指定数量的node都返回了ack消息,那么cluster state就会被commit,而后一个message会被发送给全部的node。全部的node接收到那个commit message以后,接着才会将以前接收到的集群状态应用到本身本地的状态副本中去。
接着master会等待全部节点再次响应是否更新本身本地副本状态成功,在一个等待超时时长内,若是接收到了响应,那么就会继续处理内存queue中保存的下一个更新状态。discovery.zen.publish_timeout默认是30s,这个超时等待时长是从plublish cluster state开始计算的。
若是要让集群正常运转,那么必须有一个master,还有discovery.zen.minimum_master_nodes指定数量的master候选node,都在运行。discovery.zen.no_master_block能够控制当master宕机时,什么样的操做应该被拒绝。有下面两个选项:
all:一旦master宕机,那么全部的操做都会被拒绝
write:这是默认的选项,全部的写操做都会被拒绝,可是读操做是被容许的
复制代码
默认状况下,es会启动一个名称为elasticsearch的集群。一般建议必定要将本身的集群名称从新进行命名,主要是避免公司网络环境中,也许某个开发人员的开发机会无心中加入你的集群。好比说将你的集群名称命名为elasticsearch_production。在elasticsearch.yml中,能够设置集群名称:cluster.name: elasticsearch_production。
es的默认参数是很是好的,适合绝大多数的状况,尤为是一些性能相关的配置。所以刚开始部署一个生产环境下的es集群时,几乎全部的配置参数均可以用默认的设置。有不少的生产环境场景下,都是由于es集群管理人员本身去调整es的某些配置,结果致使集群出现了严重的故障,那些es集群管理员甚至还觉得作出那些调节能够将es性能提高一百倍以上。可是,采用SSD盘不会差。
默认状况下,es会将plugin,log,还有data ,config,file都放在es的安装目录中。这有一个问题,就是在进行es升级的时候,可能会致使这些目录被覆盖掉。致使咱们丢失以前安装好的plugin,已有的log,还有已有的数据,以及配置好的配置文件。
因此通常建议在生产环境中,必须将这些重要的文件路径,都从新设置一下,放在es安装目录以外。path.data用于设置数据文件的目录,path.logs用于设置日志文件的目录,path.plugins用于设置插件存放的目录。
path.data能够指定多个目录,用逗号分隔便可。若是多个目录在不一样的磁盘上,那么这就是一个最简单的RAID 0的方式,将数据在本地进行条带化存储了,能够提高总体的磁盘读写性能。es会自动将数据在多个磁盘的多个目录中条带化存储数据。
通常建议的目录地址是:
mkdir -p /var/log/elasticsearch
mkdir -p /var/data/elasticsearch
mkdir -p /var/plugin/elasticsearch
mkdir -p /etc/elasticsearch
elasticsearch.yml
path.logs: /var/log/elasticsearch
path.data: /var/data/elasticsearch
path.plugins: /var/plugin/elasticsearch
config:/etc/elasticsearch
复制代码
在RAID 0的存储级别下,每一个磁盘上会存储一部分数据,可是若是一个磁盘故障了,那么可能致使这台机器上的部分数据就丢失了。若是咱们的es是有replica的,那么在其余机器上仍是会有一份副本的。
若是data file指定了多个目录,为了尽可能减小数据丢失的风险,es会将某个shard的数据都分配到一个磁盘上去。这就意味着每一个shard都仅仅会放在一个磁盘上。es不会将一个shard的数据条带化存储到多个磁盘上去,由于若是一个磁盘丢失了,就会致使整个shard数据丢失。
可是这又引入了性能的问题,若是咱们给一个机器添加更多的磁盘来提高单个索引的读写性能,是没有效果的。由于这个索引在这个机器上的shard仅仅存在于一个磁盘上。所以data file指定多个目录,仅仅对于你的一台机器上存储了多个index的多个shard时,才会有效果的。由于不一样index的shard可能就被存储到不一样的磁盘上去了,对多个index的shard读写能够走不一样磁盘,提高了性能。
虽然multiple data path是一个颇有用的功能,可是es毕竟不是一个专门的RAID软件。若是咱们要对RAID存储策略进行更多的配置,提升存储的健壮性以及灵活性,仍是要用专门的RAID软件来进行机器的磁盘数据存储,而不是用multiple data path策略。综上所述,multiple data path功能在实际的生产环境中,实际上是较少使用的。
es有两个配置文件,elasticsearch.yml,用于配置es,还有一个log4j.properties用来配置es日志打印。这些文件都被放在config目录下,默认就是ES_HOME/config。能够经过下面的命令来从新设置:
./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
复制代码
es使用log4j2来记录日志,log4j2能够经过log4j2.properties文件来进行配置。好比下面的这份配置文件:
appender.rolling.type = RollingFile
appender.rolling.name = rolling
appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log
appender.rolling.layout.type = PatternLayout
appender.rolling.layout.pattern = [%d{ISO8601}][%-5p][%-25c] %.10000m%n
appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}.log
appender.rolling.policies.type = Policies
appender.rolling.policies.time.type = TimeBasedTriggeringPolicy
appender.rolling.policies.time.interval = 1
appender.rolling.policies.time.modulate = true
appender.rolling.type = RollingFile,就配置了appender类型是RollingFile
appender.rolling.fileName = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}.log,就配置了日志路径是/var/log/elasticsearch/production.log
appender.rolling.filePattern = ${sys:es.logs.base_path}${sys:file.separator}${sys:es.logs.cluster_name}-%d{yyyy-MM-dd}.log,就配置了将日志天天写一份到/var/log/elasticsearch/production-2017-01-01.log文件中
appender.rolling.policies.time.type = TimeBasedTriggeringPolic,这里配置了用基于时间的roll策略
appender.rolling.policies.time.interval = 1,这个设置了天天一份日志文件
appender.rolling.policies.time.modulate = true,这个设置了根据天然天来划分文件,而不是24小时
复制代码
还能够配置将日志文件保留一段时间内,同时删除以前的日志文件
appender.rolling.strategy.type = DefaultRolloverStrategy
appender.rolling.strategy.action.type = Delete
appender.rolling.strategy.action.basepath = ${sys:es.logs.base_path}
appender.rolling.strategy.action.condition.type = IfLastModified
appender.rolling.strategy.action.condition.age = 7D
appender.rolling.strategy.action.PathConditions.type = IfFileName
appender.rolling.strategy.action.PathConditions.glob = ${sys:es.logs.cluster_name}-*
第一行是配置了默认的DefaultRolloverStrategy
第二行是配置了Delete action,在rollover以后,就会删除文件
第三行是配置了es log的基础路径
第四行是配置了rollover发生的条件,是基于IfLastModified
第五行是配置了保留的天数,这里是7天
第六行是配置了删除匹配7天前的文件
第七行是配置了一个删除文件的格式,这样就只是删除过时日志文件,可是不要删除慢查询日志
复制代码
discovery.zen.minimum_master_nodes参数对于集群的可靠性来讲,是很是重要的。这个设置能够预防脑裂问题。若是由于网络的故障,致使一个集群被划分红了两片,每片都有多个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
综上所述,一个生产环境的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从新复制,移动,删除,再次移动的过程,会大量的耗费网络和磁盘资源。对于数据量庞大的集群来讲,可能致使每次集群重启时,都有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过程从数小时缩短为数分钟。
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 2.x里面,通常推荐用ES_HEAP_SIZE环境变量的方式来设置jvm heap size。
在新版本的es中,好比es 5.x里面,通常推荐在jvm.options文件里面去设置jvm相关的参数。
虽然heap对于es来讲是很是重要的,jvm heap被es用来存放不少内存中的数据结构来提供更快的操做性能。可是还有另一个内存的用户,那就是lucene。lucene的设计就是要使用底层的os filesystem cache来缓存数据结构。lucene的segment是保存在单独的文件中的。由于这些segment是不可变的,因此这些文件实际上也历来不会改变。这样的话,就能够更好的缓存这些文件,底层的os cache会将hot segment驻留在内存中以供更快的访问。这些segment包括了倒排索引(为了全文检索)以及正排索引(为了聚合操做)。lucene的性能是严重依赖于底层的os的,可是若是咱们给了过多的内存到es的jvm heap,那么就没有足够的内存留给lucene。这会极大的影响性能。
通常建议的是,将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耗时会更少。
不要给jvm分配超过32G内存,若是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能够耗费更少的内存耗费。
越过了32G这个界限,就是给jvm heap分配了超过32G的内存,由于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,几乎是同样的。
在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]。
复制代码
对于有1TB内存的超大内存机器该如何分配?(通常64G)
若是咱们的机器是一台超级服务器,内存资源甚至达到了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。
复制代码
若是频繁的将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。
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。
1)修改当前交互终端的limit值
查询当前终端的文件句柄数: ulimit -n 回车,通常的系统默认的1024.
修改文件句柄数为65535,ulimit -n 65535.此时系统的文件句柄数为65535.
2)将ulimit 值添加到/etc/profile文件中(适用于有root权限登陆的系统)
为了每次系统从新启动时,均可以获取更大的ulimit值,将ulimit 加入到/etc/profile 文件底部。
echo ulimit -n 65535 >>/etc/profile
source /etc/profile #加载修改后的profile
ulimit -n #显示65535,修改完毕!
3)OK,好多朋友都觉得大功告成了,能够忽然发现本身再次登陆进来的时候,ulimit的值仍是1024,这是为何呢?
关键的缘由是你登陆的用户是什么身份,是否是root用户,因为服务器的root用户权限很大,通常是不能用来登陆的,都是经过本身本人的登陆权限进行登陆,并经过sudo方式切换到root用户下进行工做。 用户登陆的时候执行sh脚本的顺序:
/etc/profile.d/file
/etc/profile
/etc/bashrc
/mingjie/.bashrc
/mingjie/.bash_profile
因为ulimit -n的脚本命令加载在第二部分,用户登陆时因为权限缘由在第二步还不能完成ulimit的修改,因此ulimit的值仍是系统默认的1024。
解决办法:
修改linux的软硬件限制文件/etc/security/limits.conf.
在文件尾部添加以下代码:
* soft nofile 10240
* hard nofile 10240
4) /etc/security/limits.conf
elasticsearch hard nofile 65536
5) 能够用上面这行代码检查每一个node上的file descriptor数量
GET _nodes/stats/process?filter_path=**.max_file_descriptors
{
"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
}
}
}
}
}
复制代码
es使用mmap来将索引映射到es的address space中,这可让jvm heap外可是内存中的索引数据,能够有很是高速的读写速度。所以es须要拥有unlimited address space。最大虚拟内存大小的检查,会要求es进程有unlimited address space。
/etc/security/limits.conf,设置as为unlimited
elasticsearch hard as unlimited
复制代码
若是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便可完成受权
elasticsearch hard memlock unlimited
elasticsearch soft 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中设置这个参数
复制代码
在生产环境中,会使用daemon进程的方式来启动es,而不是直接采用前台进程的方式来启动es,具体命令以下
./bin/elasticsearch -d -p pid
上面命令中的-d option用来指定es以daemon进程方式启动,
而且-p option指定将进程id记录在指定文件中
复制代码
es启动后,日志信息能够在ES_HOME/logs目录中查看
启动es进程的时候,还能够直接覆盖一些配置,使用-E便可,以下面的命令,一般用于调试集群参数时,方便快速调节参数,查看效果。
es是禁止用root用户去启动es进程,能够加一个配置来容许用root去启动。紧急修复方法:
adduser elasticsearch
passwd elasticsearch
chown -R elasticsearch /usr/local/elasticsearch
chown -R elasticsearch /var/log/elasticsearch
chown -R elasticsearch /var/data/elasticsearch
chown -R elasticsearch /var/plugin/elasticsearch
chown -R elasticsearch /etc/elasticsearch
chown -R elasticsearch /usr/local/tmp
su elasticsearch
./elasticsearch -d -Epath.conf=/etc/elasticsearch
复制代码
通常建议在管理机上安装一个curl工具,能够手工发送rest api请求,能够对启动了es的节点的9200端口,发送一个GET /请求,能够看看es是否启动成功
curl -XGET elasticsearch02:9200
curl -XGET elasticsearch02:9200/_cat/nodes?v
复制代码
优雅的关闭es,能够确保es关闭的很干净,而且优雅关闭资源。举例来讲,若是node在一个合理的顺序下关闭了,首先会将本身从cluster中优雅移除,fsync translog日志到磁盘中去,而后执行其余相关的cleanup活动。
若是将es用service的方式来运行,那么能够经过server管理功能来中止es。
若是是直接启动es的,能够control-C中止es,或者是发送SEGTERM信号给es进程
jps | grep Elasticsearch
kill -SIGTERM 15516
复制代码
ES replica提供了运行时的高可用保障机制,能够容忍少数节点的故障和部分数据的丢失,可是总体上却不会丢失任何数据,并且不会影响集群运行。可是replica无法进行灾难性的数据保护,好比说机房完全停电,全部机器所有立即,等等状况。对于这种灾难性的故障,咱们就须要对集群中的数据进行备份了,集群中数据的完整备份。
要备份集群数据,就要使用snapshot api。这个api会将集群当前的状态和数据所有存储到一个外部的共享目录中去,好比NAS,或者hdfs。并且备份过程是很是智能的,第一次会备份全量的数据,可是接下来的snapshot就是备份两次snapshot之间的增量数据了。数据是增量进入es集群或者从es中删除的,那么每次作snapshot备份的时候,也会自动在snapshot备份中增量增长数据或者删除部分数据。所以这就意味着每次增量备份的速度都是很是快的。
若是要使用这个功能,咱们须要有一个预先准备好的独立于es以外的共享目录,用来保存咱们的snapshot备份数据。es支持多种不一样的目录类型:shared filesystem,好比NAS;Amazon S3;hdfs;Azure Cloud。不过对于国内的状况而言,其实NAS应该不多用,通常来讲,就用hdfs会比较多一些,跟hadoop这种离线大数据技术栈整合起来使用。
shared filesystem做为仓库类型,包括了仓库名称以及仓库类型是fs,还有仓库的地址。这个里面就包含了仓库的一些必要的元数据了。可能还有其余的一些参数能够配置,主要是基于咱们的node和网络的性能来配置。max_snapshot_bytes_per_sec,这个参数用于指定数据从es灌入仓库的时候,进行限流,默认是20mb/s。max_restore_bytes_per_sec,这个参数用于指定数据从仓库中恢复到es的时候,进行限流,默认也是20mb/s。假如说网络是很是快速的,那么能够提升这两个参数的值,能够加快每次备份和恢复的速度,好比下面:
POST _snapshot/my_backup/
{
"type": "fs",
"settings": {
"location": "/mount/backups/my_backup",
"max_snapshot_bytes_per_sec" : "50mb",
"max_restore_bytes_per_sec" : "50mb"
}
}
复制代码
建立一个仓库以后,就能够查看这个仓库的信息了:GET /_snapshot/my_backup,或者是查看全部的仓库,GET /_snapshot/_all。可能返回以下的信息:
{
"my_backup": {
"type": "fs",
"settings": {
"compress": true,
"location": "/mount/backups/my_backup"
}
}
}
复制代码
可是其实若是在国内使用es的话,仍是建议跟hadoop生态整合使用,不要用那种shared filesystem。能够用hadoop生态的hdfs分布式文件存储系统。首先先要安装repository-hdfs的插件:bin/elasticsearch-plugin install repository-hdfs,必须在每一个节点上都安装,而后重启整个集群。
kill -SIGTERM 15516
su elasticsearch
elasticsearch -d -Epath.conf=/etc/elasticsearch
curl -XGET elasticsearch02:9200/_cat/nodes?v
复制代码
在3个hdfs node上,都加入hdfs-site.xml,禁止权限检查,若是要修改这个配置文件,要先在/usr/local/hadoop/sbin,运行./stop-dfs.sh,中止整个hdfs集群,而后在3个node上,都修改hdfs-site.xml,加入下面的配置,禁止权限的检查
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
复制代码
hdfs snapshot/restore plugin是跟最新的hadoop 2.x整合起来使用的,目前是hadoop 2.7.1。因此若是咱们使用的hadoop版本跟这个es hdfs plugin的版本不兼容,那么考虑在hdfs plugin的文件夹里,将hadoop相关jar包都替换成咱们本身的hadoop版本对应的jar包。即便hadoop已经在es所在机器上也安装了,可是为了安全考虑,仍是应该将hadoop jar包放在hdfs plugin的目录中。
安装好了hdfs plugin以后,就能够建立hdfs仓库了,用以下的命令便可:
curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}
'
curl -XPUT 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository' -d '
{
"type": "hdfs",
"settings": {
"uri": "hdfs://elasticsearch02:9000/",
"path": "elasticsearch/respositories/my_hdfs_repository",
"conf.dfs.client.read.shortcircuit": "false",
"max_snapshot_bytes_per_sec" : "50mb",
"max_restore_bytes_per_sec" : "50mb"
}
}'
复制代码
验证仓库,若是一个仓库被建立好以后,咱们能够当即去验证一下这个仓库是否能够在全部节点上正常使用。verify参数均可以用来作这个事情,好比下面的命令。这个命令会返回一个node列表,证实那些node都验证过了这个仓库是ok的,可使用的
curl -XPOST 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository/_verify'
三个节点追加相关受权,才能验证经过:
/usr/local/elasticsearch/plugins/repository-hdfs/plugin-security.policy
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "getClassLoader";
permission java.lang.RuntimePermission "shutdownHooks";
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission javax.security.auth.AuthPermission "doAs";
permission javax.security.auth.AuthPermission "getSubject";
permission javax.security.auth.AuthPermission "modifyPrivateCredentials";
permission java.security.AllPermission;
permission java.util.PropertyPermission "*", "read,write";
permission javax.security.auth.PrivateCredentialPermission "org.apache.hadoop.security.Credentials * \"*\"", "read";
三个节点追加相关受权,才能验证经过:
/usr/local/elasticsearch/config/jvm.options
-Djava.security.policy=file:////usr/local/elasticsearch/plugins/repository-hdfs/plugin-security.policy
复制代码
一个仓库能够包含多分snapshot,每一个snapshot是一部分索引的备份数据,建立一份snapshot备份时,咱们要指定要备份的索引。好比下面这行命令:
PUT _snapshot/my_hdfs_repository/snapshot_1
这行命令就会将全部open的索引都放入一个叫作snapshot_1的备份,而且放入my_backup仓库
中。这个命令会当即返回,而后备份操做会被后台继续进行。
复制代码
若是咱们不但愿备份操做之后台方式运行,而是但愿在前台发送请求时等待备份操做执行完成,那么能够加一个参数便可,好比下面这样:
PUT _snapshot/my_backup/snapshot_1?wait_for_completion=true。
执行备份:
curl -XPUT 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository/snapshot_1'
复制代码
对指定的索引进行snapshotting备份,默认的备份是会备份全部的索引,可是有的时候,可能咱们不但愿备份全部的索引,有些多是不重要的数据,并且量很大,没有必要占用咱们的hdfs磁盘资源,那么能够指定备份少数重要的数据便可。此时可使用下面的命令去备份指定的索引:
PUT _snapshot/my_backup/snapshot_2
{
"indices": "index_1,index_2",
"ignore_unavailable": true,
"include_global_state": false,
"partial": true
}
ignore_unavailable若是设置为true的话,那么那些不存在的index就会被忽略掉,不会进行备份过程当中。默认状况下,这个参数是不设置的,那么此时若是某个index丢失了,会致使备份过程失败。设置include_global_state为false,能够阻止cluster的全局state也做为snapshot的一部分被备份。默认状况下,若是某个索引的部分primary shard不可用,那么会致使备份过程失败,那么此时能够将partial设置为true。
复制代码
snapshotting的过程是增量进行的,每次执行snapshotting的时候,es会分析已经存在于仓库中的snapshot对应的index file,而后仅仅备份那些自从上次snapshot以后新建立的或者有过修改的index files。这就容许多个snapshot在仓库中能够用一种紧凑的模式来存储。并且snapshotting过程是不会阻塞全部的es读写操做的,然而,在snapshotting开始以后,写入index中的数据,是不会反应到此次snapshot中的。每次snapshot除了建立一份index的副本以外,还能够保存全局的cluster元数据,里面包含了全局的cluster设置和template。
每次只能执行一次snapshot操做,若是某个shard正在被snapshot备份,那么这个shard此时就不能被移动到其余node上去,这会影响shard rebalance的操做。只有在snapshot结束以后,这个shard才可以被移动到其余的node上去。
一旦咱们在仓库中备份了一些snapshot以后,就能够查看这些snapshot相关的详细信息了,使用这行命令就能够查看指定的snapshot的详细信息:
GET _snapshot/my_backup/snapshot_2
复制代码
,固然也能够查看全部的snapshot列表,GET _snapshot/my_backup/_all。
curl -XGET 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository/snapshot_1?pretty'
备份列表:
{
"snapshots" : [
{
"snapshot" : "snapshot_1",
"uuid" : "x8DXcrp2S0md-BC9ftYZqw",
"version_id" : 5050099,
"version" : "5.5.0",
"indices" : [
"my_index"
],
"state" : "SUCCESS",
"start_time" : "2017-07-08T19:54:54.914Z",
"start_time_in_millis" : 1499543694914,
"end_time" : "2017-07-08T19:54:56.886Z",
"end_time_in_millis" : 1499543696886,
"duration_in_millis" : 1972,
"failures" : [ ],
"shards" : {
"total" : 5,
"failed" : 0,
"successful" : 5
}
}
]
}
复制代码
若是要删除过于陈旧的snapshot备份快照,那么使用下面这行命令便可:DELETE _snapshot/my_backup/snapshot_2。记住,必定要用api去删除snapshot,不要本身手动跑到hdfs里删除这个数据。由于snapshot是增量的,有可能不少snapshot依赖于底层的某一个公共的旧的snapshot segment。可是delete api是理解数据如何增量存储和互相依赖的,因此能够正确的删除那些不用的数据。若是咱们本身手工进行hdfs文件删除,可能致使咱们的backup数据破损掉,就没法使用了。
curl -XDELETE 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository/snapshot_1'
复制代码
使用wait_for_completion能够在前台等待备份完成,可是实际上也没什么必要,由于可能要备份的数据量特别大,难道还等待1个小时??看着是不太现实的,因此通常仍是在后台运行备份过程,而后使用另一个监控api来查看备份的进度,首先能够获取一个snapshot ID:GET _snapshot/my_backup/snapshot_3。若是这个snapshot还在备份过程当中,此时咱们就能够看到一些信息,好比何时开始备份的,已经运行了多长时间,等等。然而,这个api用了跟snapshot同样的线程池去执行,若是咱们在备份很是大的shard,进度的更新可能会很是之慢。 一个更好的选择是用_status API,
GET _snapshot/my_backup/snapshot_3/_status,
复制代码
这个api当即返回最详细的数据。这里咱们能够看到总共有几个shard在备份,已经完成了几个,还剩下几个,包括每一个索引的shard的备份进度:
取消一个正在执行的snapshotting备份过程,好比咱们发现备份时间过于长,但愿先取消而后在晚上再运行,或者是由于不当心误操做发起了一次备份操做,这个时候就能够运行下面这条命令:DELETE _snapshot/my_backup/snapshot_3。也就是当即删除这个snapshot,这个命令会去取消snapshot的过程,同时将备份了一半的仓库中的数据给删除掉。
curl -XDELETE 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository/snapshot_1'
复制代码
在es集群故障,致使数据丢失的时候,就能够用_restore api进行数据恢复了。好比下面这行命令:POST _snapshot/my_hdfs_repository/snapshot_1/_restore。这个时候,会将那个snapshot中的全部数据恢复到es中来,若是snapshot_1中包含了5个索引,那么这5个索引都会恢复到集群中来。不过咱们也能够选择要从snapshot中恢复哪几个索引。
咱们还能够经过一些option来重命名索引,恢复索引的时候将其重命名为其余的名称。在某些场景下,好比咱们想恢复一些数据可是不要覆盖现有数据,而后看一下具体状况,用下面的命令便可恢复数据,而且进行重命名操做:
POST /_snapshot/my_hdfs_repository/snapshot_1/_restore
{
"indices": "index_1",
"ignore_unavailable": true,
"include_global_state": true,
"rename_pattern": "index_(.+)",
"rename_replacement": "restored_index_$1"
}
复制代码
还能够在恢复的过程当中,修改index的一些设置,好比下面的命令:
POST /_snapshot/my_backup/snapshot_1/_restore
{
"indices": "index_1",
"index_settings": {
"index.number_of_replicas": 0
},
"ignore_index_settings": [
"index.refresh_interval"
]
}
curl -XDELETE 'http://elasticsearch02:9200/my_index?pretty'
curl -XGET 'http://elasticsearch02:9200/my_index/my_type/1'
curl -XPOST 'http://elasticsearch02:9200/_snapshot/my_hdfs_repository/snapshot_1/_restore?pretty'
curl -XGET 'http://elasticsearch02:9200/my_index/my_type/1'
复制代码
从一个仓库中恢复数据,其实内部机制跟从其余的node上恢复一个shard是同样的。若是要监控这个恢复的过程,能够用recovery api,好比:GET restored_index_3/_recovery。若是要看全部索引的恢复进度:GET /_recovery/。能够看到恢复进度的大体的百分比。结果大体以下所示:
curl -XGET 'http://elasticsearch02:9200/my_index/_recovery?pretty'
复制代码
若是要取消一个恢复过程,那么须要删除已经被恢复到es中的数据。由于一个恢复过程就只是一个shard恢复,发送一个delete操做删除那个索引便可,好比:DELETE /restored_index_3。若是那个索引正在被恢复,那么这个delete命令就会中止恢复过程,而后删除已经恢复的 全部数据。
curl -XDELETE 'http://elasticsearch02:9200/my_index'
复制代码
生产部署还有不少工做要作,本文从初级思路切入,进行了问题的整合。
本套技术专栏做者(秦凯新)专一于大数据及容器云核心技术解密,具有5年工业级IOT大数据云平台建设经验,可提供全栈的大数据+云原平生台咨询方案,请持续关注本套博客。QQ邮箱地址:1120746959@qq.com,若有任何学术交流,可随时联系
秦凯新