运行时重配置

etcd 被设计为能承受机器失败。etcd 集群自动从临时失败(例如,机器重启)中恢复,而且对于一个有 N 个成员的集群能容许 (N-1)/2 的持续失败。当一个成员持续失败时,不管是因为硬件失败或者磁盘损坏,它丢失到集群的访问。如果集群持续丢失超过 (N-1)/2 的成员,则它只能悲惨的失败,无可救药的失去法定人数(quorum)。一旦法定人数丢失,集群无法达到一致而因此无法继续接收更新。

etcd 自带对渐进的运行时重配置的支持,这容许用户在运行时更新集群成员。

重配置请求仅能在集群成员的大多数正常工作时可以处理。强烈推荐 在产品中集群大小总是大于2,从一个两成员集群中移除一个成员是不安全的。在移除过程中如果有失败,集群可能无法前进而需要从重大失败中重启。

重配置使用案例

让我们过一下一些重配置集群的常见理由,他们中的大多数仅仅涉及添加或者移除成员的组合。

循环或升级多台机器

如果多个集群成员因为计划的维护(硬件升级,网络停工)而需要移动,推荐一次一个的修改多个成员。
移除 leader 是安全的,但是在选举过程发生的期间有短暂的停机时间。如果集群保存超过50MB,推荐迁移成员的数据目录

修改集群大小

增加集群大小可以改善失败容忍度并提供更好的读取性能。因为客户端可以从任意成员读取,增加成员的数量可以提高整体的读取吞吐量。

减少集群大小可以改善集群的写入性能,作为交换是降低弹性。写入到集群是要复制到集群成员的大多数才能被认定已经提交。减少集群大小消减了大多数的数量,从而每次写入可以更快提交。

替换失败的机器

如果机器因为硬件故障,数据目录损坏,或者一些其他致命情况而失败,它应该尽快被替代。已经失败但是还没有移除的机器对法定人数有不利影响并减低对额外失败的容忍性。

为了替换机器,遵循从集群中移除成员的建议, 然后再添加新成员替代它的未知。 如果集群保存超过50MB,而且它还可以访问,推荐迁移失败成员的数据目录

从多数失败中重启集群

如果集群的多数已经丢失或者所有的节点已经修改了IP地址,则需要手工动作来安全恢复。

恢复过程中的基本步骤包括使用旧有数据创建新的集群,强制单个成员成活,并最终使用运行时配置来一次一个添加新的成员到这个新的集群。

集群重配置操作

在任何变动前,etcd 成员的简单多数(quorum) 必须可用。对于任何其他到 etcd 的写入,这也是根本性的同样要求。
所有集群的改动一次一个的完成:

  • 要更新单个成员peerURLs,做一个更新操作
  • 要替代单个成员,做一个添加然后一个删除操作
  • 要将成员从3增加到5,做两次添加操作
  • 要将成员从5减少到3,做两次删除操作

所有这些案例将使用etcd自带的etcdctl命令行工具。
如果不用etcdctl修改成员,可以使用 v2 HTTP members API 或者 v3 gRPC members API。

测试环境

name IP 状态
etcd1 192.168.4.10 原有
etcd2 192.168.4.20 原有
etcd3 192.168.4.30 新增、更新或删除

etcd1中的示例/usr/lib/systemd/system/etcd.service启动文件:

[Unit]
Description=etcd key-value store
Documentation=https://github.com/etcd-io/etcd
After=network.target

[Service]
EnvironmentFile=/etc/etcd/etcd.conf
ExecStart=/usr/bin/etcd
Restart=always

[Install]
WantedBy=multi-user.target

etcd1中的示例/etc/etcd/etcd.conf配置文件:

ETCD_NAME=etcd1
ETCD_DATA_DIR=/etc/etcd/data

ETCD_LISTEN_CLIENT_URLS=http://192.168.4.10:2379
ETCD_LISTEN_PEER_URLS=http://192.168.4.10:2380

ETCD_ADVERTISE_CLIENT_URLS=http://192.168.4.10:2379
ETCD_INITIAL_ADVERTISE_PEER_URLS=http://192.168.4.10:2380

ETCD_INITIAL_CLUSTER_STATE=new
ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster
ETCD_INITIAL_CLUSTER=etcd1=http://192.168.4.10:2380,etcd2=http://192.168.4.20:2380

ETCD_ENABLE_V2=true

检查成员信息:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379"  member list
39d7dd629f95330e, started, etcd2, http://192.168.4.20:2380, http://192.168.4.20:2379, false
f9b6e5803038fabb, started, etcd1, http://192.168.4.10:2380, http://192.168.4.10:2379, false

