本文主要探讨etcd集群的高可用容错方案和容错能力的探讨。在出现单机故障时相关的容错方案,如果故障的节点是Leader,重新选择Leader的选主逻辑,以及集群的恢复方案。由于单节点已经保存保存了所有数据,因此集群出现节点异常时,只需要重新选举出Leader即可,不需要进行数据迁移、副本备份等操作。
更多关于分布式系统的架构思考请参考文档关于常见分布式组件高可用设计原理的理解和思考
etcd通过Raft协议通过Quorum机制进行集群的高可用和保证数据一致性,在节点故障场景下,能够通过选主逻辑,选择新的Leader,重新提供服务。集群最大能够容忍的故障节点数量为 (n -1) / 2
,n表示集群的节点数量(通常要求部署n为单数,如3,5,7)。
选主的目标,是希望选择一个Leader以继续提供服务,分为初始运行选择Leader和运行中Leader出现异常重新触发选主。
在讨论选主逻辑之前,应该思考一下,什么样的节点应该成为Leader? 因为投票过程需要比较,比较的依据就是按照成为Leader的标准进行参照!
按照etcd的设计,成为Leader的节点应该满足如下条件,在同一个选主周期过程中
因此整理出,在投票选主过程中,有效相关因素
属性 | 作用 | 说明 |
---|---|---|
lastIndex | 日志记录单调递增 | 节点lastIndex越大(数据最新),该节点越容易成为Leader |
lastTerm | 选举纪元,单调递增 | 原则上选举纪元大的更有效,如果多个Leader相遇,纪元大有效(规避脑裂情况) |
可以总结出,成为Leader的原则是,在相同选票周期内,选择数据最新的节点成为Leader。
在同一投票纪元内,只有Follower才能参与投票成为Leader,默认启动时节点的角色都是Follower
1,阶段1: 各节点增加当前的投票周期term,转变为candidate给自己投票,投票的信息内容是(lastTerm,lastIndex),将选票信息广播
2,阶段2: 接受其他节点的广播选票信息,跟当前节点的选对比,根据lastTerm大、lastIndex大的原则,更改本节点的选票,比如当前节点的选票信息(2,0) < 广播选票(2,1),则当前节点的选票更新为(2,1)
3,阶段3: 完成阶段2的交换选票后,将当前节点交换后的选票广播给其他节点并进行统计,当前节点会根据获取的选票判断自己的角色,并得知对应的Leader节点。如果本节点收到超过集群半数节点选票,当前节点则成为Leader,反之则是Follower
4,阶段4: Leader主动发布心跳,如果Follower能够跟Leader进行通信并进行数据同步,选主结束;否则进行下一轮投票。
整体的逻辑跟zk的 选主逻辑 相近,但是差别是第4步,zk是Follower主动联系Leader,etcd是Leader主动发送心跳给Follower,目标都是为了让Leader和Follower完成握手,确认和承认对方的江湖地位。
以5个服务节点组成的etcd集群来看看Leader选举的过程。
第一次启动,都处于初始状态,lastIndex都是0,lastTerm分别为0。票据(lastIndex,lastTerm)分别为(0,0)、(0,0)、(0,0)、(0,0)、(0,0)(此概念不涉及源码层面,但是原理上差不多),逐个启动服务器,如下是具体的投票选举过程:
etcd集群正常运行期间,一旦选出了Leader,那么所有服务器的角色都不会再发生变化。
但是Leader所在的服务器挂了,如果集群中正常运行的机器半数以上,就得重选Leader。整体的选主逻辑跟《1.2 初始运行的选主过程》一致。
日志复制分为2个阶段
整体过程如下
(N - 1) / 2
个Follower(加上自己已经过半)响应成功时, Leader发起commit该数据并向所有节点发送commit请求C(X), 同时响应客户端Leader在挂掉的时候,可能还有一些写操作没有完成,如下是几种Leader故障的场景:
这就会导致数据恢复过程中,需要拆分为Leader选举和数据恢复 2个阶段
Leader 重新选举的过程和第一次启动选举一样,不再赘述。
成为新Leader必须满足以下两个条件:
需要注意的是在Leader选举的过程中,集群是处于无法正常对外提供服务的状态。如果由于各种原因,导致选主失败,则集群一直无法恢复。
为了集群全局数据一致性,所有的 Follower上的数据都必须和Leader的一样,数据同步过程如下
特别是已经宕机很久的Follower节点,有可能会保存一些无效的COMMIT事务。
如果原先集群运行了一段时间,产生了数据不一致,然后全部停机后,再一个个重启,最好是先去看看每个服务的事务日志文件内记录的最后提交的lastIndex,最大的那个服务先重启,否则可能会导致Leader已经选举好了,后面一个带着最全数据的服务节点重启了,反而要回滚事务。
Leader在广播COMMIT的过程中挂了,只要有1个节点完成了COMMIT,则认为这个事务已经完成数据有效,否则就会认为无效会被丢弃。如果完成COMMIT的节点也宕机了,数据就会丢失,就算后续拉起该节点,相关的事务也会被回滚。
Leaner的职能和功能跟Follower整体上并没有区别,但是Leaner没有选票权,不参与投票,也不会参与竞选Leader。这样设计的目的是,能够提升集群的读能力,但是又不必引入过多的投票节点,从而导致造成过多轮投票。过多轮投票选择出Leader,势必会延迟集群的恢复时间。