系统学习 演练学习路径:35 个 lab 的难度排序、4 周基础计划与复盘方法

Lab 状态:实跑通过2026-10-25 · 3 节点 kind / 一次性 etcd

快照恢复与 quorum 重建通过

在一次性 Docker etcd 中演练快照校验、停止写入、恢复、回滚与键值一致性核对

演练目标

安全边界:本 Lab 只允许在一次性虚拟机或本地 Docker 中进行。生产 etcd 恢复要停止全部控制平面 kubelet,影响整个集群 API;不要在生产上执行 snapshot restore、替换数据目录或 --force-new-cluster。真实操作必须先备份证书、加密配置和旧数据目录,并由变更负责人确认快照时点和可丢失窗口。

环境准备

准备 Docker。实验使用一个不映射宿主机端口的 etcd 容器,客户端命令都通过 docker exec 在容器内执行,避免误连其他 etcd。镜像标签沿用现有 etcd Lab 中已验证的镜像;如果本地没有该镜像,换成你确认过来源和版本的 etcd 镜像。

mkdir -p /tmp/lab-etcd-restore/backup
docker version --format '{{.ServerVersion}}'
timeout 30 docker volume create lab-etcd-data
timeout 120 docker run -d --name lab-etcd \
  -v lab-etcd-data:/etcd-data \
  -v /tmp/lab-etcd-restore/backup:/backup \
  quay.io/coreos/etcd:v3.5.15 \
  /usr/local/bin/etcd \
  --name=lab \
  --data-dir=/etcd-data \
  --listen-client-urls=http://0.0.0.0:2379 \
  --advertise-client-urls=http://127.0.0.1:2379 \
  --listen-peer-urls=http://0.0.0.0:2380 \
  --initial-advertise-peer-urls=http://127.0.0.1:2380 \
  --initial-cluster=lab=http://127.0.0.1:2380 \
  --initial-cluster-token=lab-etcd-token \
  --initial-cluster-state=new
timeout 60 bash -c 'until docker exec lab-etcd etcdctl endpoint health; do sleep 2; done'
docker exec lab-etcd etcdctl endpoint status -w table

写入一组可识别的键,记录快照前的键值内容和 revision。不要只看“写入成功”,要把基线保存成文件,恢复后逐字对比:

timeout 30 docker exec lab-etcd etcdctl put /lab/before snapshot-time
timeout 30 docker exec lab-etcd etcdctl put /lab/config mode=baseline
docker exec lab-etcd etcdctl get /lab --prefix > /tmp/lab-etcd-restore/backup/before.txt
docker exec lab-etcd etcdctl get /lab --prefix --keys-only | grep -c .
docker exec lab-etcd etcdctl endpoint status -w table

保存快照并校验。快照文件通过 bind mount 出现在宿主机 /tmp/lab-etcd-restore/backup,再计算校验和;真实环境还要把快照复制到独立存储,不能只留在待恢复节点:

timeout 60 docker exec lab-etcd etcdctl snapshot save /backup/snapshot.db
docker exec lab-etcd etcdutl snapshot status /backup/snapshot.db -w table
sha256sum /tmp/lab-etcd-restore/backup/snapshot.db \
  | tee /tmp/lab-etcd-restore/backup/snapshot.db.sha256

如果镜像内没有 etcdutl,不要跳过校验;使用与 etcd 版本匹配的 etcdutl 二进制在宿主机或另一容器中校验,并记录版本来源。etcdctl snapshot status 在部分版本中已不再推荐,优先使用 etcdutl。

演练步骤

1. 制造快照后的写入

快照完成后继续写入,并修改一个已存在的键,模拟快照时间点之后发生的业务变更。恢复后这些变更应当消失:

timeout 30 docker exec lab-etcd etcdctl put /lab/after will-be-lost
timeout 30 docker exec lab-etcd etcdctl put /lab/before mutated-after-snapshot
docker exec lab-etcd etcdctl get /lab --prefix > /tmp/lab-etcd-restore/backup/after-writes.txt
docker exec lab-etcd etcdctl get /lab/before

2. 停止写入并保留旧数据

