第 25 章 灾备、跨区域与 RTO/RPO:当整个控制面塌了怎么办

墨:第 24 章我们怕的是”慢”——延迟高、吞吐低。这章我们怕的是”没”——集群整个没了。节点可以重建,Pod 可以重调度,但etcd 里那一份”真理状态”要是丢了,你的集群就没有记忆了:哪个 Deployment 该有几个副本、哪个 PVC 绑了哪块盘、哪个 Secret 里是数据库密码——全没了。

你:etcd 不是有多副本强一致吗?三副本挂一个还有两个,怎么会”整个没了”?

墨:副本防的是”单点故障”,防不了”逻辑灾难”。比如一个误操作的 kubectl delete ns production --all 会同步删掉三个副本;一次错误的 etcdctl defrag 全节点同时做会短暂不可用;一个 Region 级的云故障(可用区断电、网络分区)会让整个控制面连不上节点。副本解决”挂一个”,解决不了”人犯错”和”天灾”。这章就讲:怎么在副本之外,给”真理状态”再买一份保险。


25.1 先定义敌人:RTO 与 RPO 不是名词,是承诺

做任何灾备,先别碰技术,先答两个问题——这是系统设计者第一课:先量化损失,再选方案。

  • RPO(Recovery Point Objective,恢复点目标):灾难发生时,你能接受丢多少数据。用”时间”表达:RPO=5 分钟,意思是允许丢失最近 5 分钟内的变更。
  • RTO(Recovery Time Objective,恢复时间目标):灾难发生后,你多久必须恢复服务。RTO=30 分钟,意思是系统挂了之后半小时内要重新能跑业务。

墨:这两个指标,谁决定了”你得多频繁备份”?谁决定了”你得准备多少冗余”?

你:RPO 决定备份频率——RPO 越短,备份越频繁、越要近实时。RTO 决定冗余架构——RTO 越短,越要”热备/双活”,而不是”冷备份慢慢恢复”。

墨:对。而且注意一个反直觉:RPO 和 RTO 越严苛,成本指数级涨。RPO=0 且 RTO=0(零丢失零中断)基本只有”跨区双活同步复制”能做到,那是真金白银堆出来的。大多数业务 RPO 几分钟、RTO 几十分钟,用”定期备份 + 演练过的恢复流程”就够了。灾备的第一性原理——为真实的业务损失买单,不为”理论上绝对安全”买单。

⚠️ 坑:很多人写灾备方案时直接写”RTO<5 分钟,RPO=0”,却没算过成本,也没演练过。承诺是工程师的债——你承诺了却恢复不了,比不承诺更糟。先问业务方:“丢 1 小时数据、停 4 小时,损失多大?“再定指标。


25.2 etcd 备份:真理状态的”第一份保险”

K8s 的全部声明式状态都在 etcd 里。备份 etcd,就是备份整个集群的灵魂。

25.2.1 snapshot:一致性快照怎么做

etcd v3 提供 snapshot save,它从正在运行的成员上取一份一致性快照——不需要停集群,因为 MVCC + Raft 保证了”某一时刻的全局一致视图”。

# 在 etcd 成员容器内/可达 etcd 的机器上
ETCDCTL_API=3 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 \
  snapshot save /backup/etcd-snapshot-$(date +%s).db
 
# 校验快照完整性
etcdctl snapshot status /backup/etcd-snapshot-*.db -w table

关键认知:snapshot 是点对点的,它不关心你 kube-apiserver 当时在干啥。因为 etcd 自己是强一致的,快照那一刻的数据就是”真相”。这也是为什么 K8s 把”状态”收敛到单一 CP 存储——备份一个东西就行,不用去拼 50 个组件的本地状态。

墨:对比一下——如果你的状态散落在 MySQL、Redis、本地文件各一份,灾备要怎么备?

你:得分别备每个组件、还要保证它们之间的一致性时间点对齐,几乎不可能严丝合缝。etcd 把”状态”收敛成一个点,备份就变成一个点的动作。这就是架构级简化带来的运维红利。

墨:说得好。收敛单一真理源,是 K8s 给灾备最大的礼物。

