u1timate
Published on 2022-03-31 / 843 Visits
0

ETCD迁移

  • 线上版本
    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
    Image
    ./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
Image [2]
说明新节点已经可以加入集群
根据上面的配置示例,修改新节点的配置文件,如下
,重点配置如下

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

Image [3]
重点关注箭头指向的配置

  • 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
      示例
      Image [4]
  • 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 选项