系统学习 学习路径总入口:先选领域,再建立模型,最后用故障演练验证

用快照把 Kubernetes 控制面恢复到确定时点,并完成证书校准、验证与回滚

适用场景

本流程会停止所有控制平面节点的 kubelet,属于计划性停机操作。开始前要确认业务影响、维护窗口和决策人;工作节点无需停止,但如果业务仍在写外部系统,需另行冻结。

前置检查

  1. 确认快照可用。在宿主机上执行:
export ETCDCTL_API=3
export ETCDCTL_ENDPOINTS=https://127.0.0.1:2379
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/server.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/server.key
etcdutl snapshot status /backup/etcd.db --write-out=table
sha256sum -c /backup/etcd.db.sha256

快照的 revision、键数量和大小应与备份记录一致。不要用直接拷贝的 /var/lib/etcd 目录代替快照,运行中的 WAL 与 SST 文件可能不一致。

  1. 确认恢复工具与数据格式匹配。etcdutl 必须能读取目标快照,恢复时使用的参数要能复现集群拓扑。etcd 官方镜像不一定自带 etcdutl,宿主机上要提前准备好可用二进制。

  2. 记录当前拓扑和健康状态。只要 API 还能访问,就先留一份现场:

etcdctl --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 \
  endpoint status --cluster -w table
etcdctl --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 \
  member list -w table
kubectl get nodes -o wide
kubectl get ns

记录成员名、peer URL、集群 token、数据目录和证书路径。恢复时不能把不同快照、不同集群的成员混在一起。

  1. 检查证书、静态 Pod 清单和加密配置。快照只包含 etcd 数据,不包含 /etc/kubernetes/pki/etcd/、apiserver-etcd-client 证书,也不包含静态加密所用的 EncryptionConfiguration 与密钥。缺少这些文件,数据恢复成功也无法让控制面正常工作。提前从离线备份中取回,并核对文件权限。

  2. 检查磁盘空间、时间同步和端口。恢复会生成一份完整的新数据目录,至少需要与当前数据目录同量级的空闲空间;所有控制平面节点要保持时间同步,2379/2380 端口不能被旧进程占用。

  3. 冻结变更并通知相关方。停止发布、控制器调谐和自动化脚本,确认没有 CronJob 会在恢复窗口内重建被删对象。把维护窗口、恢复目标时点和回滚条件写进事故记录。

恢复步骤

1. 停止控制面

在每个控制平面节点上停止 kubelet,让 etcd、kube-apiserver、controller-manager 和 scheduler 全部退出:

sudo systemctl stop kubelet
sudo crictl ps | grep -E 'etcd|kube-apiserver|kube-controller-manager|kube-scheduler'

第二条命令应没有输出。不要只停一个节点就恢复,否则其他 apiserver 可能继续写入,造成恢复后数据再次分叉。

2. 保留旧数据目录

先保留现场,再准备空目录。禁止直接覆盖在线成员的数据目录:

sudo mv /var/lib/etcd /var/lib/etcd.pre-restore-$(date +%Y%m%d%H%M%S)
sudo install -d -m 0700 /var/lib/etcd

如果实际数据目录不是 kubeadm 默认路径,以静态 Pod 的 hostPath 为准。旧目录至少保留到验证完成,不要用 rm -rf 清理故障证据。

3. 在每个成员上执行快照恢复

所有成员使用同一份快照、同一个集群 token,但各自的 --name、peer URL 必须不同。下面以三成员为例:

sudo etcdutl snapshot restore /backup/etcd.db \
  --name cp-1 \
  --data-dir /var/lib/etcd \
  --initial-cluster cp-1=https://10.0.0.11:2380,cp-2=https://10.0.0.12:2380,cp-3=https://10.0.0.13:2380 \
  --initial-cluster-token prod-etcd-restore \
  --initial-advertise-peer-urls https://10.0.0.11:2380

在 cp-2、cp-3 上分别替换 --name 和 --initial-advertise-peer-urls。--initial-cluster 三台必须完全一致,token 也必须一致;命令中的 prod-etcd-restore 要换成静态 Pod 清单里原有的 --initial-cluster-token。单节点集群只恢复一个成员。

恢复完成后,按原数据目录的属主和属组修正权限:

sudo chown -R etcd:etcd /var/lib/etcd
sudo chmod 0700 /var/lib/etcd

若原环境以 root 运行 etcd,就不要改成 etcd 用户;以静态 Pod 清单和旧目录的实际属主为准。