25.2.2 备份频率与 RPO 的对账

  • 想 RPO=15 分钟 → 至少每 15 分钟一个 snapshot(或持续把 etcd 的 Raft 日志/WAL 异地归档)。
  • snapshot 要传到集群外(对象存储/S3/异地机房)。放同一个 etcd 磁盘上 = 没备(磁盘坏一起坏)。
  • 加密:etcd 里可能有 Secret(除非你配了 KMS 静态加密,见第 12 章)。备份文件落地前必须加密,否则备份库泄露 = 全集群密码泄露。

📦 案例:某厂把 etcd snapshot 存在同一个云可用区的对象存储。结果该可用区网络隔离,控制面全挂,而备份也在那个区——恢复时拉不到备份。教训:备份必须跨可用区,最好是跨 region。

25.2.3 恢复:重建一个”有记忆”的集群

恢复 etcd 不是”把文件丢回去”那么简单,它有严格顺序,因为 API Server 和 etcd 是绑定的。

标准恢复流程(以 kubeadm 风格为例):

  1. 停掉所有 kube-apiserver、kube-controller-manager、kube-scheduler(它们持有着旧 etcd 的连接)。
  2. 在每个 etcd 成员上,用 etcdctl snapshot restore 把快照恢复到新的数据目录(restore 会重写成员 ID 和集群元数据,所以必须”恢复成新集群”而非”覆盖旧库”)。
  3. 用新数据目录重启 etcd 集群。
  4. 重启控制面组件,确认它们重新连上恢复后的 etcd。
  5. 校验:关键命名空间、核心 Deployment、Secret 都在。

⚠️ 坑(致命):snapshot restore 产出的 etcd 必须用全新的 data dir 和 member 名。有人图省事直接覆盖旧 --data-dir,结果新 etcd 启动时带着旧 member ID 却对不上快照里的集群 ID,集群起不来。恢复是”建一个新 etcd,把记忆灌进去”,不是”修旧 etcd”。

⚠️ 坑(更致命):恢复后,集群里所有节点的 kubelet 仍然是旧 etcd 时代的”老节点”。如果恢复的是”昨天 23:00 的快照”,那么今天新增的节点、今天改的标签,都不在快照里。恢复不是魔法——它把时间拨回备份点,备份点之后的所有变更物理消失。这就是 RPO 的真实含义:你丢的就是备份点到灾难点之间的那段时间。

25.2.4 自动化备份:别靠人记

手动备份 = 没人做。必须自动化:

  • 用 CronJob 或独立 sidecar 定期 etcdctl snapshot save + 上传 S3。
  • 用 Prometheus 监控”上次成功备份时间”,超过 RPO 阈值就告警。
  • 定期演练恢复(见 25.6),不演练的备份等于没备份——你永远不知道 snapshot 是不是坏的。

25.3 Velero:不只是 etcd,还有”资源和卷”

etcd snapshot 是”整个集群级别”的核弹级备份——恢复就是全量回滚到某个时间点。但很多时候你要的是更细粒度、更应用视角的备份:

  • 我只想备份 production 命名空间的 Deployment/Service/ConfigMap;
  • 我想把 PVC 里的持久数据也一起备(etcd 里只存 PVC 的元数据,不存卷里的 actual 字节);
  • 我想把整个命名空间”搬到另一个集群”(迁移/多集群)。

这就是 Velero(原 Heptio Ark,CNCF 项目)。它的设计哲学很 K8s:

  • 资源备份:Velero 调 API Server 的 LIST,把指定资源序列化成 JSON 存到对象存储——本质就是”用声明式对象本身当备份介质”。恢复时再 kubectl apply 回去。
  • 卷备份:通过给 PV 打快照(云厂商的 volume snapshot,或 restic/Restic 集成)把 actual 数据也备走。
  • 粒度:可以按 namespace、label、resource 类型筛选,不是全量。

墨:对比 etcd snapshot 和 Velero,你什么时候用哪个?

你:etcd snapshot 是”集群级、全量、时间点恢复”——用于整个控制面塌了的灾难恢复,粒度粗但保命;Velero 是”应用级、可筛选、能跨集群迁移”——用于误删命名空间、需要搬应用、需要细粒度恢复的日常场景。两者互补:etcd 是最后兜底,Velero 是日常手术刀。

