- 线上版本
etcd Version: 3.4.4
Git SHA: c65a9e2dd
Go Version: go1.12.12
Go OS/Arch: linux/amd64
0x01 方案一: 加入原有集群进行数据同步
如果集群使用了证书,同步之前需要先使用etcdctl同步新证书,在进行相关操作
- 1、查看当前集群状态
./etcdctl --endpoints=etcd1:23801,etcd2:23802,etcd3:23803 endpoint status --write-out=table
./etcdctl --endpoints=etcd1:23801,etcd2:23802,etcd3:23803 endpoint health -w table
- 2、先在三台服务器上新部署etcd,暂不启动
- 3、将新节点添加到集群中命令如下
./etcdctl --endpoints=etcd1:23801,etcd1:23802,etcd1:23803 --peer-urls=http://192.168.18.116:23801 member add etcd4
会出现该节点的部分重点配置示例
Member e0077ba8f95b900e added to cluster e17fbb5e81c7ed5c
ETCD_NAME="etcd4"
ETCD_INITIAL_CLUSTER="etcd1=http://etcd1:23801,etcd2=http://etcd2:23802,etcd4=http://192.168.18.116:23804,etcd3=http://etcd3:23803"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.18.116:23804"
ETCD_INITIAL_CLUSTER_STATE="existing"
查看当前集群成员
./etcdctl --endpoints=etcd1:23801,etcd1:23802,etcd1:23803 member list
说明新节点已经可以加入集群
根据上面的配置示例,修改新节点的配置文件,如下
,重点配置如下
initial-cluster-state: existing # 当前阶段不能是new
# 最重要配置,其中etcd4,etcd5,etcd6是按照新节点的启动逐渐增加的,不能一开始就把新的本次不会启动的新节点都配置上
initial-cluster: etcd1=http://etcd1:23801,etcd2=http://etcd2:23802,etcd3=http://etcd3:23803,etcd4=http://192.168.18.116:23801
重点关注箭头指向的配置
- 4、启动这个新节点
./etcd --config-file=/data/etcd/config/config_4.conf
其它两个节点也按照上面步骤去操作加入原来三台etcd集群即可
-
添加新节点2
./etcdctl --endpoints=etcd1:23801,etcd1:23802,etcd1:23803 --peer-urls=http://192.168.18.116:23802 member add etcd5
-
添加新节点3
./etcdctl --endpoints=etcd1:23801,etcd1:23802,etcd1:23803 --peer-urls=http://192.168.18.116:23803 member add etcd6
-
5、查看当前集群节点状态
./etcdctl --endpoints=etcd1:23801,etcd1:23802,etcd1:23803 member list -w table
所有新的节点成功加入
- 6、查看集群状态
通过查看状态相关的信息,判断是否完成同步
./etcdctl --endpoints=192.168.18.37:23801,192.168.18.37:23802,192.168.18.37:23803,192.168.18.116:23802,192.168.18.116:23803,192.168.18.116:23801 endpoint status -w table
+----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 192.168.18.37:23801 | 592828cadb6c8103 | 3.4.4 | 1.4 MB | false | false | 28 | 12120 | 12119 | |
| 192.168.18.37:23802 | 725cb91928661599 | 3.4.4 | 1.4 MB | false | false | 28 | 12121 | 12120 | |
| 192.168.18.37:23803 | f8f0674e079db8b6 | 3.4.4 | 1.4 MB | true | false | 28 | 12121 | 12120 | |
| 192.168.18.116:23802 | 5b4e1f142937d57 | 3.4.4 | 1.4 MB | false | false | 28 | 12121 | 12121 | |
| 192.168.18.116:23803 | 16b9ed6d3dd53bc0 | 3.4.4 | 1.4 MB | false | false | 28 | 12122 | 12122 | |
| 192.168.18.116:23801 | 4f51c5a154ca3cc6 | 3.4.4 | 1.4 MB | false | false | 28 | 12122 | 12122 | |
+----------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
-
查看集群健康状态
./etcdctl --endpoints=192.168.18.37:23801,192.168.18.37:23802,192.168.18.37:23803,192.168.18.116:23802,192.168.18.116:23803,192.168.18.116:23801 endpoint health -w table
-
等待集群节点之间同步完成
-
7、修改DNS指向到新的集群节点
-
8、逐步关闭老的集群节点
- 将旧节点从集群中移除,member remove [旧节点名称]
-
9、再次修改新节点配置文件
- 修改集群地址,删除旧节点地址
- 修改节点状态,existing改为new,(注意这个配置会导致客户端的etcd连接出现错误,注意在客户端做好异常处理,尤其是watch逻辑,出现错误需要重新watch)
示例
-
10、依次重启新节点1,新节点2和新节点3 ,注意每次重启节点前需要确定集群是否同步成功和是否健康,然后再进行下一个节点的重启
./etcdctl --endpoints=192.168.18.116:23801,192.168.18.116:23802,192.168.18.116:23803 endpoint status -w table
./etcdctl --endpoints=192.168.18.116:23801,192.168.18.116:23802,192.168.18.116:23803 endpoint health -w table
相关操作命令
# 查看集群节点列表
./etcdctl --endpoints=etcd1:23801,etcd2:23802,etcd3:23803,192.168.18.116:23801,192.168.18.116:23802,192.168.18.116:23803 member list -w table
+------------------+---------+-------+-----------------------------+----------------------+------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER |
+------------------+---------+-------+-----------------------------+----------------------+------------+
| 528912e8433b0644 | started | etcd4 | http://192.168.18.116:23801 | http://0.0.0.0:23791 | false |
| 592828cadb6c8103 | started | etcd1 | http://etcd1:23801 | http://0.0.0.0:23791 | false |
| 725cb91928661599 | started | etcd2 | http://etcd2:23802 | http://0.0.0.0:23792 | false |
| ae64f3eb77d6b809 | started | etcd6 | http://192.168.18.116:23803 | http://0.0.0.0:23793 | false |
| da1b57b644945581 | started | etcd5 | http://192.168.18.116:23802 | http://0.0.0.0:23792 | false |
| f8f0674e079db8b6 | started | etcd3 | http://etcd3:23803 | http://0.0.0.0:23793 | false |
+------------------+---------+-------+-----------------------------+----------------------+------------+
# 删除旧集群的节点1
./etcdctl --endpoints=etcd1:23801,etcd2:23802,etcd3:23803,192.168.18.116:23801,192.168.18.116:23802,192.168.18.116:23803 member remove [592828cadb6c8103]
# 查看集群节点状态
./etcdctl --endpoints=192.168.18.37:23801,192.168.18.37:23802,192.168.18.37:23803 endpoint status -w table
# 查看集群健康状态
./etcdctl --endpoints=192.168.18.116:23801,192.168.18.116:23802,192.168.18.116:23803 endpoint health -w table
最后完成迁移
可能的问题
- 移除旧节点的过程中,可能出现无法成功选举leader的情况,注意时刻注意集群健康状态
0x02 方案二: 使用快照
该方案不适合容器环境,如果在etcd容器中直接复制数据目录,
- 1.1、获取etcd集群的快照,命令如下(集群地址指定一个就行了)
./etcdctl --endpoints 192.168.18.116:23801 snapshot save snapshot.db
- 1.2、 安装和配置好新集群,暂不启动
- 1.3、 用原来的快照进行数据恢复,将数据恢复到新的三台etcd集群中。在新的三台etcd主机上都执行(不需要启动服务),命令如下:
节点1:
./etcdctl snapshot restore /root/snapshot.db --name etcd1 --initial-cluster etcd1=http://etcd1:23801,etcd2=http://etcd2:23802,etcd3=http://etcd3:23803 --initial-advertise-peer-urls http://etcd1:23801 --data-dir /data/etcd/data1
节点2:
./etcdctl snapshot restore /root/snapshot.db --name etcd2 --initial-cluster etcd1=http://etcd1:23801,etcd2=http://etcd2:23802,etcd3=http://etcd3:23803 --initial-advertise-peer-urls http://etcd1:23802 --data-dir /data/etcd/data2
节点3:
./etcdctl snapshot restore /root/snapshot.db --name etcd3 --initial-cluster etcd1=http://etcd1:23801,etcd2=http://etcd2:23802,etcd3=http://etcd3:23803 --initial-advertise-peer-urls http://etcd1:23803 --data-dir /data/etcd/data3
- 1.4、依次启动这三个节点
- 1.5、查看集群状态
./etcdctl --endpoints=192.168.18.37:23801,192.168.18.37:23802,192.168.18.37:23803 endpoint health -w table
/etcdctl --endpoints=192.168.18.37:23801,192.168.18.37:23802,192.168.18.37:23803 endpoint status -w table
0x03 补充
3.1 分区脑裂
这是一个被广为流传的误解,众所周知 etcd 使用 Raft 协议来解决数据一致性问题。一个 Raft Group 只能有一个 Leader 存在,如果一旦发生网络分区,Leader 只会在多数派一边被选举出来,而少数派则全部处于 Follower 或 Candidate 状态,所以一个长期运行的集群是不存在脑裂问题的。etcd 官方文档也明确了这一点:
The majority side becomes the available cluster and the minority side is unavailable; there is no “split-brain” in etcd.
但是有一种特殊情况,假如旧的 Leader 和集群其他节点出现了网络分区,其他节点选出了新的 Leader,但是旧 Leader 并没有感知到新的 Leader,那么此时集群可能会出现一个短暂的「双 Leader」状态。这种情况并不能称之为脑裂,原因有二:
这不是一个长期运行状态,维持时间不会超过一个投票周期
etcd 也有 ReadIndex、 Leaseread 机制来解决这种状态下的一致性语义问题
3.2 分区后,少数派依然可以工作
这个描述并不准确,分区后少数派全部处于 Follower 或 Candidate 状态,所以肯定无法执行写操作(CP 系统,无法保证可用性)。那么读请求呢?答案是:默认无法读取!
etcd 默认使用 ReadIndex 的机制来实现线性一致性读。其原理就是 etcd 在处理读请求时,那么它必须先要向 Leader 去查询 commit index,此时少数派联系不上 Leader,所以必然会失败。但是这时候是支持非一致性读请求的。不过需要开启一个选项:
SDK 访问需要配置 WithSerializable
选项(默认并不开启)
etcdctl 访问需要配置 --consistency=s
选项