Skip to content

高可用系统设计

高可用不是“机器尽量不宕机”,而是系统在故障、维护和流量变化中,仍能持续满足用户成功率、延迟、正确性和数据目标。

设计高可用系统,需要把抽象口号转换成可测量目标、明确故障边界,并验证恢复路径。

可用性指标

SLI、SLO 与 SLA

概念作用示例
SLI实际测量的服务指标成功请求数 / 有效请求数
SLO团队希望达到的目标30 天窗口内成功率 ≥ 99.9%
SLA对客户的正式承诺及后果低于承诺后提供服务赔偿

内部 SLO 通常应比外部 SLA 更严格,为检测和处理事故保留空间。

面向请求的可用性可以表示为:

Availability=good eventsvalid events

“Good” 必须包含用户真正关心的条件。HTTP 200 但返回错误业务结果、响应慢到超过截止时间,可能仍应计为失败。

参考:Google SRE Workbook:Implementing SLOsGoogle SRE:Embracing Risk

可用性目标

下面按连续 30 天和 365 天估算最大不可用时间:

可用性目标30 天错误预算365 天错误预算
99%7 小时 12 分3 天 15 小时 36 分
99.9%43 分 12 秒8 小时 45 分 36 秒
99.99%4 分 19 秒52 分 34 秒
99.999%约 26 秒5 分 15 秒

每增加一个“九”,容许失败量缩小一个数量级,成本却往往非线性增长。应根据用户影响和业务损失选择目标,而不是默认追求 100%。

对于全球或部分可用的服务,按成功请求比例计算通常比简单的停机分钟数更有意义。

故障域

故障域是可能一起失效的一组资源。副本只有跨越独立故障域,才能真正降低共同失效概率。

需要逐层询问:

  • 多个实例是否在同一节点?
  • 多个节点是否在同一机架或可用区?
  • 多个区域是否依赖同一控制面、身份系统或数据库?
  • 配置错误和软件缺陷是否会同时传播到全部副本?
  • 故障转移依赖的组件在事故中是否仍可使用?

基础设施独立并不能消除软件、配置和人为操作造成的相关性故障。

冗余、隔离与容量余量

冗余模式

模式特点主要风险
Active-Active多个副本或站点同时承载流量状态一致性、双写、故障域相关性
Active-Passive备用容量平时不承载或少量承载备用漂移、切换慢、长期未验证
N+1 / N+2为单个或多个实例故障预留余量只覆盖预期数量的故障
Quorum多数副本可用时继续读写网络分区、延迟和成员变更

冗余必须配合隔离。若一个租户能耗尽共享线程池,或一个区域故障把全部流量压向容量不足的区域,副本会把局部故障扩散成全局故障。

故障后的容量

假设三个可用区平均分担流量,每个区域正常运行在 60% 容量。一个区域退出后,剩余两个区域各自要承接约 50% 的额外流量:

如果正常水位已经达到 80%,故障转移后理论需求会超过单区容量。高可用容量规划必须同时计算:

  • 正常峰值;
  • 最大计划内维护;
  • 目标故障域退出后的峰值;
  • 扩容完成前的短时余量;
  • 恢复时数据同步和缓存预热的额外开销。

健康检查与故障转移

启动、存活与就绪探针

  • 启动探针:应用是否已经完成初始化,避免启动阶段被误杀。
  • 存活探针:进程是否陷入不可恢复状态,需要重启。
  • 就绪探针:实例当前是否应该接收新流量。

高负载时把“响应稍慢”误判成“不存活”,可能导致实例被批量重启,进一步减少容量。依赖变慢时,通常应先取消就绪、停止接收新流量,再判断进程是否真的需要重启。

故障转移时序

每一步都要有明确时间预算和失败处理。故障检测太敏感会产生误切换,太迟钝则延长用户错误;自动切换如果没有自动回退或人工停止条件,也可能在两个站点间震荡。

延伸阅读:Kubernetes Liveness, Readiness, and Startup Probes

数据高可用

计算副本可以快速重建,数据错误却可能永久传播。数据层需要同时讨论:

  • 复制:副本数、同步或异步复制、确认条件;
  • 一致性:故障与网络分区时允许读到多旧的数据;
  • 持久性:已确认写入能否在节点或区域故障后保留;
  • 备份:独立介质、不可变副本、保留周期和恢复验证;
  • 恢复:切换后如何补齐增量、处理冲突和防止脑裂。