墨:完美。而且你看 Velero 的”资源备份”——它其实就是把”声明式对象”当成可搬运的数据。这又回到第 3 章的老话:声明式 API 的最大红利,是”集群的状态”和”描述它的 YAML”是同一个东西。所以备份/迁移 K8s,本质上就是搬运 YAML(+ 卷数据),不需要导出什么私有格式。

📦 案例:一次 kubectl delete namespace ci 误删了持续集成命名空间(含 Jenkins、流水线、凭据)。etcd snapshot 在 2 小时前,全量回滚会丢掉这两小时所有其他变更,不可接受。运维用 Velero 的”按 namespace 选择性恢复”,只把 ci 命名空间从最近备份里 restore,其他命名空间毫发无损。这就是细粒度备份的价值。


25.4 跨区域容灾:Region 没了怎么办

etcd snapshot 救的是”控制面逻辑故障”。但如果是整个云 Region 不可用(机房断电、骨干网断、账号被封),单集群在 Region A 里再稳也没用——它和 Region A 一起没了。

三种跨区架构,从省到贵:

25.4.1 冷备(Cold Standby):备份 + 演练

Region A 跑生产,etcd snapshot 定期同步到 Region B 的对象存储。平时 Region B 没集群(或只有空壳)。灾难发生后,在 Region B 用快照恢复出集群,把流量切过去。

  • RTO:几十分钟到几小时(取决于恢复+切流量速度)。
  • RPO:取决于备份频率(如 15 分钟)。
  • 成本:最低,Region B 平时空着。

演化本质:这就是 25.2 的 etcd 恢复流程 + “异地存储”。最朴素,但有效。

25.4.2 温备(Warm Standby / Active-Passive):常驻备用控制面

Region B 一直跑着一个”空控制面 + 节点”,etcd 用 Region A 的定期快照 + WAL 持续归档异步追平(etcd 的 disaster recovery 支持把 WAL 增量喂给备用)。灾难时,B 已经基本跟上 A 的状态,切过去只需补最后一点。

  • RTO:分钟级。
  • RPO:秒~分钟级(取决于 WAL 同步延迟)。
  • 成本:B 常驻资源。

25.4.3 双活(Active-Active):跨区同步复制

两个 Region 都跑生产,etcd 用跨区 Raft(成员分布在两个 Region,写要跨区确认)。或者应用层双写。这是 RPO≈0、RTO≈0 的终极形态。

  • 代价:跨区 Raft 的写入延迟 = 跨区 RTT(几十 ms),且网络分区时两区会”脑裂”——必须靠 quorum 规则处理(跨区奇数成员,保证总有一边能成多数派)。架构极复杂,成本极高。
  • 谁用:金融核心、跨国大厂的关键链路。

墨:你品品”双活”和”CP 系统”的冲突在哪?

你:etcd 是 CP,跨区部署时,如果两区网络抖一下,为了保一致,写入会卡(可用性让位一致性)。双活想”零中断”,但 CP 在分区时偏偏要”停写保一致”。所以跨区 etcd 双活,本质是在和 CAP 定理搏斗——分区发生时,要么接受写卡顿(CP),要么接受两区数据分叉(CA,放弃强一致)。没有免费午餐。

墨:一针见血。这就是为什么大多数业务选”温备”而非”双活”——双活的成本和复杂度,远超它救的那点 RTO。系统设计者的成熟,是知道在哪妥协,不是追求理论上的完美。


25.5 持久数据的容灾:PVC 不藏在 etcd 里

一个常被忽略的真相:etcd 里没有你的业务数据。etcd 存的是 PVC 这个”对象”(说这个卷存在、绑给谁),但卷里的 actual 字节在底层存储(云盘/EBS/ceph/本地盘)。

所以容灾要分两层算:

  1. 控制面状态(Deployment、PVC 元数据、Secret)→ etcd snapshot / Velero。
  2. 持久数据(卷里的字节)→ 底层存储的快照/复制(云盘跨区域复制、Ceph 远端镜像、Velero + 卷快照)。

⚠️ 坑:只备 etcd,没备卷,灾难恢复后你得到”一堆指向不存在数据的 PVC”——Deployment 起来了,但数据库文件是空的。灾备必须控制面 + 数据面双线备,且恢复时要保证”控制面恢复到的时间点”和”数据面恢复到的点”对得上(否则应用读到一半旧一半新的数据,一致性崩)。这就是 RPO 对账的难点:两个独立备份的恢复点要对齐。