先停止 etcd 容器,确认客户端无法再写入,再准备恢复。旧容器和旧数据卷都保留,不执行 docker rm 或删除卷,它们是回滚材料:

timeout 60 docker stop lab-etcd
timeout 30 docker ps -a --filter name=lab-etcd --format '{{.Names}} {{.Status}}'
timeout 30 docker exec lab-etcd etcdctl put /lab/should-fail nope 2>/dev/null; echo "write_exit=$?"

3. 恢复到新的数据目录

创建一个全新的数据卷,把同一份快照恢复进去。不要覆盖旧卷,也不要把不同快照或不同集群的成员混在一起。单成员实验使用原成员名、原 token 和原 peer URL:

timeout 30 docker volume create lab-etcd-restored
timeout 120 docker run --rm \
  -v lab-etcd-restored:/etcd-data \
  -v /tmp/lab-etcd-restore/backup:/backup:ro \
  --entrypoint /usr/local/bin/etcdutl \
  quay.io/coreos/etcd:v3.5.15 \
  snapshot restore /backup/snapshot.db \
  --name=lab \
  --data-dir=/etcd-data \
  --initial-cluster=lab=http://127.0.0.1:2380 \
  --initial-cluster-token=lab-etcd-token \
  --initial-advertise-peer-urls=http://127.0.0.1:2380

4. 启动恢复后的成员

用恢复后的数据卷启动新容器,避免与旧容器同名。启动参数要沿用原集群的成员名、token 和 peer URL;initial-cluster-state 的具体取值以当前 etcd 版本的官方恢复文档为准,不能把本页命令直接套到生产集群:

timeout 120 docker run -d --name lab-etcd-restored \
  -v lab-etcd-restored:/etcd-data \
  quay.io/coreos/etcd:v3.5.15 \
  /usr/local/bin/etcd \
  --name=lab \
  --data-dir=/etcd-data \
  --listen-client-urls=http://0.0.0.0:2379 \
  --advertise-client-urls=http://127.0.0.1:2379 \
  --listen-peer-urls=http://0.0.0.0:2380 \
  --initial-advertise-peer-urls=http://127.0.0.1:2380 \
  --initial-cluster=lab=http://127.0.0.1:2380 \
  --initial-cluster-token=lab-etcd-token \
  --initial-cluster-state=existing
timeout 60 bash -c 'until docker exec lab-etcd-restored etcdctl endpoint health; do sleep 2; done'

观察现象

恢复后的成员应只有一个 leader,endpoint health 返回 healthy,alarm list 为空。读取 /lab 前缀时,/lab/before 应回到 snapshot-time,快照后的 mutated-after-snapshot 和 /lab/after 都不存在;这证明恢复回到了快照 revision,而不是把旧数据目录原样启动。

成功的判据是:成员健康且 leader 唯一;/lab 下的键值与快照前基线文件一致;快照后的写入消失;键数量与快照时点一致;revision 与 etcdutl snapshot status 记录的 revision 对齐,至少不包含快照后的写入;没有 NOSPACE、CORRUPT 等告警。

失败的判据是:成员反复退出、没有 leader 或 health 超时;键值缺失、顺序和内容与基线不一致;快照后的键仍然存在,说明启动的是旧数据目录或拿错快照;出现告警;恢复后仍有客户端向旧成员写入。

判断与定位

  1. 先看成员是否真的启动:docker ps -a、容器日志、endpoint status、alarm list。容器 Running 不代表 etcd 已形成 leader。
  2. 对比快照记录和当前状态:etcdutl snapshot status 的 revision、键数量、大小,与恢复后 endpoint status 和 get --prefix 的结果对照。差异明显时先查是不是拿错了快照或数据目录。
  3. 键值对不上时,先查恢复目标卷是否正确挂载,再查启动命令中的 --data-dir、成员名、token 和 peer URL。不要用 rm -rf 清掉旧目录来“重试”,那会破坏回滚和取证材料。
  4. 容易误判:单成员恢复成功不代表多成员生产集群能直接照做;生产必须所有成员用同一份快照,并先停止全部控制平面 kubelet,避免其他 API Server 继续写入。
  5. 快照不包含证书、EncryptionConfiguration 和静态加密密钥。恢复后 etcd 健康、kubectl 却连不上或 Secret 无法解密时,要查这些文件,而不是反复恢复数据。
  6. 成员启动失败常见原因是数据目录权限、peer 证书 SAN、token 或 initial-cluster 不一致、2379/2380 被旧进程占用。先保留日志和旧目录,再逐项排查。

