共计 8804 个字符,预计需要花费 23 分钟才能阅读完成。
概要
本篇开始介绍 Elasticsearch 生产集群的搭建及相关参数的配置。
ES 集群的硬件特性
我们从开始编程就接触过各种各样的组件,而每种功能的组件,对硬件要求的特性都不太相同,有的需要很强的 CPU 计算能力,有的对内存需求量大,有的对网卡要求高等待,下面我们讨论一下 ES 集群对几种硬件的特性需求。
CPU
ES 集群对 CPU 的要求相对低一些,毕竟纯计算的比重要小一些,选用主流的 CPU,2 核到 8 核的都可以。
如果有两种 CPU 可以挑选,一种是主频高但核数少的 CPU,另一种是主频一般核数多的 CPU,肯定选后一种,因为多核的 CPU 可以提供更多的并发处理能力,远比单核高性能带来的效益要高。
内存
ES 集群对内存的要求很高,部署 ES 集群时,要把大部分资源投入到内存当中。内存分配主要有两部分,JVM heap 内存(堆内存)和 OS Cache 内存。
JVM heap 内存用得不多,主要是 OS Cache,我们知道,ES 建立的倒排索引,正排索引,过滤器缓存,都是优先放在内存当中的,OS Cache 的大小直接决定搜索的性能,如果 OS Cache 不够,ES 搜索等操作只有被迫读硬盘,延时就会从毫秒级升到秒级。
OS Cache 具体在多大才算够,取决于数据量,如果是百万级别的数据,16GB 左右应该可以接受,如果是亿级,一般单节点都是 64GB 内存。生产环境最低要求内存应不低于 8GB。
硬盘
硬盘成本本身比较便宜,能用 SSD 就用 SSD,访问速度肯定比机械硬盘快,预估好数据量后就尽可能多规划一些容量。
另外尽量使用本地存储,网络存储还依赖于网络传输,这个容易造成一些延迟。
网络
对 ES 集群这种分布式系统来说,快速并且可靠的网络还是比较重要的,shard 的分配和 rebalance 都需要占用大量的带宽,集群最好部署在同一个局域网内,异地容灾等跨数据中心的部署方案,要考虑到网络故障带来的影响。
JVM 选择
使用 ES 官网推荐的 JDK 版本,服务端和客户端尽量使用同一个版本的 JDK。
涉及到 ES 服务端的 JVM 调优设置,保持原样不要轻易改动,毕竟 ES 已经花了大量人力物力验证过的,随意调整 jvm 参数可能适得其反。
容量规划
规划集群里,要规划好投入几台服务器,数据量上限是多少,业务模型数据读写的比例是多少,历史数据的迁移方案等,一般来说,百万到 10 亿内的数据量,使用 ES 集群还是能够支撑下来的,ES 节点数建议不要超过 100 个。
举个例子:数据量 10 亿以内,部署 5 台服务器,8 核 64GB 内存,是能够支撑的。
生产案例模拟
Linux 操作系统搭建
我们使用 Linux 虚拟机来演示一个生产 ES 集群的搭建。我们创建 4 台虚拟机,每台 2 核 CPU,4GB 内存,操作系统为 CentOS 7 64bit。
虚拟机我用的是 VMware workstation,有用 virtual box 也行,CentOS 7、JDK 的安装不赘述。记得把 CentOS 的防火墙关了。
修改每台机器的 hostname 信息,命令vi /etc/hostname
,修改文件,保存即可,建议修改成 elasticsearch01,elasticsearch02,elasticsearch03,elasticsearch04。
假定我们 4 台虚拟机的域名和 IP 是这样分配的:
192.168.17.138 elasticsearch01
192.168.17.137 elasticsearch02
192.168.17.132 elasticsearch03
192.168.17.139 elasticsearch04
把这段配置放在 /etc/hosts
文件末尾,4 台机器做相同的配置。
这 4 台机器之间,可以配置免密登录,如在 elasticsearch01 机器上,我们执行以下操作:
- 生成公钥文件,命令:
ssh-keygen -t rsa
一直输入回车,不要设置密码默认会将公钥放在 /root/.ssh 目录下生成 id_rsa.pub 和 id_rsa 两个文件
- 拷贝公钥文件
cp id_rsa.pub authorized_keys
- 将公钥文件拷贝到另外三台机器
ssh-copy-id -i elasticsearch02
ssh-copy-id -i elasticsearch03
ssh-copy-id -i elasticsearch03
拷贝完成后,可以在目标机器上 /root/.ssh/
目录下看到多了一个 authorized_keys 文件。
- 尝试免密登录,在 elasticsearch01 机器上输入
ssh elasticsearch02
,如果不需要输入密码就能登录到 elasticsearch02,说明配置成功,其他机器类似。
这 4 台机器也可以相互做 ssh 免密设置。
这里补充一点免密登录的方向性问题,上面的案例是在 elasticsearch01 机器生成的公钥,并且发送给了 elasticsearch02 等三台机器,那么我从 elasticsearch01 跳到 elasticsearch02 是不需要密码的,反过来从 elasticsearch02 登录到 elasticsearch01,还是需要密码的。
最后补充几个常用检查命令:
- 检查 NetManager 的状态:systemctl status NetworkManager.service
- 检查 NetManager 管理的网络接口:nmcli dev status
- 检查 NetManager 管理的网络连接:nmcli connection show
Elasticsearch 服务端
这里选用的 JDK 版本为 1.8.0_211,Elasticsearch 版本为 6.3.1,自行安装不赘述。
ES 解压后的目录结构:
# 用 "tree -L 1" 命令得到的树状结构
.
├── bin
├── config
├── lib
├── LICENSE.txt
├── logs
├── modules
├── NOTICE.txt
├── plugins
└── README.textile
- bin:存放 es 的一些可执行脚本,比如用于启动进程的 elasticsearch 命令,以及用于安装插件的 elasticsearch-plugin 插件
- config:用于存放 es 的配置文件,比如 elasticsearch.yml
- logs:用于存放 es 的日志文件
- plugins:用于存放 es 的插件
- data:用于存放 es 的数据文件的默认目录,就是每个索引的 shard 的数据文件,一般会另外指定一个目录。
Elasticsearch 参数设置
在 config 目录下的文件,包含了 ES 的基本配置信息:
.
├── elasticsearch.yml
├── jvm.options
├── log4j2.properties
├── role_mapping.yml
├── roles.yml
├── users
└── users_roles
默认参数
Elasticsearch 的配置项比较丰富并且默认配置已经非常优秀了,基本上我们需要改动的是跟服务器环境相关的配置,如 IP 地址,集群名称,数据存储位置,日志存储位置等外围参数,涉及到内部机制及 JVM 参数的,一般不干预,不恰当的 JVM 参数调整反而会导致集群出现性能故障,如果没有充足的理由或数据验证结果,不要轻易尝试修改。
集群和节点名称
在 elasticsearch.yml 文件里这项配置表示集群名称,配置项默认是注释掉的,集群名称默认为 elasticsearch。
#cluster.name: my-application
这个配置项强烈建议打开,用项目约定的命名规范进行重命名,并且将研发环境、测试环境、STG 准生产环境、生产环境分别命名,如 elasticsearch_music_app_dev 表示研发环境,elasticsearch_music_app_sit 表示测试环境,elasticsearch_music_app_pro 表示生产环境等。避免开发测试环境连错环境,无意中加入集群导致数据问题。
cluster.name: elasticsearch_music_app_pro
节点名称的配置项
#node.name: node-1
默认也是注释掉的,ES 启动时会分配一个随机的名称,建议还是自行分配一个名称,这样容易记住是哪台机器,如
node.name: es_node_001_data
文件路径
涉及到文件路径的几个参数,主要有数据、日志、插件等,默认这几个地址都是在 Elasticsearch 安装的根目录下,但 Elasticsearch 升级时,有些目录可能会有影响,安全起见,可以单独设置目录。
#
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
#path.data: /path/to/data
#
# Path to log files:
#
#path.logs: /path/to/logs
#
例如我们可以在 /var
目录下创建相应的文件夹,并且赋予相应的读写权限,如:
path.data: /var/es/data
path.logs: /var/es/logs
日志文件配置
log4j2.properties 文件,ES 日志框架选用的是 log4j2,也就是 log4j 的进化版本,对 Java 技术栈熟悉的童鞋,看到这个配置文件会非常熟悉,默认的日志输入配置、格式均能满足日常的故障定位和分析,也不需要什么改动。
默认是一天生成一个日期文件,如果 ES 承载的数据量特别大,可以调整日志文件产生频率和每个日志文件的大小,以及 ES 最多存储日志的大小、数量。
Elasticsearch 集群发现机制
配置参数
Zen Discovery 是 Elasticsearch 集群发现机制的默认实现,底层通信依赖 transport 组件,我们完成 Elasticsearch 集群的配置主要有下面几个参数:
- cluster.name 指定集群的名称。
- node.name 节点名称。
- network.host 节点绑定的 IP。
- node.master 可选值为 true/false,决定该节点类型为 master eligible 或 data node。
- discovery.zen.ping.unicast.hosts gossip 路由服务的 IP 地址,即集群发现协议通信的公共节点,可以写多个,有节点启动时会向里面的 IP 发送消息,获取集群其他节点的信息,最后加入集群。
Elasticsearch 集群是点对点 (P2P) 的分布式系统架构,数据索引、搜索操作是 node 之间直接通信的,没有中心式的 master 节点,但 Elasticsearch 集群内的节点也分成 master node 和 data node 两种角色。
正常情况下,Elasticsearch 集群只有一个 master 节点,它负责维护整个集群的状态信息,集群的元数据信息,有新的 node 加入或集群内 node 宕机下线时,重新分配 shard,并同步 node 的状态信息给所有的 node 节点,这样所有的 node 节点都有一份完整的 cluster state 信息。
集群发现的一般步骤如下:
- 节点配置 network.host 绑定内网地址,配置各自的 node.name 信息,cluster.name 设置为相同的值。
- discovery.zen.ping.unicast.hosts 配置了几个 gossip 路由的 node。
- 所有 node 都可以发送 ping 消息到路由 node,再从路由 node 获取 cluster state 回来。
- 所有 node 执行 master 选举。
- 所有 node 都会跟 master 进行通信,然后加入 master 的集群。
master 选举
node.master 设置为 true 的,将成为 master eligible node,也叫 master 候选节点,只有 master eligible node 才能被选举成 master node。如果是个小集群,那么所有节点都可以是 master eligible node,10 个节点以上的集群,可以考虑拆分 master node 和 data node,一般建议 master eligible node 给 3 个即可。
master 选举过程是自动完成的,有几个参数可以影响选举的过程:
- discovery.zen.ping_timeout: 选举超时时间,默认 3 秒,网络状况不好时可以增加超时时间。
- discovery.zen.join_timeout: 有新的 node 加入集群时,会发送一个 join request 到 master node,同样因为网络原因可以调大,如果一次超时,默认最多重试 20 次。
- discovery.zen.master_election.ignore_non_master_pings:如果 master node 意外宕机了,集群进行重新选举,如果此值为 true,那么只有 master eligible node 才有资格被选为 master。
- discovery.zen.minimum_master_nodes: 新选举 master 时,要求必须有多少个 master eligible node 去连接那个新选举的 master。而且还用于设置一个集群中必须拥有的 master eligible node。如果这些要求没有被满足,那么 master node 就会被停止,然后会重新选举一个新的 master。这个参数必须设置为我们的 master eligible node 的 quorum 数量。一般避免说只有两个 master eligible node,因为 2 的 quorum 还是 2。如果在那个情况下,任何一个 master 候选节点宕机了,集群就无法正常运作了。
集群故障探查
有两种集群故障探查机制
- master 主动对集群中所有的其他 node 发起 ping 命令,判断它们是否是存活着的。
- 每个 node 向 master node 发送 ping 请求,判断 master node 是否存活,否则就会发起一个选举过程。
有下面三个参数用来配置集群故障的探查过程:
- ping_interval:ping 一次 node 的间隔时间,默认是 1s
- ping_timeout:每次 ping 的 timeout 等待时长,默认是 30s
- ping_retries:对 node 的 ping 请求失败了,重试次数,默认 3 次。
集群状态更新
master node 是集群中唯一可以对 cluster state 进行更新的 node。更新的步骤如下:
- master node 收到更新事件,如 shard 移动,可能会有多条事件,但 master node 一次只处理一个集群状态的更新事件。
- master node 将事件更新到本地,并发布 publish message 到集群所有的 node 上。
- node 接收 publish message 后,对这个 message 返回 ack 响应,但是不会立即更新。
- 如果 master 没有在指定的时间内(discovery.zen.commit_timeout 配置项,默认是 30s),从至少 N 个节点(discovery.zen.minimum_master_nodes 配置项)获取 ack 响应,那么这次 cluster state change 事件就会被 reject,最终不会被提交。
- 如果在指定时间内,指定数量的 node 都返回了 ack 消息,那么 cluster state 就会被 commit,然后 master node 把 commit message 发送给所有的 node。所有的 node 接收到那个 commit message 之后,接着才会将之前接收到的集群状态应用到自己本地的状态副本中去。
- master 会等待所有 node 的 commit message 的 ack 消息,在一个等待超时时长内,如果接收到了响应,表示状态更新成功,master node 继续处理内存 queue 中保存的下一个更新事件。
discovery.zen.publish_timeout 默认是 30s,这个超时等待时长是从 plublish cluster state 开始计算的。
我们可以参照此图:
master node 宕机问题
Elasticsearch 集群中,master node 扮演着非常重要的角色,如果 master node 宕机了,那岂不是群龙无首了?虽然有 master 选举,但这个也是要时间的,没有 master node 那段空档期集群该怎么办?
说了一半,基本上是完了,但我们也可以设置,群龙无首时哪些操作可以做,哪些操作不能做。
discovery.zen.no_master_block 配置项可以控制在群龙无首时的策略:
- all: 一旦 master 宕机,那么所有的操作都会被拒绝。
- write:默认的选项,所有写操作都会被拒绝,但是读操作是被允许的。
split-brain(脑分裂问题)
在 Elasticsearch 集群中,master node 非常重要,并且只有一个,相当于整个集群的大脑,控制将整个集群状态的更新,如果 Elasticsearch 集群节点之间出现区域性的网络中断,比如 10 个节点的 Elasticsearch 集群,4 台 node 部署在机房 A 区,6 台 node 部署在机房 B 区,如果 A 区与 B 区的交换机故障,导致两个区隔离开来了,那么没有 master node 的那个区,会触发 master 选举,如果选举了新的 master,那么整个集群就会出现两个 master node,这种现象叫做脑分裂。
这样现象很严重,会破坏集群的数据,该如何避免呢?
回到我们前面提到的 discovery.zen.minimum_master_nodes
参数,这个值的正确设置,可以避免上述的脑分裂问题。
discovery.zen.minimum_master_nodes
参数表示至少需要多少个 master eligible node,才可以成功地选举出 master,否则不进行选举。
足够的 master eligible node 计算公式:
quorum = master_eligible_nodes / 2 + 1
如上图我们 10 个 node 的集群,如果全部是 master eligible node,那么 quorum = 10/2 + 1 = 6。
如果我们有 3 个 master eligible node,7 个 data node,那么 quorum = 3/2 + 1 = 2。
如果集群只有 2 个节点,并且全是 master eligible node,那么 quorum = 2/2 + 1 = 2,问题就来了,如果随便一个 node 宕机,在只剩下一个 node 情况下,无法满足 quorum 的值,master 永远选举不成功,集群就彻底无法写入了,所以只能设置成 1,后果是只要这两个 node 之间网络断了,就会发生脑分裂的现象。
所以一个 Elasticsearch 集群至少得有 3 个 node,全部为 master eligible node 的话,quorum = 3/2 + 1 = 2。如果我们设置 minimum_master_nodes=2,分析一下会不会出现脑分裂的问题。
场景一:A 区一个 node,为 master,B 区两个 node,为 master eligible node
A 区因为只剩下一个 node,无法满足 quorum 的条件,此时 master 取消当前的 master 角色,且无法选举成功。
B 区两个 master eligible node,满足 quorum 条件,成功选举出 master。
此时集群还是只有一个 master,待网络故障恢复后,集群数据正常。
场景二:A 区一个 node,为 master eligible node,B 区 2 个 node,其中一个是 master
A 区只有一个 master eligible node,不满足 quorum 的条件,无法进行选举。
B 区原本的 master 存在,不需要进行选举,并且满 quorum 的条件,master 角色可以保留。
此时集群还是一个 master,正常。
综上所述:3 个节点的集群,全部为 master eligible node,配置 discovery.zen.minimum_master_nodes: 2,就可以避免脑裂问题的产生。
minimum_master_nodes 动态修改
因为集群是可以动态增加和下线节点的,quorum 的值也会跟着改变。minimum_master_nodes 参数值需要通过 api 随时修改的,特别是在节点上线和下线的时候,都需要作出对应的修改。而且一旦修改过后,这个配置就会持久化保存下来。
修改 api 请求如下:
PUT /_cluster/settings
{
"persistent" : {"discovery.zen.minimum_master_nodes" : 2}
}
响应报文:
{
"acknowledged": true,
"persistent": {
"discovery": {
"zen": {"minimum_master_nodes": "2"}
}
},
"transient": {}}
也可以通过命令查询当前的配置:
GET /_cluster/settings
响应结果如下:
{
"persistent": {
"discovery": {
"zen": {"minimum_master_nodes": "1"}
}
},
"transient": {}}
留一个问题
上图 10 个节点的集群,假设全是 master eligible node,按照上述的网络故障,会不会出现脑分裂现象?配置项 minimum_master_nodes 最低要配置成多少,才不会出现脑分裂的问题?
小结
本篇主要介绍了 Elasticsearch 集群的部署和参数设置等知识,大部分都不需要人工干预,默认值已经是最优选,集群发现机制和 master 选举机制了解一下就 OK。
专注 Java 高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java 架构社区
可以扫左边二维码添加好友,邀请你加入 Java 架构社区微信群共同探讨技术