📦 案例:某数据库用 StatefulSet + PVC,etcd 备在 T0,卷快照在 T0+3min。恢复时用了 etcd 的 T0 和卷的 T0+3min——结果是”控制面以为数据库在 T0 的状态,卷里却是 T0+3min 的数据”,事务日志超前,数据库启动报错。正确做法是恢复时让卷也回到 T0(或在应用层做 crash recovery)。跨系统恢复点的对齐,是灾备里最容易被忽略的细节。


25.6 演练:不演练的灾备 = 没灾备

这是全章最重要的一节,写在最后不是因为它最不重要,是因为它要建立在前面所有知识上。

墨:你觉得”备份做好了、脚本写好了”,灾备就完成了吗?

你:没。没演练过,你不知道脚本对不对、snapshot 坏没坏、恢复要多久。真出事时第一次跑恢复,等于拿生产事故练手。

墨:对。灾备的成熟度,不看你备了多少,看你能不能在承诺的 RTO 内、不丢超过 RPO 的数据、把服务拉起来,而且这件事你上周刚练过。**

演练怎么做:

  1. GameDay(故障演练日):定期(季度/月)故意制造灾难——关掉控制面、删命名空间、把 etcd 卷卸了——然后按预案恢复,计时。
  2. 混沌工程:用 Chaos Mesh / Litmus 注入”etcd 延迟""API Server 不可用""节点失联”,验证控制面自愈和你的监控是否真的报警。
  3. 恢复计时 + 复盘:每次演练记录实际 RTO/RPO,和承诺对比。超了就改预案。
  4. 恢复验证:恢复完不是”进程起来了”,是”业务真的能读写、数据对得上”。要有校验脚本。

📦 案例:某公司灾备文档写”RTO 30 分钟”,但每年演练时实际要 2 小时——因为恢复脚本里有个手动步骤没人写清楚,且 etcd snapshot 因证书过期早就不能用了(告警被忽略)。演练把这两个雷都排了。不演练,这两个雷会一直埋到真出事那天同时炸。


25.7 演化线小结

  • 问题:副本防单点,防不了”人误删""Region 天灾""逻辑灾难”;且 etcd 这个单一真理源一旦丢,集群失忆。灾备要回答 RTO/RPO 两个量化承诺。
  • 设计分离:
    • 集群级兜底 → etcd snapshot(一致性快照,异地加密存,演练恢复流程);
    • 应用级手术刀 → Velero(把声明式对象当可搬运数据 + 卷快照,细粒度/跨集群);
    • Region 级 → 冷备/温备/双活三层,逐级加成本降 RTO/RPO;
    • 数据面 → 卷快照独立于 etcd,且恢复点要对齐。
  • 演化规律:又是”收敛单一真理源简化备份”(etcd 一个点)、“声明式对象即可搬运数据”(Velero 搬运 YAML)、“知道在哪妥协”(大多选温备而非双活)。K8s 没发明灾备,但它用”声明式 + 单一状态源”让灾备从”拼 50 个组件”变成”搬一个 etcd + 一批 YAML”——架构级的简化。

25.8 思考题

🔧 思考题 1(RPO 真实含义):有人说”etcd 有三副本强一致,所以 RPO=0”。这句话错在哪?

【参考答案】三副本保证的是”副本间不丢、不分歧”,但 RPO=0 要求的是”灾难发生时零数据丢失”。两者不是一回事:① 误操作的 delete 会同步删三副本,三副本救不了;② 灾难恢复靠 snapshot,snapshot 是备份时刻的快照,备份点到灾难点之间的所有变更物理不存在于备份里,必然丢失——这就是 RPO>0 的来源;③ Region 级故障连三副本一起没。RPO=0 只有”跨区同步双活且每笔写都跨区确认成功”才可能,三副本同 Region 做不到。所以”三副本=RPO 0”是经典误解。

🔧 思考题 2(恢复顺序):恢复 etcd 时,为什么必须先停 API Server 等控制面组件,再用 snapshot restore 起新 etcd,最后才重启控制面?顺序反过来会怎样?

