1. 引言
此前,在 rabbitmq 系列文章中,我们介绍过脑裂问题。
所谓的脑裂问题,就是在多机热备的高可用 HA 系统中,当两个节点心跳突然断开,就分裂为了两个独立的个体,由于互相失去联系,都认为对方出现了故障,因此都会去争抢对方的资源,争抢启动,由此就会发生严重的后果。 举个形象的例子,A 和 B 作为一个双机热备集群的两个节点,各自持有集群的一部分数据 — a 和 b,这时,两机器之间突然无法通信,A 认为 B 已经挂掉,B 认为 A 已经宕机,于是会出现:
- 如果 A 拥有 b 数据的备份,则 A 将以完整数据运行,B 也同样以完整数据运行,这将很可能导致两个节点同时读写共享数据造成数据损坏
- 如果 A、B 各自仅拥有 a、b 数据,那么两个节点要么均无法启动,要么以瓜分完整共享数据的方式启动
Elasticsearch 也有可能发生这样的问题。
2. Elasticsearch 脑裂问题可能产生的原因
- 网络问题 — 节点间网络异常造成集群发生物理分离,造成脑裂问题
- 节点负载 — 如果 master 节点负载过高,则可能造成 master 节点停止响应,从而脱离集群,集群重新选主,恢复响应后出现脑裂问题
3. 如何避免脑裂问题
3.1. 集群搭建
根据脑裂问题发生的两点原因,从集群搭建上需要遵循以下原则:
- 集群尽量部署在同一个内网环境中,从而保证各节点通讯的可靠性
- master 节点与 data 节点分离,从而保证 master 节点的响应能力
Elasticsearch 提供了以下配置,来决定该节点是否有成为 master 节点或数据节点的资格:
node.master: true node.data: false
这样配置,该节点将不会存储任何数据,将上述两项分别做相反配置,则节点只会成为数据节点,不会被选举为 Master 节点,这样就可以很大程度上保证集群的稳定性。
3.2. 多播与单播
通常,我们只需要在每个节点配置好了集群名称,节点名称,互相通信的节点会根据es自定义的服务发现协议去按照多播的方式来寻找网络上配置在同样集群内的节点。 这就是多播模式,但事实上,在阿里云等云服务器上采用多播模式自行搭建 Elasticsearch 将导致各节点无法发现从而集群无法建成,这也是云服务供应商从安全角度考虑的策略,在这样的云服务器上,我们只能采用单播模式搭建集群。 所谓的单播,就是在配置中读取到 master 节点信息,直接连入,其效率和安全性都高于多播模式,但是单播存在的问题是如果新增节点是 master 候选节点,则必须更改集群中所有节点的配置。 下面的配置开启单播模式:
discovery.zen.ping.multicast.enabled: false discovery.zen.ping.unicast.hosts: ["master1", "master2", "master3"]
3.3. 最少包含Master节点数与存活检测时长
最少包含Master节点数与存活检测时长两个配置项也是避免脑裂问题的重要配置:
discovery.zen.minimum_master_nodes: 2 discovery.zen.ping_timeout: 60s
- discovery.zen.minimum_master_nodes
这个配置项就是最少包含 Master 节点数,他指的是集群中至少有多少个 master 候选节点存在才能组成集群。 官方推荐配置是 (N/2) 1,N 是 Master 候选节点总数量。 这样配置以后,假设集群从网络故障中恢复,若干个数据节点与 (N/2) 1 个 Master 候选节点组成了一个集群,剩余数据节点与剩余 master 候选节点尝试组成集群,由于剩余 Master 候选节点数为 N-((N/2) 1) = (N/2)-1 < (N/2) 1,所以他们不能成为一个集群,他们必须去继续寻找其他节点来组成集群,从而从根本上解决了脑裂问题。 这个配置也是几乎所有 HA 系统都会提供的基础配置。
- discovery.zen.ping_timeout
这个配置是存活检测时长,意即超过该配置项的时间内节点没有响应,则认为该节点出现故障脱离集群,默认值为 3 秒。
适当增大该配置,可以降低误报的几率,减少 master 重新选举的可能。
4. 两节点集群的建议
对于 Elasticsearch 集群来说,是强烈不建议搭建仅具有两个节点的集群的,因为搭建两节点集群则必须在下列两个策略中选择其一:
- 将 discovery.zen.minimum_master_nodes 配置为 2,则任何一个节点故障,整个集群即无法提供服务
- 将 discovery.zen.minimum_master_nodes 配置为 1,则无法避免脑裂问题的发生
如果在生产环境中,只有两台主机的环境下,更为推荐的做法是,在某一台主机上增加一份不同端口的设置:
node.data: false node.master: true
这样,在该主机上启动两个 Elasticsearch 实例,这样就可以创建一个具有三节点的集群,而数据的存储方式与此前的两节点集群保持一致。 当故障发生时,具有两节点的机器仍然可以提供集群服务,又可以避免脑裂问题,算是一种资源不足情况下的让步方案。
5. 参考资料
https://github.com/elastic/elasticsearch/issues/2488。 https://blog.csdn.net/cnweike/article/details/39083089。 https://www.cnblogs.com/zhukunrong/p/5224558.html。