恢复与回滚

如果恢复后的成员无法启动,或验证发现拿错快照,先停止新容器,保留新卷和日志,再启动旧容器回到恢复前状态:

timeout 60 docker stop lab-etcd-restored 2>/dev/null || true
timeout 60 docker start lab-etcd
timeout 60 bash -c 'until docker exec lab-etcd etcdctl endpoint health; do sleep 2; done'
docker exec lab-etcd etcdctl get /lab/before

这条回滚只适用于旧数据目录本身仍然可用的情况。如果故障是逻辑损坏、误删或数据污染,回到旧目录等于回到坏状态,应改用另一份已校验的快照,而不是盲目回滚。生产环境回滚前还要确认没有业务已经向恢复后的集群写入;一旦写入,重新灌快照会产生冲突,必须先把新写入导出、隔离,再决定重新恢复还是人工核对。

如果快照损坏,停止所有成员,换一份校验通过的快照重新恢复,所有成员必须来自同一份快照。不要尝试拼接不同快照的数据目录,也不要在恢复过程中升级 etcd 或 Kubernetes。若证书或加密密钥丢失,先从离线备份恢复;无法恢复时按集群 PKI 重建流程处理,不要关闭证书校验来绕过问题。

实验结束后,确认恢复结果已验证、快照和记录已保存,再清理本实验的容器和卷:

timeout 60 docker rm -f lab-etcd lab-etcd-restored 2>/dev/null || true
timeout 60 docker volume rm lab-etcd-data lab-etcd-restored 2>/dev/null || true
rm -rf /tmp/lab-etcd-restore

恢复后的数据一致性检查包括:对比快照前与恢复后的完整键值文件;核对 /lab 键数量和 revision;确认快照后的写入和修改都已消失;检查告警和成员列表;如果是在 Kubernetes 环境,还要核对 Namespace、CRD、关键对象数量和 Secret 解密是否正常。所有差异都要能由“快照时点 + 可接受的丢失窗口”解释。

验证

按成员、数据、快照、上层集群四个层次验证,所有可能阻塞的命令都加超时:

timeout 30 docker exec lab-etcd-restored etcdctl endpoint health
docker exec lab-etcd-restored etcdctl endpoint status -w table
docker exec lab-etcd-restored etcdctl alarm list
docker exec lab-etcd-restored etcdctl get /lab --prefix > /tmp/lab-etcd-restore/restored.txt
diff -u /tmp/lab-etcd-restore/backup/before.txt /tmp/lab-etcd-restore/restored.txt
docker exec lab-etcd-restored etcdctl get /lab/after | wc -l
docker exec lab-etcd-restored etcdctl get /lab --prefix --keys-only | grep -c .

复盘

相关命令

参考链接

读完自测

  1. 为什么 etcd 恢复前要停止所有写入方,而不是只停止待恢复的成员?
  2. 恢复后 etcd 健康、但 Kubernetes 的 Secret 无法解密,最可能缺少什么?
  3. 什么情况下不能“回滚到恢复前的数据目录”?应该改用哪种恢复材料?
答案
  1. 只要还有其他 API Server 或客户端能写入,恢复期间就会产生新数据,恢复完成后会再次分叉。生产环境要停止所有控制平面 kubelet,并确认没有其他写入方,再让所有成员用同一份快照恢复。
  2. 快照只包含 etcd 键值数据,不包含 EncryptionConfiguration 和静态加密密钥。缺少这些文件时 etcd 本身健康,但 Secret 数据无法解密;应从离线备份恢复加密配置和密钥。
  3. 旧数据目录本身存在逻辑损坏、误删或污染时,回退只会回到坏状态。此时应使用另一份已校验的快照重新恢复所有成员,而不是把旧目录重新启动。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令