Kubernetes 恢复、灾备与长期治理
一次区域故障中,备用集群在二十分钟内创建完成,恢复团队却用了四个小时才敢接管。CRD 与 Operator 顺序错误,PVC 指向不可访问的快照,KMS 授权仍绑定旧账号,旧站的消费者没有停写,DNS 又提前把请求送入未校验的新数据库。备份数据都在,恢复过程却没有共同的时钟、依赖图、单写约束和停止条件。
灾备的难点不是执行一条 Restore 命令,而是在证据不完整、人员紧张和基础设施变化的现场,仍能按依赖恢复、在错误层暂停、阻止双写、验证业务水位,并在接管失败时回退。只有这些动作能被反复演练和度量,RPO、RTO 才不是方案文档里的愿望。
恢复开始前先建立事故控制面
恢复团队接到告警后不应立刻创建资源。先建立单一事故时钟和操作记录,确认故障域、最后健康业务水位、仍在运行的写入者、可用恢复点、目标环境能力和决策人。
incidentId: dr-lab-alpha
declaredAt: '<utc-timestamp>'
failureDomain:
cluster: source-a
storageAccountLost: true
regionReachable: false
writeState:
sourceWritersFenced: false
consumersPaused: false
recoveryPoint:
id: '<approved-recovery-point-id>'
businessWatermark: '<verified-watermark>'
objectDigest: '<sha256>'
volumeEvidence: '<snapshot-or-repository-reference>'
target:
clusterUid: '<target-cluster-uid>'
networkIsolated: true
externalSideEffectsBlocked: true
decision:
incidentCommander: '<role-reference>'
dataOwner: '<role-reference>'
trafficOwner: '<role-reference>'记录只保留角色引用、恢复点 ID 和摘要,不保存真实 Token、证书、内部域名或客户数据。所有命令输出标注执行人、时间、context 和退出码;破坏性动作需要双人复核。
事故开始后的第一项技术动作是固定 context 并保存发现证据:
kubectl config current-context
kubectl cluster-info
kubectl get namespace
kubectl get node -o wide
kubectl get storageclass,csidriver
kubectl get crd
kubectl get events -A --sort-by=.lastTimestamp | tail -n 200如果源站仍能运行,要先冻结 GitOps prune、HPA/KEDA、备份 Schedule、数据库迁移任务、队列消费者和可能产生外部副作用的 CronJob。冻结动作必须写明 owner、恢复条件和超时时间,避免事故结束后长期遗忘。
两条恢复路径不能混成一条
etcd 整体恢复面向自管控制面的完整状态回退。它要求隔离或停止 API Server,恢复所有 etcd 成员并处理 revision 回退,再重启依赖旧状态的控制组件。它不是在一个健康的新集群里导入对象。
对象级恢复面向已经具备健康 API Server 的目标集群,由 Velero、Kasten、GitOps 或其他工具创建 CRD、Namespace、RBAC、工作负载和卷,并完成环境映射。
同一个事故中先恢复 etcd,再把同一恢复点的对象归档覆盖进去,会在 UID、resourceVersion、ownerReference、云资产和卷绑定上制造冲突。整群控制面损坏时进入 Kubernetes 控制面 etcd 快照与恢复;跨集群重建时采用对象级路径,并保存源对象与目标对象的映射关系。
八阶段放行代替一次性 apply
恢复顺序由消费关系决定:基础设施承载网络和存储,网络存储承载 API 扩展与卷,CRD 承载 Operator,自定义控制器与身份共同承载数据和应用,入口最后才承载真实流量。
恢复顺序与依赖图 把八个阶段展开为可暂停批次,并给出每层门禁。工程实现中,每个批次至少包含输入资产、目标对象、执行身份、幂等键、通过证据、失败停止条件和回滚动作。
阶段对象存在不等于阶段通过:
| 阶段 | 表面绿色 | 能放行下一层的证据 |
|---|---|---|
| 基础设施 | 集群状态 Running | API、节点、时间同步、配额和目标故障域满足基线 |
| CNI/CSI | DaemonSet Ready | Pod 跨节点连通,测试 PVC 可绑定挂载,快照 API 与 driver 可工作 |
| CRD | Established=True | stored version、conversion 与 admission webhook 可用 |
| Operator | Pod Ready | leader election、调谐、外部 API 和目标 CR 状态正常 |
| 身份 | Secret 已创建 | workload identity 能以最小权限访问 KMS、仓库和依赖 |
| PVC/数据 | PVC Bound | 新卷中的摘要、事务位点和多卷关系通过校验 |
| 应用 | Deployment Available | 数据完整性、读写、schema、依赖与业务 SLI 通过 |
| 入口 | Gateway/Ingress Ready | 单写约束成立、灰度流量通过、回切开关可用 |
选择性恢复先解决身份冲突
误删一个 Namespace、回滚一份配置和恢复整个集群不是同一操作。选择性恢复最容易在名称、UID、ownerReference、finalizer、admission、ServiceAccount、PVC 和外部资源之间产生冲突。
选择性恢复、冲突与所有权处理 需要先明确三种策略:保留目标、覆盖目标、转换后创建新目标。生产系统优先恢复到隔离 Namespace 或新资源名,完成 diff 与业务校验后再切换引用;直接覆盖活动对象会把恢复动作和删除动作叠加在一起。
kubectl diff -f transformed-bundle/
kubectl apply --server-side --dry-run=server -f transformed-bundle/
kubectl auth can-i create deployments -n recovery-lab
kubectl auth can-i create secrets -n recovery-lab
kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurationsdry-run=server 可以发现未知 GVK、schema 和部分 admission 问题,却不能证明 controller、KMS、卷数据和业务可用。转换规则要保存源 GVK、目标 GVK、Namespace、StorageClass、镜像、Secret 引用和外部 ID 的映射,并对输出计算 digest。
整群重建先重建能力,再恢复状态
整群丢失时,恢复目标不是复制旧集群的所有偶然配置,而是重建一个满足已声明能力基线的新集群,再导入业务状态。整群重建 将基础设施、集群附加组件、API 扩展、身份、卷数据、应用和入口拆成可重入阶段。
目标集群先证明这些能力:
Kubernetes 与关键 CRD/API 版本能够读取恢复资产。CNI、CSI、snapshot controller、DNS、镜像和时间同步工作正常。StorageClass、拓扑、访问模式、KMS 与云身份已经映射。
Operator 与 webhook 使用目标环境版本和镜像 digest。外部数据库、消息系统、证书、DNS 和负载均衡器有明确重建 owner。恢复网络隔离,生产队列、邮件、支付和第三方回调默认阻断。
只有当目标能力基线通过后,才导入 API 对象和数据。否则每个失败都会被误判为“备份坏了”,实际根因却是目标环境不具备消费恢复点的能力。
跨集群和跨区域恢复必须先建立单写者
跨集群与跨区域灾难恢复 不只是复制更多副本。账号、区域、KMS、CSI、DNS、镜像、证书和外部服务都可能形成独立故障域,目标站需要一份可执行映射合同。
接管前必须证明旧写入者已经被隔离。可以通过数据库只读、存储 fencing、消息消费组停用、网络策略、凭证撤销或外部仲裁实现;仅把旧 Deployment 缩容并不可靠,因为控制器、自动扩缩容或第二个集群可能重新拉起它。
SOURCE WRITES FENCED
-> target data restored to approved watermark
-> target application validated in isolation
-> internal canary traffic enabled
-> business owner approves read/write
-> external traffic shifted gradually
-> old site remains fenced until rollback window closes任何步骤失败都要有明确回退位置。回退不是删除 Restore CR,而是停止目标副作用、撤销新入口、恢复旧站的唯一写权限或进入人工数据仲裁。已经在目标站产生的新写入不能被静默丢弃。
RPO 与 RTO 用业务水位计算
调度间隔不是 RPO,Restore Job 时长也不是 RTO。RPO / RTO 恢复演练 使用故障切点、已验证恢复点业务水位和完整接管时间重算指标:
实际 RPO = 故障切点 - 最后通过业务校验的恢复点水位
实际 RTO = 业务重新满足获批 SLI 的时间 - 事故宣布时间RTO 必须包含事故确认、权限申请、目标基础设施、对象恢复、数据导入、身份映射、应用校验、流量切换和人工审批。恢复任务在十分钟内完成,但团队等待 KMS 授权两小时,实际 RTO 就包含这两小时。
每轮演练保存阶段时间:
date -u +%FT%TZ
kubectl get events -A --sort-by=.lastTimestamp
kubectl get pvc -A
kubectl get pods -A -o wide
kubectl get --raw='/readyz?verbose'业务水位应来自订单序号、账本位置、数据库备份 ID、WAL/binlog 或可验证 fixture,而不是控制器的完成时间。演练数据必须脱敏,目标网络必须阻断生产副作用。
正向演练走完整接管链
正向演练使用可销毁集群、随机 fixture、短期身份和独立仓库前缀:
写入单调递增业务水位,记录对象摘要、卷 ID、应用位点、镜像 digest 和 KMS key 引用。创建恢复点并把副本复制到与源站不同的故障域。宣布模拟事故,冻结源写入者、GitOps、自动扩缩容、Schedule 和副作用任务。
在隔离目标按八阶段恢复,每层保存通过证据和耗时。在新 PVC 与新应用上校验 SHA-256、schema、事务、只读和受控写入。开放内部探针,再开放少量灰度流量;监控错误率、延迟、数据水位和双写信号。
执行回切或保持目标接管,随后清理临时权限、卷、快照、DNS 记录和测试仓库前缀。
预期结果包括:恢复点能被独立发现和解密;目标不依赖源 PVC、节点缓存和源 ServiceAccount;业务断言通过;实际 RPO/RTO 可重算;操作链可审计;清理不影响保留期内的副本。
反向演练验证停止能力
成熟的恢复系统不仅能前进,也能在错误时停住。
反例一:提前开放入口。 在数据校验前创建测试 Gateway,但通过 NetworkPolicy、服务网格或外部防火墙阻断生产流量。门禁应拒绝接管,并指出业务水位或单写证据缺失。
反例二:撤销 KMS 或仓库读取权限。 恢复任务应在身份层明确失败,不能创建一个空 PVC 后继续放行应用。修复权限后使用新 run ID 重试,保留第一次失败证据。
反例三:让目标 StorageClass 不兼容。 PVC 应保持 Pending,Event 能定位 driver、topology、access mode 或容量问题。不得通过手工创建不受追踪的 PV 掩盖映射缺陷。
反例四:让旧站重新写入。 使用隔离的模拟写入者验证 fencing。检测到两个业务水位同时前进时,自动化必须停止流量切换,转入数据 owner 仲裁。
反例五:使用过期恢复点。 让最新点不可读,验证系统能否选择上一份已校验恢复点并重新计算 RPO,而不是悄悄宣称仍满足目标。
反向演练的成功标准是错误被限定在正确阶段、证据足以定位、后续阶段没有副作用、修复后能够幂等重跑。
可观测性要回答“卡在哪一层”
备份恢复可观测性与排障 应把控制器状态、Kubernetes Event、云 API、CSI、仓库、网络、应用日志和业务 SLI 放到同一时间线。仅监控 Backup/Restore CR 的成功率会漏掉 PVC 数据错误、身份失效和入口提前开放。
关键指标至少包括:
恢复点年龄与最后一次验证时间
备份/导出队列年龄、失败率和部分成功数量
快照创建、数据移动、PVC 供应和应用校验耗时
对象数、有效字节、传输字节、吞吐、重试与限流
仓库读取错误、KMS 解密错误、CSI/API throttling
各恢复阶段耗时、实际 RPO/RTO、人工等待时间
旧写入者与目标写入者同时活动的告警日志和指标不能暴露 Secret、Token、证书、对象内容或内部 endpoint。恢复点 ID、请求 UID、资源 UID、工具 run ID 和事故 ID可以作为关联键。
权限和职责必须在事故前分配
| 角色 | 主要权限 | 不应同时持有的权限 |
|---|---|---|
| 事故指挥 | 阶段批准、风险接受、接管/回切决策 | 日常仓库删除密钥 |
| 平台恢复 | 目标集群对象创建、状态读取、恢复任务执行 | 源数据销毁与长期 KMS 管理 |
| 存储 owner | SnapshotClass、CSI、后端副本和容量 | 业务流量切换 |
| 数据 owner | checkpoint、完整性检查、业务水位确认 | 修改平台审计记录 |
| 安全 owner | 短期身份、KMS、凭证撤销和审计 | 单人批准并执行大范围恢复 |
| 流量 owner | Gateway、DNS、负载均衡和回切 | 绕过业务校验门禁 |
安装 CRD/webhook、执行恢复、读取仓库、删除恢复点、修改保留策略和切换流量应使用不同身份。事故授权设置最短有效期、目标集群与 Namespace 限制,并在结束后自动撤销。break-glass 账户每次使用都要产生告警、审批和复盘。
容量、成本与灾备架构取舍
灾备模式可从冷备、温备到热备逐级增加成本。冷备依赖事故后创建集群,基础设施时间占 RTO;温备保留集群能力和部分副本,降低恢复时间但持续消耗资源;热备维持应用与数据复制,能够更快接管,却必须解决双写、复制延迟、许可、跨区流量和长期演练。
容量评估同时包含:
源数据、快照链、独立仓库、异地副本和保留代数。恢复卷、临时克隆、数据移动缓存、日志和失败重试空间。CSI、对象存储、跨区网络、KMS、API 请求和读取/取回费用。
目标节点、预热镜像、数据库恢复 CPU/内存和并发限流。演练环境、人工值守、审计存储和长期兼容测试。
每次演练逐级增加恢复并发,观察队列年龄、吞吐、API throttling、节点资源和完成时间。超过拐点后,并发会让 CSI controller、云 API 或对象仓库拥塞,RTO 反而恶化。
保留、升级、迁移和退出是一条连续链
保留、升级、迁移与退出治理 处理恢复系统的长期寿命。保留策略要同时满足业务恢复窗口、合规留存、删除权、不可变窗口和成本预算;不能只按“保留 30 份”配置任务。
任何 Kubernetes、CSI、CRD、备份控制器、仓库格式、KMS 或云平台升级,都要用黄金恢复点验证旧读、新写、删除和回滚。迁移工具或存储时保持双轨恢复,直到新旧路径对同一 fixture 产生一致业务结果。
退出时盘点并处理:控制器 CR、CRD/finalizer、对象仓库、后端快照、临时卷、KMS grant、云 IAM、ServiceAccount、RoleBinding、许可、审计索引和持续账单。卸载控制器不等于完成退出;历史恢复点仍需可读,孤儿快照仍可能计费,未撤销凭证仍是安全风险。
恢复能力交付检查
事故时钟、故障域、业务水位、恢复点、目标环境和决策角色能够在同一记录中追踪。etcd 整体恢复与对象级恢复路径明确,自动化不会把两条路径混跑。八阶段恢复均有输入、身份、幂等键、通过证据、停止条件和回滚动作。
选择性恢复先做冲突与映射检查,默认进入隔离 Namespace 或新资源。跨域接管前证明旧写入者被隔离,目标业务断言通过,流量切换能够回退。RPO 由已验证业务水位计算,RTO 覆盖从事故宣布到业务 SLI 恢复的全部时间。
正向和反向演练均能重复,失败会停在正确层,不会继续产生副作用。权限、仓库、KMS、敏感数据和审计按职责隔离,临时授权能按时撤销。容量模型覆盖恢复峰值、跨区、缓存、演练、许可和人工成本。
升级能读取旧恢复点,迁移有双轨验证,退出后孤儿资产、权限和账单归零。