查看etcd1和etcd2中原有的测试数据:

[root@localhost ~]# ETCDCTL_API=2 etcdctl --endpoints="http://192.168.4.10:2379" get /docker-flannel/network/config
{
  "Network": "10.0.0.0/16",
  "SubnetLen": 24,
  "Backend": {
    "Type": "vxlan"
  }
}

[root@localhost ~]# ETCDCTL_API=2 etcdctl --endpoints="http://192.168.4.20:2379" get /docker-flannel/network/config
{
  "Network": "10.0.0.0/16",
  "SubnetLen": 24,
  "Backend": {
    "Type": "vxlan"
  }
}

添加新成员

添加成员的过程有两个步骤:

  1. 通过 HTTP members API 添加新成员到集群, gRPC members API, 或者 etcdctl member add 命令.
  2. 使用新的层原配置启动新成员,包括更新后的成员列表(以后成员加新成员)

使用 etcdctl 指定 name 和 advertised peer URLs 来添加新的成员etcd3:192.168.4.30到集群(在任意一台etcd执行都可以):

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379" member add etcd3 --peer-urls=http://192.168.4.30:2380
Member 9825b911c2558475 added to cluster  b9b6bab8c2110fd

ETCD_NAME="etcd3"
ETCD_INITIAL_CLUSTER="etcd2=http://192.168.4.20:2380,etcd3=http://192.168.4.30:2380,etcd1=http://192.168.4.10:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.4.30:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"

此时,再次查看etcd集群信息,可以看到http://192.168.4.30:2380处于unstarted状态:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379"  member list
39d7dd629f95330e, started, etcd2, http://192.168.4.20:2380, http://192.168.4.20:2379, false
9825b911c2558475, unstarted, , http://192.168.4.30:2380, , false
f9b6e5803038fabb, started, etcd1, http://192.168.4.10:2380, http://192.168.4.10:2379, false

etcdctl 已经给出关于新成员的集群信息并打印出成功启动它需要的环境变量,补全到etcd3机器中的/etc/etcd/etcd.conf文件:

ETCD_NAME="etcd3"
ETCD_DATA_DIR=/etc/etcd/data

ETCD_LISTEN_CLIENT_URLS=http://192.168.4.30:2379
ETCD_LISTEN_PEER_URLS=http://192.168.4.30:2380

ETCD_ADVERTISE_CLIENT_URLS=http://192.168.4.30:2379
ETCD_INITIAL_ADVERTISE_PEER_URLS=http://192.168.4.30:2380

ETCD_INITIAL_CLUSTER="etcd2=http://192.168.4.20:2380,etcd3=http://192.168.4.30:2380,etcd1=http://192.168.4.10:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
ETCD_INITIAL_CLUSTER_TOKEN=etcd-cluster

ETCD_ENABLE_V2=true

ETCD_INITIAL_CLUSTER_STATE修改成existing,视图加入已有的etcd集群。

修改后执行systemctl status etcd.service启动。

新成员将作为集群的一部分运行并立即开始赶上集群的其他成员。

如果添加多个成员,最佳实践是一次配置单个成员并在添加更多新成员前验证它正确启动。

如果添加新成员到一个节点的集群,在新成员启动前集群无法继续工作,因为它需要两个成员作为 follower才能在一致性上达成一致。这个行为仅仅发生在 etcdctl member add 影响集群和新成员成功建立连接到已有成员的时间内。

检查3台etcd的状况:

[root@localhost ~]# etcdctl --endpoints=http://192.168.4.10:2379,http://192.168.4.20:2379,http://192.168.4.30:2379 endpoint health
http://192.168.4.10:2379 is healthy: successfully committed proposal: took = 16.900029ms
http://192.168.4.30:2379 is healthy: successfully committed proposal: took = 17.184419ms
http://192.168.4.20:2379 is healthy: successfully committed proposal: took = 10.413913ms

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379"  member list
39d7dd629f95330e, started, etcd2, http://192.168.4.20:2380, http://192.168.4.20:2379, false
9825b911c2558475, started, etcd3, http://192.168.4.30:2380, http://192.168.4.30:2379, false
f9b6e5803038fabb, started, etcd1, http://192.168.4.10:2380, http://192.168.4.10:2379, false

查看etcd3中的数据是否已同步:

[root@localhost ~]# ETCDCTL_API=2 etcdctl --endpoints="http://192.168.4.30:2379" get /docker-flannel/network/config
{
  "Network": "10.0.0.0/16",
  "SubnetLen": 24,
  "Backend": {
    "Type": "vxlan"
  }
}

更新成员