【参考答案】因为 API Server/控制器/调度器都长连接持有旧 etcd 的 lease 和 watch。如果先 restore 新 etcd 却不先停它们,旧控制面会继续往”旧 etcd 的数据目录”写、读,和 restore 出来的新数据打架;更糟的是它们缓存着旧 resourceVersion,会以为新 etcd 的数据”过期”或产生冲突写入。正确顺序:停控制面(切断所有对旧 etcd 的持有)→ restore 出新 etcd(新 member ID/集群)→ 起新 etcd → 重启控制面(它们以”全新客户端”连上新 etcd,重新 LIST+WATCH 建立缓存)。反过来做,会出现”控制面连着半旧半新的 etcd”的脏状态,可能把旧数据写回新库,污染恢复结果。

🔧 思考题 3(双活与 CAP):跨区 etcd 双活想做到 RPO≈0、RTO≈0,但 etcd 是 CP。当两区之间网络分区时,会发生什么?该怎么设计成员分布来避免”两区都写不了”?

【参考答案】分区时,CP 的 etcd 必须保一致,于是拿不到 quorum 的那一边会拒绝写入(可用性让位一致性)。如果两区各放偶数成员(如 2+2),分区后每边都不到多数(需 ≥3/5 或 ≥2/3?注意总成员数),两边全写不了——最糟情况。正确设计:跨区成员数取奇数且偏置一边,例如 5 成员=3 在 Region A、2 在 Region B。平时写要 3 票,A 区内 3 票即可快写(跨区只作容灾);若 A 区整体挂(分区或故障),B 区 2 票仍不足 3,整个集群写停——但这是”保一致”的代价。要 B 区也能写,需 5=2A+3B 或加第区成 3+2+2(7 成员,任意两区≥? ),让”任意单区故障后剩余仍能成多数”。本质是:用奇数+跨区偏置,保证”最多一个区故障时仍有 quorum 可写”,但”两区同时故障”仍不可写。双活永远在和 CAP 搏斗,没有完美解。

🔧 思考题 4(进阶·总账):你的业务是”电商订单系统”,老板要求 RTO=1 小时、RPO=5 分钟,预算有限(不能上跨区双活)。请你设计一套够用不浪费的灾备方案,列出:备份什么、频率、存哪、恢复流程、演练节奏。并指出一个”跨系统恢复点对齐”的坑。

【参考答案】

  • 备份什么:① etcd snapshot(集群级兜底);② Velero 备份 production/order 命名空间资源 + PVC 卷快照(应用级+数据);③ 卷快照独立于 etcd,因为卷里是真实订单数据。
  • 频率:etcd snapshot 每 5 分钟一次(对齐 RPO=5min);Velero 资源备份每 10 分钟;卷快照每 5 分钟(云盘跨区域复制)。
  • 存哪:全部上传到另一个 Region 的对象存储(跨区,防 Region 天灾);etcd snapshot 落地前加密(含 Secret)。
  • 恢复流程:Region A 挂 → 在 Region B 起空控制面 → etcd restore 最近快照 → 重启控制面 → Velero 恢复订单命名空间资源 → 挂载跨区域复制过来的卷快照 → 切 DNS/流量到 B。全程计时,目标 <1h。
  • 演练:每季度 GameDay,故意关 A 区控制面,按流程恢复到 B,验证订单能读写、数据对得上,记录实际 RTO/RPO。
  • 跨系统对齐坑:etcd snapshot 在 T0,卷快照在 T0+2min(异步复制有延迟)。恢复时若用 etcd 的 T0 + 卷的 T0+2min,会出现”控制面认为订单状态在 T0,卷里数据是 T0+2min”——订单表可能多出 2 分钟已提交但未在控制面记录的事务,或反之。必须在恢复时让卷也回滚到与 etcd 相同的 T0 点(或用数据库自身 crash recovery 重放/截断到一致点)。这就是”两个独立备份的恢复点要对齐”,是细粒度灾备最容易翻车的细节。

墨:灾备聊完,最后剩一块硬骨头——安全攻防。前面 13 章讲了安全模型(认证/RBAC/NetworkPolicy/PSA),但那是从”设计者怎么防”的角度。这章我们从”攻击者怎么打”的角度反推:一个真实攻击者进了集群,他会怎么横向移动?你的那些防御,哪个是纸糊的?这就是第 26 章——安全实战攻防与最小暴露。

(第 25 章 完)