Kubernetes 基础体系 · 第 74/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes 灾难恢复:控制面、资源、数据、DNS、依赖和演练
Kubernetes 灾难恢复不是“把 YAML 重新执行一遍”。一个运行中的集群同时包含控制面状态、工作负载资源、持久化数据、网络与 DNS、身份凭据,以及集群外部的数据库、镜像仓库和云服务。不同部分的备份方式、恢复顺序和一致性边界都不同。
如果只备份了 etcd,控制面对象可能可以恢复,但卷中的业务数据、外部 DNS 记录和云负载均衡器不一定存在;如果只备份了 PVC 对象,PVC 所指向的块存储数据仍可能已经丢失。因此,灾难恢复首先要回答三个问题:
- 恢复什么状态:控制面、资源对象、数据,还是整个业务系统?
- 恢复到哪个时间点:允许丢失多长时间的数据?
- 恢复后如何证明系统真的可用:API Server 能响应并不等于业务恢复。
一、先定义恢复目标:RPO、RTO 和一致性
**RPO(Recovery Point Objective,恢复点目标)**表示灾难发生后,业务最多能接受丢失多长时间的数据。例如 RPO 为 15 分钟,意味着恢复结果可以退回到灾难前不超过 15 分钟的状态。
**RTO(Recovery Time Objective,恢复时间目标)**表示从灾难发生到业务达到可用状态允许经过的最长时间。例如 RTO 为 60 分钟,意味着备份、控制面恢复、节点恢复、数据挂载、DNS 切换和验证必须在一小时内完成。
RPO 和备份周期之间存在直接关系,但不是简单的等式。设:
- 是备份周期;
- 是备份延迟;
- 是备份系统或存储系统发生故障导致的额外缺口。
在理想情况下,最坏数据丢失量近似为:
例如每 15 分钟创建一次数据库备份,但备份需要 4 分钟才能上传到异地对象存储,那么灾难发生在下一次备份刚开始时,最坏丢失时间可能接近 19 分钟;若对象存储同步还可能延迟 10 分钟,则实际目标可能接近 29 分钟。
但是,Kubernetes 对象备份的 RPO 和业务数据的 RPO 不是同一个值:
- Deployment、Service、ConfigMap 等对象通常可以通过 Git、etcd 快照或 Velero 备份恢复;
- 数据库事务、消息队列偏移量和对象存储对象需要各自的数据保护机制;
- 一个较新的 Deployment 对象可能引用一个较旧的数据库备份,恢复后业务仍然可能不一致。
因此应定义恢复一致性集合:
其中:
- :控制面状态,例如 etcd;
- :Kubernetes 资源对象;
- :业务数据;
- :DNS、入口和网络状态;
- :外部依赖,例如数据库、镜像仓库、身份系统和云资源。
一次真正可用的恢复必须让这些状态在时间和依赖关系上相容,而不是只恢复其中一个集合。
二、灾难恢复涉及哪些状态
Kubernetes 中“集群状态”至少有以下几层。
1. 控制面状态
控制面通常包含:
kube-apiserver:提供 Kubernetes API;etcd:保存 Kubernetes 对象的权威持久化状态;kube-scheduler:为待调度 Pod 选择节点;kube-controller-manager:根据期望状态创建或修正实际状态;- 证书、加密配置、审计配置和控制面静态 Pod 配置。
其中,etcd 保存的是 Kubernetes API 对象,不是所有 Kubernetes 运行时数据。例如:
- Pod 的期望配置在 etcd 中;
- Pod 内的文件系统不在 etcd 中;
- 容器镜像不在 etcd 中;
- 节点上的 kubelet 本地目录不在 etcd 中;
- CSI 卷中的数据库文件不在 etcd 中。
2. 资源对象状态
资源对象是 API Server 暴露的对象,例如:
- Namespace;
- Deployment、StatefulSet、DaemonSet、Job;
- Service、Ingress、Gateway;
- ConfigMap、Secret;
- PVC、StorageClass;
- ServiceAccount、Role、RoleBinding;
- CRD 以及由 CRD 定义的自定义资源。
这些对象通常可以通过 API 重新创建,但重新创建不代表语义完全相同。对象中的 status、UID、resourceVersion、生成字段和控制器维护字段通常不应被直接当作用户配置恢复。
3. 数据状态
数据可能存在于多个位置:
- PersistentVolume 对应的云盘、分布式存储或 NFS;
- 数据库自身的 WAL、归档日志和备份;
- 对象存储;
- 消息队列;
- 节点本地磁盘;
- Pod 的
emptyDir和容器可写层。
其中 emptyDir 和容器可写层一般属于临时数据。Pod 被删除或节点损坏后,它们通常不能恢复。若业务把重要数据写入这些位置,Kubernetes 资源备份无法弥补这个设计缺陷。
4. 集群外状态
Kubernetes 可能只保存了外部资源的引用:
Service的LoadBalancer状态由云控制器管理;StorageClass只是声明存储参数,不能替代存储系统中的数据;Secret可能包含访问外部数据库的凭据,但不包含数据库本身;- Ingress 或 Gateway 对象不一定包含云厂商负载均衡器的全部配置;
- DNS 记录可能由外部 DNS 服务托管;
- 镜像可能位于独立的镜像仓库。
灾难恢复计划必须明确哪些资源由 Kubernetes 管理,哪些资源由云平台、SaaS 或专用运维系统管理。
三、控制面恢复:etcd 是权威状态,但不是完整业务备份
3.1 etcd 快照保存什么
etcd 快照是某一时刻 etcd 键空间的持久化副本。对于使用 etcd 的 Kubernetes 集群,它通常包含 Kubernetes API 对象,因此可以恢复:
- Namespace;
- Deployment、Service、Secret、ConfigMap;
- CRD 和自定义资源;
- PVC 对象;
- Lease 等控制器协调数据。
它不能恢复:
- PV 后端卷中的业务数据;
- 节点本地文件;
- 容器镜像;
- kubelet 的运行时缓存;
- 外部 DNS 和数据库;
- 未写入 etcd 的临时运行状态。
etcd 快照还与集群身份、加密配置和版本兼容性有关。生产环境不能只保留一个 snapshot.db 文件,而应同时保存:
- etcd 版本或兼容矩阵;
- Kubernetes 版本;
- etcd 启动参数;
- peer/client 证书;
--encryption-provider-config对应的加密配置和密钥;- 控制面静态 Pod 清单;
- 快照校验值;
- 快照的创建时间和来源集群标识。
如果 Kubernetes Secret 使用了静态数据加密,恢复 etcd 数据但丢失加密配置,API Server 可能无法正确读取这些对象。反过来,只有加密配置而没有对应的密钥,也无法解密旧数据。
3.2 生产快照示例
在执行快照前,需要确认:
- 使用的
etcdctl与 etcd 版本兼容; - 客户端证书具有读取权限;
- 磁盘空间足够;
- 快照文件会被复制到独立故障域;
- 备份过程不会只写入与 etcd 相同的磁盘或节点。
示例:
export ETCDCTL_API=3
etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
endpoint health
etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
snapshot save /var/backups/etcd-snapshot.db
etcdutl snapshot status /var/backups/etcd-snapshot.db
sha256sum /var/backups/etcd-snapshot.db
endpoint health 成功只能说明客户端能够访问当前端点,不代表快照已复制到异地,也不代表快照可以恢复。etcdutl snapshot status 用于检查快照元数据和状态;具体命令名称和参数会随 etcd 版本变化,必须以所使用版本的工具文档为准。较新的 etcd 版本推荐使用 etcdutl 执行快照恢复相关操作,不能假设所有发行版中的 etcdctl 都提供相同子命令。
3.3 etcd 恢复的核心步骤
假设原控制面已经损坏,恢复流程的关键不是“把快照复制回去”,而是建立一个新的、只有一个初始成员身份的 etcd 数据目录,然后让 API Server 连接它。
典型流程如下:
- 停止或隔离旧的 API Server、Controller Manager 和 Scheduler,避免旧控制面继续写入;
- 准备与目标 etcd 版本兼容的工具;
- 从异地存储下载快照;
- 校验 SHA-256 或对象存储校验信息;
- 将快照恢复到新的数据目录;
- 使用恢复后的 etcd 启动参数启动 etcd;
- 启动 API Server;
- 检查 API 对象、控制器、节点和工作负载;
- 恢复数据卷并进行应用级验证。
单成员恢复示例:
etcdutl snapshot restore /var/backups/etcd-snapshot.db \
--data-dir=/var/lib/etcd-restored \
--name=restored-etcd \
--initial-cluster=restored-etcd=https://127.0.0.1:2380 \
--initial-advertise-peer-urls=https://127.0.0.1:2380
这段命令的含义是:
- 从快照生成一个新的 etcd 数据目录;
- 为恢复后的成员指定新的成员名;
- 用新的初始集群配置启动一个单成员集群;
- 使用
--data-dir指定恢复数据的位置,而不是覆盖原数据目录。
实际参数必须与 etcd 启动参数一致,尤其是:
--listen-client-urls;--advertise-client-urls;--listen-peer-urls;--initial-advertise-peer-urls;- TLS 证书和密钥;
- 认证、自动压缩和存储路径配置。
不能把上述示例直接用于三成员生产集群而不修改。多成员恢复需要明确每个成员的新地址和初始集群配置;如果只恢复一个成员,原有其他成员仍然运行,可能产生错误的成员关系或脑裂风险。恢复期间必须隔离旧集群,禁止两个控制面同时对外提供写服务。
3.4 为什么恢复后要关注 revision
etcd 中的 revision 是键值空间的单调递增版本号。Kubernetes 控制器和 informer 会根据 watch 机制观察对象变化。恢复快照后,etcd 的逻辑历史可能回到了较旧的 revision。
考虑如下时间线:
- 在 revision 100 创建了 Deployment;
- 在 revision 105 更新了 Deployment;
- informer 已经观察到了 revision 105;
- 灾难后从 revision 100 的快照恢复;
- API Server 重新使用原来的对象 UID 和旧资源历史。
此时,部分客户端可能持有“已经看到 revision 105”的缓存,而恢复后的 etcd 只知道 revision 100。若恢复实现没有正确推进或隔离旧的历史,客户端可能出现 watch 错误、缓存不一致或控制器无法按预期重新同步。
因此,恢复 etcd 时必须使用该版本支持的 revision bump 或等效机制,使恢复后的逻辑 revision 高于灾难前客户端可能观察到的 revision,并通常配合 compaction 清理恢复前的历史。具体参数和行为与 etcd 版本有关,不能凭空套用某个版本的命令。
这里的原则是:
- 物理快照时间点决定对象内容;
- 恢复后的逻辑 revision决定客户端如何识别这是一段新的历史;
- revision bump 不能恢复快照之后产生的业务数据;
- revision bump 也不能代替控制器、应用和卷数据的恢复。
3.5 证书和加密配置是控制面恢复的一部分
控制面常见的恢复失败表现包括:
kube-apiserver无法连接 etcd;- TLS 报
certificate signed by unknown authority; - etcd 端报客户端证书无权限;
- Secret 读取时报解密错误;
- kubelet 因集群 CA 或客户端证书变化无法重新注册;
- Controller Manager 使用过期或错误的 service-account signing key。
诊断时应按数据流检查,而不是只看 kubectl get nodes:
kubectl
-> kube-apiserver
-> etcd
-> authentication / authorization
-> admission webhooks
-> controller and scheduler
-> kubelet
-> container runtime
-> CNI / CSI / image registry
先检查 API Server 到 etcd 的 TLS 和健康状态,再检查 API Server 的认证、授权和准入配置,最后检查 kubelet、CNI、CSI 和业务应用。若 API Server 根本无法启动,kubectl 的错误通常只是表象,真正原因应查看控制面节点上的静态 Pod 日志或 systemd 日志。
四、资源恢复:对象有依赖顺序,不是任意 YAML 的集合
4.1 为什么 kubectl get -A -o yaml 不是完整备份
下面的命令可以用于临时检查或导出部分对象:
kubectl get deploy,sts,ds,svc,ingress,configmap,secret,pvc \
--all-namespaces -o yaml > resources.yaml
但它不适合作为完整生产备份,原因包括:
- 资源类型不完整,可能遗漏 CRD、自定义资源、Webhook、RBAC 和存储对象;
status、resourceVersion、UID 等字段不适合原样恢复;- 列表输出不能天然表达 CRD 必须先于自定义资源创建;
- Secret 只是当前时刻的凭据,可能已在外部系统轮换;
- PV 的对象和后端卷数据是两个不同层面;
- 某些对象由云控制器、Operator 或其他控制器生成,手工恢复可能与控制器冲突;
- 使用
--all-namespaces不能导出集群级资源,因为集群级资源没有 namespace。
更可靠的做法是同时保留:
- 以 Git 管理的声明式配置;
- etcd 快照;
- 资源级备份工具产生的对象备份;
- 数据存储自己的备份;
- 云资源和 DNS 的独立导出。
4.2 资源恢复的依赖图
资源恢复通常遵循以下依赖关系:
flowchart TD
A[恢复控制面和 API Server] --> B[Namespace]
B --> C[CRD]
C --> D[Operator 和 Webhook]
B --> E[ServiceAccount 与 RBAC]
B --> F[ConfigMap 与 Secret]
B --> G[StorageClass]
G --> H[PV/PVC]
F --> I[Deployment/StatefulSet/DaemonSet]
H --> I
E --> I
I --> J[Service]
J --> K[Ingress/Gateway]
K --> L[外部 DNS 与负载均衡]
这不是所有集群都必须严格遵循的唯一顺序,但它表达了重要因果关系:
- 没有 CRD,自定义资源无法创建;
- 没有 Operator,自定义资源可能长期停留在未就绪状态;
- 没有 Secret、ServiceAccount 或 RBAC,Pod 可能启动但无法访问依赖;
- 没有 PVC 或后端数据,StatefulSet 可能创建成功但应用无法工作;
- 没有 Service,Ingress 的后端没有可达目标;
- 没有外部 DNS,用户即使看到入口地址也无法通过域名访问。
恢复后不要只检查对象是否存在,应检查 status.conditions、EndpointSlice、Pod 就绪状态、卷挂载状态和应用健康检查。
4.3 Velero 资源和卷备份的边界
Velero 可以对 Kubernetes 资源进行备份和恢复,并可结合 CSI 快照或数据移动能力处理卷数据;具体能力依赖 Velero 版本、插件、CSI 驱动、云厂商和存储后端,不能把“启用了 Velero”理解为所有卷都自动具备异地可恢复性。
一个典型的资源备份命令如下:
velero backup create app-backup \
--include-namespaces=orders \
--snapshot-volumes \
--wait
velero backup describe app-backup --details
velero backup logs app-backup
前置条件包括:
- Velero Server 已安装并能访问对象存储;
- 对应云平台或 CSI 插件已正确配置;
- StorageClass 和 CSI 驱动支持所使用的快照能力;
- 对象存储凭据本身也被安全保管;
- 快照或数据移动结果确实位于灾难域之外。
恢复时:
velero restore create --from-backup=app-backup --wait
velero restore describe <restore-name> --details
velero restore logs <restore-name>
<restore-name> 是 Velero 实际返回的恢复对象名称,应使用命令输出中的名称,而不是假设它固定。恢复结果应重点查看:
Restore是否完成;- 哪些资源被跳过;
- 哪些资源发生冲突;
- PVC 是否绑定到正确的 PV;
- 快照是否能在目标集群中还原;
- 自定义资源是否在 CRD 之后恢复;
- Webhook 是否在 API 请求阶段阻塞恢复。
资源恢复和卷恢复的顺序也可能不同于创建顺序。例如,PVC 对象可以先恢复,但后端快照尚未完成时,Pod 仍然无法挂载卷。对于数据库,卷快照还可能不是应用一致性快照。
4.4 Hook 解决的是应用一致性,不是魔法
数据库通常需要:
- 暂停写入或进入备份模式;
- 刷新 WAL、日志或事务;
- 创建数据库原生备份或一致性快照;
- 备份完成后恢复写入。
Velero Hook 可以在备份或恢复前后执行容器命令。例如,资源可以带有类似以下注解:
metadata:
annotations:
pre.hook.backup.velero.io/command: '["/bin/sh", "-c", "psql -U postgres -c \"SELECT pg_start_backup(''velero'')\""]'
post.hook.backup.velero.io/command: '["/bin/sh", "-c", "psql -U postgres -c \"SELECT pg_stop_backup()\""]'
pre.hook.backup.velero.io/timeout: 10m
这个示例仅用于说明 Hook 机制,实际 PostgreSQL 备份命令、权限、版本和双引号转义必须按数据库部署方式调整。Hook 的失败处理、超时和重试策略必须经过演练;如果备份命令失败却仍继续创建卷快照,得到的可能是不可恢复或逻辑不一致的数据。
更稳妥的数据库恢复往往是:
- 先恢复数据库服务;
- 用数据库原生备份恢复数据;
- 重放 WAL 或增量日志;
- 通过数据库自身的健康检查确认一致性;
- 最后启动依赖数据库的 Kubernetes 工作负载。
五、数据恢复:PVC、PV 和卷内数据必须分开理解
5.1 PVC 不是数据
PersistentVolumeClaim 是 Kubernetes 中的请求对象,通常包含:
- 存储容量;
- 访问模式;
- StorageClass;
- 标签选择器;
- 数据源或快照引用。
它不等于卷内数据。即使 PVC 对象恢复成功,以下情况仍可能导致数据不可用:
- 云盘快照不在目标区域;
- 目标集群没有相同的 CSI 驱动;
- StorageClass 参数在新集群中不存在;
- 卷拓扑限制导致节点无法挂载;
- 数据库文件系统损坏;
- 快照发生在未完成事务的时刻;
- 原卷使用了目标集群无法识别的加密密钥。
5.2 文件系统快照与应用一致性
块存储快照通常保证存储系统层面的崩溃一致性,但“崩溃一致性”不一定等同于“应用一致性”。
例如数据库在写入数据页和 WAL 时发生快照:
- 如果 WAL 和数据页都能按数据库规则重放,数据库可能可以恢复;
- 如果快照跨越多个卷,而不同卷的快照时间不一致,跨卷事务可能无法重建;
- 如果应用有缓存或异步写入,存储完成不代表业务逻辑已经提交。
因此,关键数据库应优先使用:
- 数据库原生全量备份;
- 增量备份或 WAL 归档;
- 事务一致性快照;
- 经过验证的恢复时间点。
卷快照适合缩短恢复时间,但不应自动替代数据库备份。
5.3 恢复卷时的安全顺序
对 StatefulSet 或数据库工作负载,常见安全顺序是:
- 恢复或创建目标 Namespace;
- 恢复 StorageClass、CSI 配置和密钥;
- 恢复 PVC 与后端卷;
- 确认 PVC 为
Bound; - 确认卷可被目标节点挂载;
- 以单实例或维护模式启动数据库;
- 执行数据库一致性检查和日志重放;
- 验证数据;
- 再启动应用副本和流量入口。
可以使用以下命令观察卷状态:
kubectl get pvc -n orders
kubectl get pv
kubectl describe pvc database-data -n orders
kubectl get volumeattachments.storage.k8s.io
如果 PVC 是 Bound 但 Pod 仍处于 ContainerCreating,应查看 Pod 事件和 CSI 控制器日志;Bound 只说明 Kubernetes 已完成对象级绑定,不代表底层卷挂载成功。
六、DNS、Service 和入口:恢复了对象不等于用户能访问
6.1 集群内部 DNS
Kubernetes 集群内部 DNS 通常由 CoreDNS 提供。Service 的 DNS 名称通常遵循:
<service>.<namespace>.svc.<cluster-domain>
例如:
orders-api.orders.svc.cluster.local
实际集群域不一定是 cluster.local,应以 kubelet、CoreDNS 和集群配置为准。
恢复后,内部 DNS 可能失败的原因包括:
- CoreDNS Deployment 或 ConfigMap 未恢复;
- CoreDNS Pod 未调度或无法访问上游;
- Service 存在但没有 EndpointSlice;
- 应用仍绑定旧的 Service 名称;
- NetworkPolicy 阻止 Pod 访问 DNS;
- kubelet 使用的集群 DNS 地址发生变化;
- 恢复时自定义 DNS 插件或 NodeLocal DNSCache 未部署。
诊断示例:
kubectl get pods -n kube-system -l k8s-app=kube-dns
kubectl get svc -n kube-system kube-dns
kubectl get endpointslice -n orders -l kubernetes.io/service-name=orders-api
kubectl run dns-test --rm -it --restart=Never \
--image=busybox:1.36 -- nslookup orders-api.orders.svc.cluster.local
nslookup 成功只能证明 DNS 查询路径可用;还需要确认 Service 端口、Endpoint 地址、网络策略和应用协议都正确。
6.2 外部 DNS 和入口
外部访问通常经过:
客户端
-> 公网 DNS
-> Ingress / Gateway / LoadBalancer
-> Service
-> Pod
-> 外部数据库或其他依赖
恢复 Kubernetes 对象后,外部 DNS 仍可能指向:
- 已销毁的公网负载均衡器;
- 旧集群的 IP;
- 已过期的临时地址;
- 错误的 CNAME;
- 受 DNSSEC、证书或 CDN 配置影响的旧入口。
DNS 切换时间受 TTL、递归解析器缓存和客户端缓存影响。即使权威 DNS 已修改,仍可能有一段时间的流量到达旧集群。因此应采用以下任一策略:
- 预先使用稳定的全局入口,再把后端切换到新集群;
- 保留旧入口直到缓存过期;
- 使用低 TTL 的灾备域名;
- 通过流量管理系统按权重或健康状态切换。
低 TTL 会增加 DNS 查询量,但不能保证所有客户端立即刷新;DNS 不是强一致的即时切换机制。
6.3 Service IP 和域名稳定性
如果通过重建集群恢复资源,Service 的 clusterIP 可能变化。集群内部不应把 ClusterIP 写死在应用配置中,应使用 Service DNS 名称。
对外部 DNS 而言,应尽量让记录指向稳定的入口,而不是直接指向某个 Pod IP。Pod IP、节点 IP 和临时 LoadBalancer IP 都可能在恢复后改变。
Ingress TLS Secret、Gateway 证书引用和外部证书自动签发也必须验证。若 cert-manager、ACME DNS-01 所需的 DNS 凭据或外部 DNS API 无法访问,入口对象可能存在,但 HTTPS 仍不能恢复。
七、外部依赖:Kubernetes 恢复后最容易被忽略的故障路径
工作负载能启动,不表示业务已恢复。以下依赖必须单独纳入恢复设计。
1. 镜像仓库
恢复节点需要拉取镜像。如果私有镜像仓库不可用,Pod 会出现:
ErrImagePull
ImagePullBackOff
需要保留:
- 镜像仓库地址;
- 镜像和标签或摘要;
imagePullSecrets;- 仓库 CA 证书;
- 跨区域或离线镜像复制能力。
生产环境应优先使用不可变镜像摘要,而不是只依赖可被覆盖的 latest 标签。
2. CNI 和网络
没有 CNI,Pod 可能长期停留在 Pending、ContainerCreating 或网络未就绪状态。恢复 CNI 时要同时确认:
- CNI DaemonSet;
- 节点内核模块和 sysctl;
- Pod CIDR 与 Service CIDR;
- 云安全组、路由和网络 ACL;
- NetworkPolicy 控制器;
- 加密隧道或跨节点组件。
不能因为 API Server 能访问,就认为业务网络已经恢复。
3. CSI 和存储
CSI 恢复需要:
- CSI Controller;
- CSI Node 插件;
- 云平台或存储系统凭据;
- KMS 密钥;
- StorageClass;
- 拓扑、区域和节点权限;
- 快照或数据移动组件。
CSI 插件本身常常依赖云 API。如果目标区域无法访问云 API,PVC 对象恢复后仍然无法挂载。
4. 身份认证与权限
API Server 的认证可能依赖 OIDC、LDAP、Webhook 或云 IAM。控制面恢复后,如果身份系统不可用,管理员可能无法登录;如果 ServiceAccount token signing key 发生变化,应用也可能无法调用 API。
应保留并验证:
- OIDC issuer 和 CA;
- API Server 的认证配置;
- ServiceAccount signing key;
- RBAC 对象;
- 外部身份系统的管理员路径;
- 灾备环境的最小权限凭据。
5. 数据库、消息队列和云服务
应用配置中的连接字符串、Secret 和 Service 不等于外部服务已经恢复。必须验证:
- DNS 能解析外部服务;
- 网络路由和防火墙允许连接;
- TLS 证书链正确;
- 数据库用户、权限和 schema 存在;
- 消息队列的 topic、分区和消费位点正确;
- 云 API 凭据仍有效;
- 外部限流、配额和回调地址已切换。
灾难恢复中的一个典型失败是“应用 Pod 全部 Ready,但所有请求返回 500”,原因是数据库恢复到了不匹配的时间点,或应用使用了新版本 schema 连接旧数据库。
八、恢复流程:从控制面到业务验证
一次完整恢复可以分为六个阶段。
阶段一:隔离故障域
先确认旧集群是否仍可能接收写入。若旧集群和新集群同时运行:
- 两个集群可能同时消费消息;
- 两边可能同时写入数据库;
- 外部 DNS 可能把流量分散到两个版本;
- 控制器可能对同一外部资源产生竞争。
因此需要通过网络隔离、入口切换、数据库只读或租约机制,明确哪一个集群是恢复后的主集群。
阶段二:恢复控制面
恢复 etcd、控制面配置、证书和加密密钥,启动 API Server,并检查:
kubectl get --raw='/readyz?verbose'
kubectl get nodes
kubectl get namespaces
kubectl get crd
readyz 成功表示 API Server 的就绪检查通过,但不表示所有节点、存储和业务已经正常。
阶段三:恢复基础设施资源
按依赖顺序恢复:
- Namespace;
- CRD;
- CNI、CSI、CoreDNS;
- Operator 和 admission webhook;
- RBAC、ServiceAccount、ConfigMap、Secret;
- StorageClass、PV、PVC;
- 工作负载;
- Service、Ingress、Gateway。
恢复 Webhook 时尤其要小心。一个已恢复但后端不可达的 ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration,可能使后续所有资源创建请求都超时或失败。恢复前应确认 webhook 的服务、证书和网络路径可用,必要时根据组织的应急流程暂时调整其失败策略;这会降低校验保护,必须有明确的恢复后复原步骤。
阶段四:恢复数据
对每个有状态应用记录:
- 数据备份来源;
- 备份时间;
- 恢复点;
- 需要的密钥;
- 恢复命令;
- 数据一致性检查;
- 是否允许只读启动;
- 是否需要 WAL 或增量日志重放。
恢复数据库后先执行应用级查询,而不是立刻开放全部流量。例如检查订单数量、最近一笔交易、关键表约束和消息积压。数据库进程“正常运行”只说明进程存活,不说明业务数据正确。
阶段五:恢复流量入口
先在内部用固定测试请求验证:
kubectl run curl-test --rm -it --restart=Never \
--image=curlimages/curl:8.10.1 -- \
curl -fsS http://orders-api.orders.svc.cluster.local:8080/healthz
再检查:
- Service 是否有正确的 EndpointSlice;
- Ingress 或 Gateway 是否获得地址;
- TLS 证书是否匹配域名;
- 外部 DNS 是否指向新入口;
- 负载均衡器健康检查是否通过;
- 从真实网络位置访问是否成功。
最后才进行 DNS 或流量管理切换。
阶段六:业务验收和解冻写入
恢复验收不应只使用 Kubernetes 健康状态,而应包括:
- 登录和权限;
- 创建、查询和更新一条测试业务数据;
- 异步消息生产与消费;
- 文件上传和下载;
- 数据库事务;
- 定时任务;
- 外部回调;
- 监控、日志和告警;
- 备份任务能否在新集群继续执行。
如果灾难期间启用了只读模式,应在确认双写、重复消费和数据冲突风险后再恢复写入。
九、常见误区和对应的失败表现
误区一:etcd 快照就是整个集群备份
失败表现:API 对象恢复,但 PVC 无法挂载,镜像拉取失败,外部域名仍指向旧入口。
根因:etcd 只保存 Kubernetes API 状态,不保存卷数据、节点环境、镜像仓库和外部 DNS。
误区二:备份了 PVC 就备份了数据库
失败表现:PVC 为 Bound,数据库却启动失败、数据校验失败或出现事务不一致。
根因:PVC 是资源对象;卷快照也未必是数据库应用一致性备份。
误区三:所有 YAML 原样重新 apply
失败表现:
metadata.resourceVersion冲突;- webhook 或 Operator 反复修改对象;
- ClusterIP 或云资源字段冲突;
- CRD 尚未存在导致自定义资源创建失败;
- status 字段被错误带入。
根因:声明式配置和控制器生成状态没有被区分。恢复时应清理服务器生成字段,并按依赖关系分阶段应用。
误区四:Pod 为 Running 就算恢复
失败表现:Pod 状态正常,但请求全部超时或返回 5xx。
根因:应用可能尚未连接数据库、消息队列、对象存储或身份系统。Running 只描述容器进程状态,Ready 也只是就绪探针定义的局部条件。
误区五:DNS 改了就立即切流
失败表现:一部分用户访问新集群,另一部分仍访问旧集群;双写造成数据分叉。
根因:DNS 缓存不会立即失效,且旧入口可能仍然可写。切流前必须处理旧集群的写入能力和幂等问题。
误区六:恢复演练只验证命令能执行
失败表现:演练报告显示“备份成功、恢复成功”,真正灾难时却找不到证书、KMS 密钥或管理员凭据。
根因:演练只验证了工具流程,没有验证依赖、时间目标和业务结果。
十、灾难演练:验证恢复能力而不是验证备份文件存在
灾难演练应在独立环境、隔离网络或临时恢复区域进行,避免污染生产数据。最少需要验证以下内容:
- 从异地位置取得 etcd 快照并校验;
- 使用记录的版本和参数恢复控制面;
- API Server 可以读取加密 Secret;
- CRD、自定义资源和 Operator 能按顺序恢复;
- PVC 能绑定并挂载;
- 数据库能完成一致性恢复;
- CoreDNS、Service、Ingress 或 Gateway 正常;
- 镜像仓库、CNI、CSI、身份系统和外部数据库可访问;
- 外部 DNS 或流量系统可以切换;
- 真实业务流程通过;
- 实际耗时满足 RTO;
- 实际数据丢失量满足 RPO。
演练记录至少应包含:
- 备份创建时间;
- 快照复制完成时间;
- 恢复开始和结束时间;
- 每个阶段的阻塞点;
- 恢复后对象数量;
- PVC 和数据库恢复点;
- DNS 切换时间;
- 业务验证结果;
- 未恢复的非关键资源;
- 下一次演练前必须修正的问题。
可以为恢复过程定义验收条件:
其中 是从恢复开始到业务验收通过的时间, 是灾难发生时间, 是恢复后业务数据对应的最新一致时间点。若控制面 10 分钟恢复,但数据库只能恢复到 3 小时前,则整体 RPO 仍是 3 小时,而不是 10 分钟。
十一、版本、实现和生产边界
Kubernetes API 版本会随版本演进。备份对象恢复到不同版本的集群时,必须检查:
- API group/version 是否仍受支持;
- CRD 的 conversion webhook 是否可用;
- 字段是否已弃用或改变语义;
- admission webhook 的 API 是否兼容;
- StorageClass、Ingress、Gateway 和 CSI 能力是否一致;
- 云厂商插件是否支持目标 Kubernetes 版本。
kubectl、Velero、etcd、CSI 驱动和云厂商插件都有各自的版本兼容矩阵。本文命令只表示当前常见接口和流程,不能替代目标版本的官方文档。特别是 etcd 快照恢复参数、Velero 卷数据能力、CSI 快照支持和云负载均衡行为,都属于实现或厂商相关部分。
规范保证、常见实现和经验建议应明确区分:
- Kubernetes API 对象通过 API Server 持久化,使用 etcd 是常见实现;
- PVC 与 PV 的对象关系由 Kubernetes 管理,但卷后端数据由存储实现管理;
- CoreDNS 是常见集群 DNS 实现,但 DNS 组件可以被替换;
- Velero 的资源备份、Hook 和卷保护能力依赖安装版本与插件;
- 数据库应用一致性必须由数据库机制或经过验证的 Hook 保证,不能由 Kubernetes 自动推断。
灾难恢复的最终目标不是让集群“看起来像原来”,而是在明确的 RPO 和 RTO 内,恢复一个能够安全接收请求、正确读写数据、连接所有必要依赖,并经过业务验证的系统。只有控制面、资源、数据、DNS、外部依赖和演练结果共同成立,Kubernetes 集群才算真正具备灾难恢复能力。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 事件响应:止损、证据、审计、凭据轮换和恢复
- 下一篇:Kubernetes 生产就绪检查:架构、安全、容量、观测、发布和运行手册
- 延伸:Kubernetes etcd 备份恢复:Snapshot、证书、Revision 和灾难演练
- 延伸:Kubernetes 备份与 Velero:资源、卷、Hook、恢复顺序和演练
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论