etcd — 没有快照,就没有集群
一句话总结
通过 kubectl get 看到的一切,都是从 etcd 读取的值。失去 etcd,就等于失去集群。
为什么需要了解这些
增加控制平面节点数量会让人感到安心。有一份记录讲述作者把家庭实验室从 3 个节点扩展到 7 个节点,并将控制平面和 etcd 成员都配置为 3 个。3 个成员的多数派是 2,因此即使一台机器宕机,集群仍能存活。然而,那篇记录最后留下一句话:法定人数可以防故障,却不能防误操作。
这句话就是本模块的起点。节点宕机可以通过复制来抵御,但误执行 kubectl delete ns payments 却无法靠复制阻止。删除是一项正常写入,三个 etcd 成员会认真地把这次删除复制成三份。即使有 100 个成员,结果也一样。唯一能让时间倒退的手段,是该时间点之前的快照。
同一篇记录中还有另一个案例。三台控制平面节点全部正常,但第一台节点宕机后,kubectl 和 kubelet 却都无法连接。原因是 controlPlaneEndpoint 指向第一台节点的物理 IP,而不是 VIP。etcd 法定人数与 API 可用性是两个不同的问题。由此可见,“已经做了冗余,所以没问题”的感觉经常是错的。只有亲手执行过一次恢复流程的人,才知道系统真正保护了什么。
工作原理
etcd 备份不是复制文件,而是完整保存某个一致的时间点。
etcdctl snapshot save /backup/snap.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
生产环境中的 etcd 只接受启用了客户端认证的 TLS 连接,因此总会伴随三个证书相关参数。无需背诵路径。在 kubeadm 集群中,etcd 以静态 Pod 运行,所以 kubectl describe pod etcd-<노드> -n kube-system 的执行参数里就写着正确答案。本实验环境中的 etcd 没有启用客户端 TLS,而是在 127.0.0.1:2379 上监听明文连接,因此会省略这三个参数。 不过,在编写定期备份 CronJob 的步骤中,仍会完整写出生产环境的形式。
创建快照后,必须进行验证。
etcdutl snapshot status /backup/snap.db -w json
# {"hash":3106878859,"revision":12450,"totalKey":1287,"totalSize":5779456}
这三个数字都有意义。revision 表示快照对应的时间点,totalKey 表示其中包含的对象规模,hash 则用于验证文件完整性。每天都“正常”生成 0 字节文件的备份系统,比想象中更常见。未经验证的备份不是备份,只是对备份的希望。
恢复流程方向相反,但有一个决定性区别。snapshot restore 不是把数据退回原状,而是创建一个新的 etcd 集群。因此,它会接收 --name、--initial-cluster、--initial-advertise-peer-urls、--initial-cluster-token 等身份参数。而且,--data-dir 必须是一个全新的空目录。直接指定现有的 /var/lib/etcd 会导致失败;更糟糕的是,还可能触碰仍在使用的数据。
恢复顺序也是固定的。
| 顺序 | 操作 | 原因 |
|---|---|---|
| 1 | 验证快照 | 用不可用的文件重建集群,只会造成第二次故障 |
| 2 | 停止 kube-apiserver | 恢复期间不能继续写入 |
| 3 | snapshot restore | 创建新的数据目录 |
| 4 | 将 etcd 清单中的 hostPath 改为新路径 | 遗漏后会再次从旧数据启动 |
| 5 | 启动 apiserver,并通过 kubectl get 确认 |
是否恢复成功应通过 API 判断 |
生产现场中的常见情况
第一,备份周期就是最大数据损失量。 每 6 小时备份一次,最坏情况下会丢失 6 小时的变更。应先确定恢复点目标(RPO),再据此设定周期,而不是反过来。
第二,把备份文件放在同一块磁盘上。 节点磁盘损坏时,快照也会一起丢失。作者的家庭实验室同样专门设置了将备份转移到 NAS 的路径。还必须制定保留策略——如果缺少 find /backup -mtime +7 -delete 这一行,几个月后磁盘就会写满。
第三,恢复后的证书与时间差。 快照只包含对象。节点的 kubelet 证书、令牌,以及快照后创建的真实容器,都属于快照之外的世界。恢复后集群暂时显得混乱是正常现象,随后控制器会让它逐步收敛到声明的状态。
下一次实验要做什么
第一个实验将检查正在运行的 etcd 的健康状态和成员,连续创建两次快照并观察 revision 增长,然后编写定期备份 CronJob 和快照验证脚本。第二个实验会实际删除对象,把快照恢复到新的数据目录,再使用这些数据亲自启动第二个 etcd 进程,亲眼确认被删除的值重新出现。