灾难恢复

etcd v3 快照与恢复功能

etcd 设计为能够承受机器故障。etcd 集群可以自动从临时故障(例如,机器重启)中恢复,并且在 N 个成员的集群中最多容忍 (N-1)/2 个永久性故障。当一个成员永久性故障时,无论是由于硬件故障还是磁盘损坏,它都会失去对集群的访问权限。如果集群永久性地失去了超过 (N-1)/2 个成员,则会灾难性地失败,不可逆转地失去法定人数。一旦失去法定人数,集群将无法达成共识,因此无法继续接受更新。

为了从灾难性故障中恢复,etcd v3 提供了快照和恢复功能,以重新创建集群而不会丢失 v3 键数据。要恢复 v2 键,请参阅v2 管理指南

对键空间进行快照

恢复集群首先需要从 etcd 成员处获取键空间的快照。可以通过使用 etcdctl snapshot save 命令从活动成员处获取快照,或者通过复制 etcd 数据目录中的 member/snap/db 文件来获取快照。例如,以下命令将由 $ENDPOINT 服务的键空间快照保存到文件 snapshot.db 中:

$ ETCDCTL_API=3 etcdctl --endpoints $ENDPOINT snapshot save snapshot.db

请注意,从 member/snap/db 文件获取快照可能会丢失尚未写入但已包含在 wal(预写日志)文件夹中的数据。

快照的状态

要了解给定快照包含的修订版本和哈希值,可以使用 etcdutl snapshot status 命令:

$ etcdutl snapshot status snapshot.db -w table
+---------+----------+------------+------------+
|  HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| 7ef846e |   485261 |      11642 |      94 MB |
+---------+----------+------------+------------+

恢复集群

修订版本差异

在恢复集群时,现有客户端可能会感知到修订版本回退了几百或几千次。这是因为在给定快照中只包含直到快照被创建时的数据谱系,而当前状态可能已经更进一步。

这在使用 etcd 运行 Kubernetes 时尤其是一个问题,其中控制器和操作器可能会使用所谓的 informers 作为本地缓存,并通过 watch 通知更新。恢复到较旧的修订版本可能无法正确刷新缓存,导致控制器出现不可预测和不一致的行为。

在以下情况下从快照恢复时,强烈建议使用“修订版本提升”:已知的 watch API 消费者、etcd 数据的本地缓存副本或在一般情况下使用 Kubernetes。

从快照恢复

要恢复集群,只需要一个快照“db”文件。使用 etcdutl snapshot restore 恢复集群会创建新的 etcd 数据目录;所有成员应使用相同的快照进行恢复。恢复会覆盖一些快照元数据(特别是成员 ID 和集群 ID);成员会失去其先前的身份。这种元数据覆盖防止新成员意外加入现有集群。因此,为了从快照启动集群,必须启动一个新的逻辑集群。

简单的恢复可以这样执行:

$ etcdutl snapshot restore snapshot.db --data-dir output-dir

完整性检查

可以在恢复时选择验证快照的完整性。如果使用 etcdctl snapshot save 创建快照,它将具有完整性哈希,该哈希将由 etcdutl snapshot restore 进行检查。如果从数据目录复制快照,则没有完整性哈希,只能使用 --skip-hash-check 进行恢复。

使用修订版本提升进行恢复

为了确保在恢复后修订版本不会减少,您可以提供--bump-revision选项。此选项接受一个 64 位整数,表示要添加到快照当前修订版本的修订数量。由于每次写入 etcd 都会使修订版本增加 1,因此假设 etcd 每秒写入次数少于 1500 次,您可以通过增加 1'000'000'000 来覆盖一周前的快照。

在 Kubernetes 控制器的上下文中,还需要使用--mark-compacted将所有修订版本(包括增加的部分)标记为已压缩。这可以确保所有监视器被终止,并且 etcd 不会响应关于快照之后发生的修订版本的请求——从而有效地使 informer 缓存失效。

完整的调用可能如下所示:

$ etcdutl snapshot restore snapshot.db --bump-revision 1000000000 --mark-compacted --data-dir output-dir

使用更新的成员身份进行恢复

etcd 集群的成员存储在 etcd 本身中,并通过 raft 共识算法进行维护。当完全失去法定人数时,您可能需要重新考虑新集群的形成位置和方式,例如,在一组全新的成员上。

从快照恢复时,您可以直接将新的成员信息提供给数据存储,如下所示:

$ etcdutl snapshot restore snapshot.db \
  --name m1 \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380

这样可以确保新构建的集群仅连接到具有给定令牌的其他恢复成员,而不是仍然存活并尝试连接的旧成员。

或者,在启动 etcd 时,您可以提供--force-new-cluster以覆盖集群成员身份,同时保留现有应用程序数据。请注意,强烈不建议这样做,因为如果之前的集群成员仍然存活,这将导致崩溃。请确保定期保存快照。

端到端示例

从活动集群中获取快照,使用以下命令:

$ etcdctl snapshot save snapshot.db

继续前面的例子,以下命令为三成员集群创建新的 etcd 数据目录(m1.etcdm2.etcdm3.etcd):

$ etcdutl snapshot restore snapshot.db \
  --name m1 \
  --data-dir m1_data_dir.etcd
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380
$ etcdutl snapshot restore snapshot.db \
  --name m2 \
  --data-dir m2_data_dir.etcd
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host2:2380
$ etcdutl snapshot restore snapshot.db \
  --name m3 \
  --data-dir m3_data_dir.etcd
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host3:2380

接下来,使用新的数据目录启动etcd

$ etcd \
  --name m1 \
  --data-dir m1_data_dir.etcd
  --listen-client-urls http://host1:2379 \
  --advertise-client-urls http://host1:2379 \
  --listen-peer-urls http://host1:2380 &
$ etcd \
  --name m2 \
  --data-dir m2_data_dir.etcd
  --listen-client-urls http://host2:2379 \
  --advertise-client-urls http://host2:2379 \
  --listen-peer-urls http://host2:2380 &
$ etcd \
  --name m3 \
  --data-dir m3_data_dir.etcd
  --listen-client-urls http://host3:2379 \
  --advertise-client-urls http://host3:2379 \
  --listen-peer-urls http://host3:2380 &

现在,恢复的 etcd 集群应该可用,并从快照中提供键空间服务。

从 etcd v3.6 开始,用户只能使用etcdctl将数据导出到快照,但使用etcdutl从快照中恢复数据。如果没有指定--data-dir,则默认的--data-dir值为<name>.etcd(其中<name>--name的值)。例如,如果未提供--data-dir并且成员名称为m1m2m3,则--data-dir目录将是m1.etcdm2.etcdm3.etcd