RPO 与 RTO

  • RPO(Recovery Point Objective):最多允许丢失多长时间范围的数据。
  • RTO(Recovery Time Objective):事故后最多允许多长时间恢复服务。

RPO 为 0 通常要求同步持久化到独立故障域,可能增加写入延迟;很短的 RTO 通常要求预置容量、自动化切换和经常演练。两者都必须对应具体故障场景。

备份成功不代表可以恢复。需要定期在隔离环境执行恢复,验证数据完整性、应用兼容性、权限和实际耗时。

防止级联故障

隔离舱

按依赖、租户、请求优先级或业务类型划分独立的线程池、连接池和并发预算,使一个故障源不能耗尽全部资源。

熔断

熔断器在依赖持续失败时快速拒绝,减少无效等待;半开状态用少量探测验证恢复。熔断不能替代超时,也不能只依据单次错误打开。

降级

预先定义核心与非核心功能:

  • 返回旧缓存或部分结果;
  • 暂停推荐、画像等非关键调用;
  • 把同步工作改为稍后处理;
  • 对低优先级租户或后台任务负载丢弃。

降级路径必须常态化测试。如果它只在事故时执行,往往会因为数据、权限或代码漂移而失效。

安全部署与计划内维护

发布本身是最常见的故障来源之一:

  • 使用金丝雀或分批发布限制爆炸半径;
  • 兼容新旧版本同时运行时的数据和协议;
  • 设置最大不可用实例数和最小健康容量;
  • 新实例通过启动与就绪检查后再接流量;
  • 旧实例先停止新流量,再优雅处理在途请求;
  • 依据 SLO 和关键业务指标自动暂停,而不是只看进程是否启动。

在 Kubernetes 中,PodDisruptionBudget 可以限制遵循 Eviction API 的自愿中断,但不能防止节点故障,也不能约束所有直接删除和滚动更新行为。

参考:Kubernetes Disruptions

灾难恢复层次

模式资源准备恢复速度成本
Backup & Restore仅保留备份最慢最低
Pilot Light保留核心数据和最小服务较慢较低
Warm Standby备用环境以较小规模运行较快较高
Multi-site Active-Active多站点持续承载流量最快最高、复杂度最高

模式选择由业务 RPO/RTO、数据一致性、合规和成本决定。不要在没有跨区域故障目标时直接建设最复杂的双活。

错误预算

错误预算是 SLO 允许的失败量:

Error Budget=1SLO

如果 30 天 SLO 为 99.9%,错误预算就是有效事件的 0.1%。团队可以据此决定:

  • 何时继续快速发布;
  • 何时冻结高风险变更;
  • 哪些可靠性工作优先;
  • 告警是否对应错误预算的快速消耗。

告警应面向用户影响和预算消耗,而不是每个瞬时 CPU 波动。短窗口发现快速燃烧,长窗口发现持续缓慢退化。

故障验证

故障矩阵

为每种故障记录:

故障预期影响自动动作人工动作验证指标
单实例退出无用户影响负载摘除、补副本成功率、剩余容量
单可用区退出核心 SLO 不变跨区转流量确认容量SLI、跨区延迟
数据库主库故障短时写入受限选主或切换检查一致性RTO、数据正确性
依赖超时非核心功能降级熔断、负载丢弃联系依赖方Goodput、重试率
错误发布小范围影响暂停或回滚决定继续/回滚金丝雀 SLO

故障演练

  • 实例、节点、可用区和关键依赖故障;
  • 流量峰值与故障同时发生;
  • 主从切换、备份恢复和区域恢复;
  • 错误配置与发布回滚;
  • 遥测或控制面不可用时的人工操作;
  • 从大规模重试和崩溃循环中恢复。

演练要测量实际 RTO/RPO 和用户 SLI,并把发现转化为自动化、Runbook 和架构改进。

设计检查

  • [ ] 可用性以用户成功事件定义,并有明确 SLO。
  • [ ] 已识别实例、节点、区域、依赖、配置和发布故障域。
  • [ ] 副本分布与容量余量覆盖目标故障场景。
  • [ ] 健康检查区分启动、存活和就绪,不会在高负载下批量误杀。
  • [ ] 超时、隔离、熔断、限流和降级路径已经联合验证。
  • [ ] 数据复制、备份、RPO、RTO 和一致性目标相互匹配。
  • [ ] 发布限制爆炸半径,并能依据 SLO 自动暂停。
  • [ ] 故障转移与恢复均经过带真实流量模型的演练。

参考资料