4. 校准证书与静态清单

确认以下文件存在且与快照来源匹配:

sudo ls -l /etc/kubernetes/pki/etcd/
sudo ls -l /etc/kubernetes/pki/apiserver-etcd-client.crt \
  /etc/kubernetes/pki/apiserver-etcd-client.key
grep -n 'etcd-data\|--initial-cluster\|--initial-cluster-token' \
  /etc/kubernetes/manifests/etcd.yaml

从离线归档恢复证书时,先查看包内路径,再解压到 /etc/kubernetes,避免覆盖无关的 apiserver 证书。静态 Pod 清单中的 hostPath 必须指向刚恢复的数据目录;如果恢复前改过目录,要同步改回。

启用了静态加密的集群,还要把 EncryptionConfiguration 及其密钥恢复到 apiserver 能读取的位置。只恢复 etcd 数据而没有加密密钥,Secret 会变成无法解密的内容。

5. 启动控制面

在所有控制平面节点上启动 kubelet:

sudo systemctl start kubelet
sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -n 80 --no-pager

先确认 etcd 静态 Pod 已启动,再检查成员状态。etcd 需要多数派才能对外服务,三成员集群至少要有两台健康成员:

sudo crictl ps | grep etcd
etcdctl --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 \
  endpoint health --cluster

如果某个成员反复退出,先看该成员的容器日志,不要继续停止其他成员。常见原因是 data-dir 权限、peer 证书 SAN 不匹配、--initial-cluster 写错,或端口被旧进程占用。

验证

恢复后不要立即清理旧数据目录和日志。先把快照时间、revision、成员列表、验证结果和恢复耗时写进事故记录。

失败与回滚

部分成员启动失败。 保留失败成员的数据目录和日志,检查证书、URL、token、权限与磁盘。不要用另一个快照单独补这个成员;同一集群的所有成员必须来自同一快照。若只是单成员硬件故障,应在集群恢复后按正常的成员替换流程处理。

恢复后 apiserver 无法连接 etcd。 优先检查 /etc/kubernetes/pki/apiserver-etcd-client.* 与 etcd 的 CA 是否匹配,其次看 apiserver 日志中的 TLS 错误和 endpoint 地址。不要通过关闭证书校验来绕过问题。

快照损坏或数据格式不兼容。 停止所有控制面,换一份已校验的旧快照,所有成员重新恢复。不要尝试把不同快照的数据目录拼在一起,也不要在恢复过程中升级 etcd 或 Kubernetes。

恢复目标时点选错。 如果旧数据目录在故障前仍可服务,可以回退到旧目录:

sudo systemctl stop kubelet
sudo mv /var/lib/etcd /var/lib/etcd.restore-failed-$(date +%Y%m%d%H%M%S)
sudo mv /var/lib/etcd.pre-restore-* /var/lib/etcd
sudo systemctl start kubelet

这条回滚只适用于旧数据本身可用的情况。如果故障是逻辑损坏、误删或数据污染,回退到旧目录等于回到损坏状态;此时应改用另一份快照,而不是盲目回滚。

证书或加密密钥丢失。 先用离线备份恢复;无法恢复时,需要按集群 PKI 重建流程重新签发证书并重新加入节点。不要为了省事重建 etcd CA 后继续使用旧快照中的客户端证书,TLS 握手会持续失败。

验证失败但业务已恢复写入。 立即停止业务写入,重新评估快照时点。恢复后的新写入可能与快照后的数据冲突,不能简单再灌一次快照;应把新写入导出、隔离,再决定重新恢复还是人工合并。

相关命令

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. 为什么恢复 etcd 时必须停止所有控制平面节点,而不是只停故障成员?
  2. etcd 成员都启动了、但 apiserver 连不上 etcd,应该先检查哪几类文件或参数?
  3. 什么情况下“回滚到恢复前的数据目录”是错误操作?
答案
  1. 避免其他 apiserver 在恢复期间继续写入,造成恢复后数据再次分叉;多成员还必须用同一份快照恢复,不能让新旧数据混用。
  2. 先检查 apiserver-etcd-client 证书与 etcd CA 是否匹配、静态 Pod 的 endpoint 与 peer URL 是否正确,再看 apiserver 日志中的 TLS 和连接错误。
  3. 旧数据目录本身存在逻辑损坏、误删或污染时,回退只会回到坏状态;此时应改用另一份已校验快照,而不是盲目回滚。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令