用快照把误删事故撤回来
目标
实际删除对象后,再从快照中将其恢复。使用恢复的数据目录亲自启动第二个 etcd,亲眼确认“确实恢复了”。
为什么重要
只在文档中了解恢复流程,与亲自实践一次有很大区别。尤其是,snapshot restore 不是还原数据的命令,而是创建新 etcd 集群的命令,这一点只有亲手实践才会真正记住。因此,它要求 --name、--initial-cluster、--initial-advertise-peer-urls、--initial-cluster-token 等身份标志,并通过 --data-dir 要求一个空的新路径。一旦直接指定现有的 /var/lib/etcd,就会触碰正在使用的数据。另一个重要方面是顺序。先验证快照,再停止 apiserver 以切断写入,然后执行恢复,将清单中的 hostPath 改为新路径,最后使用 kubectl 检查。如果颠倒顺序,可能会用不可用的快照启动集群、丢失恢复期间进入的写入,或再次从旧数据启动。本实验不会停止实时集群,而是在独立端口启动恢复副本,以得出相同结论。
步骤
- 创建命名空间
etcd-lab,并在其中创建 ConfigMapetcd-lab-marker(data.owner=platform)。然后将事故前状态以 JSON 记录到/root/etcd/restore/out/pre.json。包含三个键:marker_owner(值为platform)、registry_keys(/registry下的键数量,不少于 20)、revision(当前 etcd revision,大于 0)。 - 将用于恢复的快照保存到
/root/etcd/restore/before.db(不少于 20000 字节),并将验证结果以 JSON 保存到/root/etcd/restore/out/before.json。revision必须大于 0。 - 删除 ConfigMap
etcd-lab-marker以复现事故。删除后,将查询该对象的输出保存到/root/etcd/restore/out/gone.txt。文件中必须包含类似NotFound或not found的“不存在”响应,而且实时集群中不能重新出现该 ConfigMap。 - 将
before.db恢复到/root/etcd/restore/data。恢复后必须生成/root/etcd/restore/data/member/snap/db和/root/etcd/restore/data/member/wal。将恢复命令的输出(包括标准错误)保存到/root/etcd/restore/out/restore.txt。 - 这次带上所有身份标志,再恢复一次到
/root/etcd/restore/data2。必须包含--data-dir、--name、--initial-cluster、--initial-advertise-peer-urls、--initial-cluster-token五个标志;名称设为restored,peer 地址设为http://127.0.0.1:12380,token 设为etcd-restore-lab。将实际执行的完整命令原样保存到/root/etcd/restore/out/restore-cmd.txt(--data-dir不能写/var/lib/etcd)。 - 在后台启动以
/root/etcd/restore/data2为数据目录的第二个 etcd 进程。客户端地址为http://127.0.0.1:12379,peer 地址为http://127.0.0.1:12380,名称为restored,初始成员为restored=http://127.0.0.1:12380,token 为etcd-restore-lab。**在评分结束前不要终止此进程。**将启动命令保存到/root/etcd/restore/out/second-etcd.txt(文件中必须显示12379和--data-dir)。 - 从恢复副本读取键
/registry/configmaps/etcd-lab/etcd-lab-marker,确认值中包含platform,并将输出保存到/root/etcd/restore/out/recovered.txt。查询目标是127.0.0.1:12379,实时集群(2379)中仍不能存在该对象。 - 在
/root/etcd/restore/runbook.md中编写不少于 400 字节的恢复运行手册。每个步骤放在不同行,并遵守以下顺序:(1)验证快照(snapshot status),(2)停止kube-apiserver,(3)执行snapshot restore,(4)将 etcd 清单的hostPath/data-dir替换为新路径,(5)使用kubectl get检查。最后还必须写明恢复失败时的回滚方法。
参考
- 快照验证和恢复的标准工具是
etcdutl。先用command -v etcdutl检查;如果存在,则使用etcdutl snapshot status/etcdutl snapshot restore,否则改用etcdctl的相同子命令(会有警告,但可以工作)。 - etcd 服务器可执行文件是独立的。先用
command -v etcd查找;如果没有,则使用find "$HOME/.kwok" -name etcd -type f查找(kwokctl 下载的二进制文件位于其下)。后台启动可以使用nohup <etcd 경로> <플래그들> > /root/etcd/restore/out/second-etcd.log 2>&1 &的形式。 - 如果第二个 etcd 的端口与实时 etcd(2379/2380)重叠,启动会失败。务必使用 12379/12380,并让进程保持运行,直到第 6、7 步评分结束。
- 恢复日志和
kubectl的 NotFound 消息都输出到标准错误。必须使用명령 > 파일 2>&1或명령 2>&1 | tee 파일接收,才能保存在文件中。 - etcd 中存储的值是 protobuf,直接查看会显得乱码。使用
| tr -d '\0'仅删除空字节后,里面的字符串仍可直接读取。 - 常见错误 1:在第 8 步运行手册中将
etcdctl snapshot restore --data-dir ...写在同一行。这样snapshot restore和data-dir会出现在同一行,导致顺序检查失败。请拆成多行,或将 hostPath 替换单独列为一项。出于同样原因,不要在文档前部提前提到data-dir、kubectl get、kube-apiserver——顺序是按每个词的首次出现位置判断的。 - 常见错误 2:在第 3 步重新创建实时集群中的 ConfigMap。本实验的结论是“只在恢复副本中复活”。
- 实验 Pod 每次实验都会重新创建,因此上一实验创建的
etcd-lab命名空间和标记不会保留。请在第 1 步亲自创建后再开始。这正是运维流程必须通过运行手册和清单保存,而不能只靠记忆的原因。
用数字记录事故前状态
创建命名空间 etcd-lab,并在其中创建 ConfigMap etcd-lab-marker(data.owner=platform)。然后将事故前状态以 JSON 记录到 /root/etcd/restore/out/pre.json。包含三个键:marker_owner(值为 platform)、registry_keys(/registry 下的键数量,不少于 20)、revision(当前 etcd revision,大于 0)。
恢复从了解“之前存在什么”开始。将标记值、/registry 键数量和当前 revision 三项汇总到一个 JSON 中。revision 位于 endpoint status 的响应头中。
获取并验证用于恢复的快照
将用于恢复的快照保存到 /root/etcd/restore/before.db(不少于 20000 字节),并将验证结果以 JSON 保存到 /root/etcd/restore/out/before.json。revision 必须大于 0。
恢复快照创建后应立即验证。等事故发生后才发现文件损坏,就已经太晚了。
复现删除事故并留下记录
删除 ConfigMap etcd-lab-marker 以复现事故。删除后,将查询该对象的输出保存到 /root/etcd/restore/out/gone.txt。文件中必须包含类似 NotFound 或 not found 的“不存在”响应,而且实时集群中不能重新出现该 ConfigMap。
删除对象后再尝试查询。查询不存在的资源时,消息会输出到标准错误,因此重定向时必须同时包含标准错误,才能保存到文件中。
将快照恢复到新数据目录
将 before.db 恢复到 /root/etcd/restore/data。恢复后必须生成 /root/etcd/restore/data/member/snap/db 和 /root/etcd/restore/data/member/wal。将恢复命令的输出(包括标准错误)保存到 /root/etcd/restore/out/restore.txt。
恢复不是覆盖现有路径,而是创建新路径。恢复日志也输出到标准错误。请确认结果目录中生成了 member 结构。
带上集群身份标志再次恢复
这次带上所有身份标志,再恢复一次到 /root/etcd/restore/data2。必须包含 --data-dir、--name、--initial-cluster、--initial-advertise-peer-urls、--initial-cluster-token 五个标志;名称设为 restored,peer 地址设为 http://127.0.0.1:12380,token 设为 etcd-restore-lab。将实际执行的完整命令原样保存到 /root/etcd/restore/out/restore-cmd.txt(--data-dir 不能写 /var/lib/etcd)。
restore 实际上是“创建一个新集群”的命令。明确指定名称、初始成员列表、peer 地址和 token 四项后,就能使用这些数据启动 etcd。
使用恢复副本启动第二个 etcd
在后台启动以 /root/etcd/restore/data2 为数据目录的第二个 etcd 进程。客户端地址为 http://127.0.0.1:12379,peer 地址为 http://127.0.0.1:12380,名称为 restored,初始成员为 restored=http://127.0.0.1:12380,token 为 etcd-restore-lab。**在评分结束前不要终止此进程。**将启动命令保存到 /root/etcd/restore/out/second-etcd.txt(文件中必须显示 12379 和 --data-dir)。
只有将恢复的数据目录接入实际进程,才能完成确认。客户端和 peer 端口不能与实时 etcd 重叠,并在评分结束前保持进程运行。
确认已删除对象在恢复副本中仍然存在
从恢复副本读取键 /registry/configmaps/etcd-lab/etcd-lab-marker,确认值中包含 platform,并将输出保存到 /root/etcd/restore/out/recovered.txt。查询目标是 127.0.0.1:12379,实时集群(2379)中仍不能存在该对象。
Kubernetes 对象的 etcd 键为 /registry/<종류>/<네임스페이스>/<이름>。值采用 protobuf 格式,人类难以直接阅读,但字符串仍会原样显示。
编写恢复运行手册
在 /root/etcd/restore/runbook.md 中编写不少于 400 字节的恢复运行手册。每个步骤放在不同行,并遵守以下顺序:(1)验证快照(snapshot status),(2)停止 kube-apiserver,(3)执行 snapshot restore,(4)将 etcd 清单的 hostPath/data-dir 替换为新路径,(5)使用 kubectl get 检查。最后还必须写明恢复失败时的回滚方法。
这是一份凌晨 3 点也要能读懂的文档。步骤顺序很重要,还必须写明失败时返回到哪里。