更新 advertise client URLs

为了更新成员的 advertise client URLs,简单更新后的 client URL 标记(--advertise-client-urls)或者环境变量来重启这个成员(ETCD_ADVERTISE_CLIENT_URLS)。重启后的成员将自行发布更新后的URL。错误更新的client URL 将不会影响 etcd 集群的健康。

更新 advertise peer URLs

要更新成员的 advertise peer URLs, 首先通过成员命令更新它然后再重启成员。需要额外的行为是因为更新 peer URL 修改了集群范围配置并能影响 etcd 集群的健康。

要更新 peer URL,首先,我们需要找到目标成员的ID。使用 etcdctl 列出所有成员:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379"  member list
39d7dd629f95330e, started, etcd2, http://192.168.4.20:2380, http://192.168.4.20:2379, false
9825b911c2558475, started, etcd3, http://192.168.4.30:2380, http://192.168.4.30:2379, false
f9b6e5803038fabb, started, etcd1, http://192.168.4.10:2380, http://192.168.4.10:2379, false

在这个例子中,更新成员ID为9825b911c2558475(etcd3)并修改它的 peerURLs 值为 http://192.168.4.30:23800

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379" member update 9825b911c2558475 --peer-urls=http://192.168.4.30:23800
Member 9825b911c2558475 updated in cluster  b9b6bab8c2110fd

再次查看成员列表:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379"  member list
39d7dd629f95330e, started, etcd2, http://192.168.4.20:2380, http://192.168.4.20:2379, false
9825b911c2558475, started, etcd3, http://192.168.4.30:23800, http://192.168.4.30:2379, false
f9b6e5803038fabb, started, etcd1, http://192.168.4.10:2380, http://192.168.4.10:2379, false

删除成员

假设我们要删除的成员ID是 9825b911c2558475(etcd3)。可以用 remove 命令来执行删除:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379" member remove 9825b911c2558475
Member 9825b911c2558475 removed from cluster  b9b6bab8c2110fd

此时目标成员将停止自身并在日志中打印出移除信息,etcd服务一会停止:

etcd: the member has been permanently removed from the cluster

可以安全的移除 leader,当然在新 leader 被选举时集群将不活动(inactive)。这个持续时间通常是选举超时时间加投票过程。

那么,可以通过以下方式查看哪个节点是leader:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379" endpoint status --cluster -w table
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|         ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| http://192.168.4.30:2379 |  f131e3371e51a36 |  3.4.13 |   25 kB |     false |      false |        62 |     198958 |             198958 |        |
| http://192.168.4.20:2379 | 441a3fcaee433945 |  3.4.13 |   25 kB |      true |      false |        62 |     198958 |             198958 |        |
| http://192.168.4.10:2379 | f9b6e5803038fabb |  3.4.13 |   25 kB |     false |      false |        62 |     198958 |             198958 |        |
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

或者使用--cluster,更便捷,不必使用--endpoints标志来单独指定每个端点。:

[root@localhost ~]# etcdctl --endpoints="http://192.168.4.10:2379,http://192.168.4.20:2379,http://192.168.4.30:2379" endpoint status -w table
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|         ENDPOINT         |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| http://192.168.4.10:2379 | f9b6e5803038fabb |  3.4.13 |   25 kB |     false |      false |        62 |     198963 |             198963 |        |
| http://192.168.4.20:2379 | 441a3fcaee433945 |  3.4.13 |   25 kB |      true |      false |        62 |     198963 |             198963 |        |
| http://192.168.4.30:2379 |  f131e3371e51a36 |  3.4.13 |   25 kB |     false |      false |        62 |     198963 |             198963 |        |
+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

严格重配置检查模式 (-strict-reconfig-check)

如上所述,添加新成员的最佳实践是一次配置单个成员并在添加更多新成员前验证它正确启动。这个逐步的方式非常重要,因为如果最新添加的成员没有正确配置(例如 peer URL不正确),集群会丢失法定人数。发生法定人数丢失是因为最新加入的成员被法定人数计数,即使这个成员对其他已经存在的成员是无法访问的。同样法定人数丢失可能发生在有连接问题或者操作问题时。

为了避免这个问题,etcd 提供选项 -strict-reconfig-check. 如果这个选项被传递给 etcd, etcd 拒绝重配置请求, 如果启动的成员的数量将少于被重配置的集群的法定人数。

推荐开启这个选项。当然,为了保持兼容它被默认关闭。环境变量名称为:环境变量: ETCD_STRICT_RECONFIG_CHECK

参考:https://doczhcn.gitbook.io/etcd/index/index-1/clustering/runtime-configuration