etcd集群在运行过程中,难免出现替换节点、新增节点、删除节点的需求,对于raft这种基于quorum机制的算法而言,实际生产环境,很难让节点在同时感知到集群节点的变化,正如raft[1]第6节,cluster membership changes呈现的场景,在一个三节点集群中新增两个新节点,就有可能出现脑裂。
针对raft集群的节点变更,raft算法提供了两阶段算法:joint consensus,这里直接贴上raft论文图文描述,如下:
当集群节点需要变更时,Leader节点会创建一份C old-new配置,并作为日志Entry同步到其他节点,日志Entry只要在节点上被存储下来,就会使用C old-new做决定,也就是说日志Entry的committed及选举投票都需要同时满足新旧配置的quorum,在C old-new被提交之后,Leader会创建一条C new作为新的日志条目拷贝到大多数,提交后使C new单独生效。
etcd实现与raft原文描述的_joint consensus略有不同,_对于单次变更的节点数量不同,etcd的实现也不同,主要有两种情况:单节点、多节点。
单节点变更流程
_etcd会将配置作为单独的日志Entry,配置Entry的类型为_EntryConfChange,配置Entry会复用普通日志Entry的append-committed流程,在配置被apply时直接进入生效阶段。
多节点变更流程
etcd会将配置作为单独的日志Entry,配置Entry的类型为_EntryConfChangeV2,配置Entry会复用普通日志Entry的append-committed流程,在配置被apply时直接进入joint consensus阶段,此时etcd节点使用C old-new配置做决策,然后Leader会创建一个空_EntryConfChangeV2日志Entry,将空日志Entry拷贝到其他节点,在空Entry被apply时结束joint consensus阶段,正式使用新配置做决策。