混沌工程:主动发现系统弱点

概述 传统的可靠性保障思路是"尽量不出故障"——加监控、加告警、加冗余。但这种被动防御有一个根本缺陷:你不知道系统在故障发生时的真实表现,直到故障真正发生。 混沌工程反其道而行之:主动、可控地注入故障,在故障变成事故之前发现系统的弱点。 它不是搞破坏,而是一种科学的实验方法——提出假设(“系统应该能承受某节点故障”),设计实验(杀掉一个节点),验证假设(服务是否仍然正常),发现弱点(如果服务异常了)。 Netflix 的 Chaos Monkey 开创了这一领域,如今混沌工程已经成为 SRE 体系的重要组成部分。从原理、实验设计、爆炸半径控制、Kubernetes 实战到常态化实践,详细梳理如何把混沌工程从"概念"落地为"日常实践"。 关于混沌工程的原则,可参考 Principles of Chaos Engineering 和 Chaos Engineering Book。 一、混沌工程的原理 核心思想 混沌工程的核心思想是: 在正常流量期间,通过有意注入故障来验证系统的弹性,从而在故障变成事故之前发现并修复弱点。 这与传统的测试有本质区别: 维度 传统测试 混沌工程 目标 验证"代码是否正确" 验证"系统是否能承受故障" 环境 测试环境 生产环境(或接近生产的预发环境) 故障来源 预定义的测试用例 真实模拟的故障场景 发现时机 开发阶段 运行阶段 关注点 功能正确性 系统弹性 为什么要在生产环境做 混沌工程最反直觉的一点是"在生产环境注入故障"。为什么不 在测试环境做? 测试环境无法复制生产的复杂性:生产环境的流量模式、数据量、网络拓扑、依赖关系与测试环境完全不同 测试环境的故障不会造成真实影响:没有压力,就不会暴露在压力下才出现的问题 只有在生产环境才能验证完整的恢复链路:告警是否触发?On-Call 是否响应?自动恢复是否生效? 当然,直接在生产环境做混沌实验需要严格的控制——这正是"爆炸半径控制"要解决的问题。 混沌工程的四条原则 根据 Principles of Chaos Engineering,混沌工程遵循以下原则: 围绕稳态行为定义"正常":先定义系统的正常状态(SLI/SLO),再注入故障看是否偏离 假设稳态在对照组和实验组中都保持:一部分流量/节点不注入故障(对照组),一部分注入(实验组),对比差异 在真实环境中实验:生产环境或接近生产的环境 自动化持续运行:不是一次性实验,而是持续自动化运行 混沌工程的收益 chaos_engineering_benefits: direct_benefits: - "提前发现系统弱点和单点故障" - "验证告警和恢复机制是否有效" - "提升团队对故障的响应能力" - "验证架构设计假设是否成立" indirect_benefits: - "建立团队对系统弹性的信心" - "驱动架构改进(从'看起来能扛'到'验证过能扛')" - "减少真实故障的 MTTR(因为已经演练过类似场景)" - "培养'故障不可避免'的工程文化" 二、从 Chaos Monkey 说起 Netflix 的混沌工程演进 Netflix 是混沌工程的开创者,其演进路径值得参考:...

December 17, 2024 · 8 分钟 · 1689 字 · 徐保金

服务依赖地图与故障域分析:从拓扑发现到爆炸半径控制

概述 在现代微服务架构中,一个看似简单的用户请求可能穿越数十个服务节点。当故障发生时,SRE 工程师面对的第一个问题往往不是"怎么修",而是"影响范围有多大"。如果无法快速回答这个问题,故障恢复就会被拖延在无休止的排查中。 服务依赖地图(Service Dependency Map)和故障域分析(Failure Domain Analysis)是解决这一问题的工程方法论。前者解决"谁依赖谁、怎么依赖"的认知问题,后者解决"故障会扩散到哪、爆炸半径多大"的控制问题。两者结合,构成了 SRE 可靠性工程的基础设施。 从依赖拓扑的发现方法出发,深入分析故障域的识别与隔离策略,最后给出爆炸半径控制的工程实践方案。 服务依赖的复杂性本质 微服务架构下的依赖特征 单体应用时代的依赖关系是显式的、编译期的——通过 import 语句和函数调用就能完整描绘依赖图。微服务架构彻底改变了这一范式: 维度 单体应用 微服务架构 依赖发现方式 代码静态分析 运行时流量观测 依赖类型 函数调用 HTTP/gRPC/消息队列/事件总线 依赖稳定性 编译期确定 运行时动态变化 依赖可见性 IDE 可直接跳转 需要专门工具发现 故障传播路径 进程内异常栈 跨网络级联故障 依赖数量级 几十到几百 几百到几千 依赖关系的分类体系 并非所有依赖都具有相同的风险等级。一个成熟的依赖地图必须对依赖关系进行分类标注: 按调用方式分类: 同步调用:HTTP REST、gRPC、数据库查询。调用方阻塞等待响应,是级联故障的主要传播路径。 异步调用:消息队列(Kafka、RabbitMQ)、事件总线。调用方不阻塞,但消费端故障可能导致消息积压。 共享资源依赖:共用数据库、缓存集群、存储卷。资源竞争可能引发间接故障。 基础设施依赖:DNS、服务发现、配置中心。这类依赖故障影响面极广,属于关键路径。 按关键性分类: 强依赖:被依赖方不可用时,调用方无法完成核心功能。例如订单服务依赖库存服务。 弱依赖:被依赖方不可用时,调用方可降级运行。例如商品详情页依赖推荐服务。 条件依赖:在特定场景下才触发的依赖。例如促销活动期间才调用的优惠券服务。 # 依赖分类标注示例 class DependencyType: SYNC_HTTP = "sync_http" SYNC_GRPC = "sync_grpc" ASYNC_MQ = "async_mq" SHARED_DB = "shared_db" SHARED_CACHE = "shared_cache" INFRA_DNS = "infra_dns" INFRA_SERVICE_DISCOVERY = "infra_sd" class DependencyCriticality: STRONG = "strong" # 不可降级 WEAK = "weak" # 可降级 CONDITIONAL = "conditional" # 条件触发 # 依赖关系数据结构 class ServiceDependency: def __init__(self, caller, callee, dep_type, criticality): self....

December 16, 2024 · 17 分钟 · 3412 字 · 徐保金

Postmortem 文化:从故障中学习的工程实践

概述 每一个故障都是一次免费的学习机会——前提是你有机制从中提取经验。Postmortem(事后复盘)不是写检讨书,不是找替罪羊,而是一套结构化的工程方法,用于把故障中的经验转化为系统性的改进。 Google SRE 的核心信条之一是:“Blameless postmortem”——无指责复盘。复盘的焦点永远是"系统为什么失败了"而非"谁搞砸了"。这不是温情主义,而是工程理性:如果人们在复盘时感到威胁,他们就会隐藏信息,而你将永远无法看到故障的真正根因。 从复盘文化的意义、blameless 原则、根因分析方法、复盘模板、改进项跟踪到组织文化障碍,详细梳理如何把 Postmortem 从"走流程"变成"真学习"。 关于 Postmortem 文化的系统方法论,可参考 Google SRE Book - Postmortem Culture 和 Google SRE Workbook - Postmortem。 一、为什么需要 Postmortem 文化 故障不可避免的工程现实 分布式系统的故障不是"如果"的问题,而是"何时"的问题。一个典型的微服务架构可能有上百个服务节点、数十个依赖系统、跨多个可用区部署,组合复杂度呈指数级增长。在这个复杂度下,以下场景几乎必然发生: 网络分区导致服务间调用超时 配置变更引发级联故障 依赖的第三方 API 限流或不可用 数据库连接池耗尽 某次发布引入了边界条件的 bug 问题不在于故障是否发生,而在于:同一个故障是否会重复发生。 不做复盘的代价 没有 Postmortem 文化的团队,通常会陷入以下循环: 故障发生 → 紧急修复 → 松一口气 → 不了了之 → 类似故障再次发生 这个循环的代价远比你想象的大: 维度 代价 重复故障 同类根因未消除,故障反复发生,MTBF 无法提升 知识断层 关键排查经验留在个人脑中,人员流动后经验丢失 信任消耗 团队反复犯类似错误,管理层和用户信任持续下降 个人压力 无制度保障,值班人员独自承担心理压力,加速 burnout 改进无追踪 修复措施停留在口头和聊天记录中,无跟踪无验收 做 Postmortem 的工程价值 一个成熟的 Postmortem 体系能带来三个层面的价值:...

November 26, 2024 · 6 分钟 · 1255 字 · 徐保金

SRE核心理念:SLI、SLO与错误预算

概述 SRE(站点可靠性工程)的核心理念是:用工程方法管理可靠性。其中最关键的工具就是 SLI、SLO 和错误预算。 SLI:服务等级指标 SLI 是衡量系统可靠性的量化指标。常见的 SLI 包括: 可用性:成功请求数 / 总请求数 延迟:P99 响应时间 < 200ms 吞吐量:QPS > 10000 正确性:数据一致性校验通过率 选择 SLI 的关键原则:从用户视角出发。用户不关心你的 CPU 使用率,只关心请求是否成功、是否够快。 SLO:服务等级目标 SLO 是 SLI 的目标值。例如: slo: availability: target: 0.999 # 99.9% 可用性 window: "30d" latency: target: 200 # P99 < 200ms window: "30d" 99.9% 的可用性意味着每月允许约 43.8 分钟 不可用。 错误预算 错误预算是 SRE 最精妙的设计: SLO 设为 99.9% → 错误预算 = 0.1% 预算未耗尽:可以发布新功能、做激进变更 预算耗尽:冻结发布,专注稳定性改进 这个机制让"稳定性 vs 迭代速度"不再是口角之争,而是可量化的工程决策。 实践建议 从核心服务开始:不要试图一次性定义所有服务的 SLO 先粗糙后精细:初始 SLO 可以基于历史数据粗略设定,逐步迭代 定期回顾:每月 review SLO 达成情况,调整不合理的指标 自动化告警:基于错误预算消耗速率设置告警,而非固定阈值 总结 SLI/SLO/错误预算构成了 SRE 的度量基石。没有度量就没有管理——这正是 SRE 区别于传统运维的核心。

October 28, 2024 · 1 分钟 · 86 字 · 徐保金

SRE 与传统运维的本质区别

概述 很多团队把 SRE 理解为"运维换个名字"——招几个会写脚本的人,改个 Title,就算转型了。这种认知忽略了一个根本事实:SRE 是一种工程方法论,不是一套工具链。Google 在 2003 年创建 SRE 职能时,核心理念就是"用软件工程方法解决运维问题",这从根本上改变了运维的定位、工作方式和文化。 从组织定位、文化差异、工程化实践和度量体系四个维度,系统性地剖析 SRE 与传统运维的本质区别,并给出团队转型路径建议。 一、组织定位:工程师 vs 支撑角色 传统运维的定位困境 传统运维团队通常被定位为"支撑角色"——开发负责写代码,运维负责让代码跑起来。这种分工看似清晰,实则制造了一个致命的对立面: 开发追求"快":快速上线、快速迭代,功能越多越好 运维追求"稳":变更越少越好,最好什么都别动 这种目标冲突导致的结果是:开发把运维当作发布的障碍,运维把开发当作故障的根源。最终演变为一个"拉锯战"——开发提需求,运维挡需求,谁的话语权大谁说了算。 SRE 的定位:工程师 SRE 的根本定位是软件工程师,只是专注于"可靠性"这个领域。Google SRE Book 第一章就明确指出: “SRE is what happens when you ask a software engineer to design an operations team.” 来源:Google SRE Book - Introduction 这意味着 SRE 的工作方式是工程化的: 遇到重复劳动 → 写工具自动化 遇到故障 → 做根因分析并修复系统性问题 遇到容量问题 → 建模型、做预测 遇到流程瓶颈 → 优化流程,而非增加人力 具体对比 维度 传统运维 SRE 定位 支撑角色,被动响应 工程师,主动设计 工作内容 工单处理、手动变更、故障排查 系统设计、自动化、可靠性工程 成功标准 系统没出事 系统在 SLO 范围内,错误预算可控 与开发关系 对立面(快 vs 稳) 协作伙伴(共同对 SLO 负责) 二、文化差异:工程文化 vs 经验文化 错误预算 vs 人工兜底 传统运维文化中,“可用性"是一个模糊的概念——领导说"要 4 个 9”,运维就拼命堆冗余、加监控、人工值守。一旦出了事故,就增加人手和流程来"防止再犯"。...

October 11, 2024 · 4 分钟 · 705 字 · 徐保金

性能工程:SRE 视角的系统优化方法论

概述 性能问题几乎是每个 SRE 都会遇到的高频场景:用户反馈"好慢"、告警说"P99 延迟超标"、监控显示"CPU 快满了"。但很多团队对性能问题的处理方式是"哪里高了调哪里"——CPU 高了就加机器,SQL 慢了就加索引,延迟高了就加缓存。这种头痛医头的做法短期内可能有效,但长期来看会让系统越来越复杂、成本越来越高、问题越来越难排查。 性能工程(Performance Engineering)与性能调优(Performance Tuning)有本质区别。性能调优是"发现问题→优化"的反应式过程;性能工程是"建立基线→持续度量→主动发现→系统优化"的工程化体系。SRE 的视角不是"让某个接口快 10ms",而是"建立系统性的性能管理体系,让性能问题在被用户感知之前发现和解决"。 从方法论、分析框架、基线建立、瓶颈定位、优化策略到持续管理,详细梳理 SRE 视角的性能工程。 关于性能分析的系统性方法,可参考 Brendan Gregg - USE Method 和 Tom Wilkie - RED Method。 一、性能工程 vs 性能调优 概念区分 维度 性能调优 性能工程 时机 性能问题出现后 贯穿系统全生命周期 目标 解决当前的性能问题 建立持续的性能管理体系 方法 经验驱动,试试看 数据驱动,详细分析 范围 聚焦特定瓶颈 覆盖全栈(应用→中间件→基础设施) 产出 问题解决 基线、SLO、监控、优化策略 持续性 一次性 持续度量和管理 为什么 SRE 需要性能工程 没有性能工程的团队: 用户投诉"慢" → 紧急排查 → 发现 SQL 慢 → 加索引 → 一个月后又慢了 → 发现是缓存命中率低 → 加缓存 → 又一个月后又慢了 → 发现是连接池不够 → 调连接池 → 循环往复,系统越来越复杂,问题越来越多 有性能工程的团队: 建立性能基线 → 持续监控 → 发现 P99 缓慢上升(用户还没感知) → 主动分析 → 定位到数据库查询模式变化 → 优化查询 → 在用户感知之前解决问题 性能工程的价值:...

August 29, 2024 · 9 分钟 · 1903 字 · 徐保金

事件管理与 On-Call 机制设计

概述 SRE 有一句名言:“系统一定会出故障,区别在于你是被它叫醒的还是主动管理它的。” 事件管理不是"出了事再处理",而是一套从预防、检测、响应到学习的完整工程体系。 从事件分级、On-Call 轮值、事故响应流程、Postmortem 文化、告警治理五个方面,详细梳理如何构建一个可落地的 On-Call 体系。 关于事件管理的系统方法论,可参考 Google SRE Book - Managing Incidents 和 Google SRE Book - Postmortem Culture。 一、事件分级标准 没有分级的事件管理等于没有管理——所有事件都按紧急处理,结果就是没有真正的紧急。合理的事件分级是 On-Call 体系的基石。 P0-P4 事件定义 级别 定义 影响范围 响应时效 示例 P0 生产服务完全不可用 全量用户受影响 立即响应,<5min 核心服务宕机、数据库不可用 P1 核心功能严重降级 大量用户受影响 <15min 支付失败率飙升、API 错误率 >10% P2 部分功能降级 部分用户受影响 <30min 某区域延迟劣化、非核心服务异常 P3 潜在风险 暂无直接影响 <2h(工作时间) 磁盘水位 >80%、单节点故障 P4 优化建议 无影响 下一工作日 告警阈值优化、文档补充 分级原则 分级的本质是资源调度优先级——让有限的人力优先处理影响最大的问题: 以用户影响为准,而非技术指标:CPU 99% 是 P3,但如果导致用户请求超时就是 P1 明确定义,避免模糊:“大量用户"是 30% 还是 50%?需要量化 可自动判定:理想状态下,事件级别应该能通过告警规则自动确定 # 告警分级规则示例 groups: - name: incident-grading rules: # P0: 核心服务完全不可用 - alert: P0ServiceDown expr: up{job="critical-service"} == 0 for: 1m labels: severity: P0 page: true # 触发电话告警 escalation: true # 自动升级到主管 annotations: summary: "核心服务不可用" runbook: "https://wiki/incident/p0-service-down" # P1: 错误率超过 SLO - alert: P1HighErrorRate expr: | sum(rate(http_requests_total{status=~"5....

August 23, 2024 · 6 分钟 · 1270 字 · 徐保金

告警策略设计:从噪声到信号

概述 告警是监控系统的"最后一公里",也是最难做好的一环。一个常见的困境是:服务器上跑着几十个告警规则,每天产生上百条告警通知,值班工程师在微信/钉钉/邮件的轮番轰炸下逐渐麻木——真正紧急的告警被淹没在噪声中,直到客户投诉才发现系统早已出问题。 SRE 的黄金法则是:每一条告警都应该有明确的处理动作。如果一个告警收到后既不需要立即处理,也不需要记录跟踪,那它就不应该存在。从告警疲劳问题出发,详细梳理告警分级、SLO-based 告警设计、抑制与聚合策略、告警度量指标和治理方法,帮助你从"告警噪声"中提取出真正的"信号"。 参考来源:Google SRE Book《Monitoring Distributed Systems》、Prometheus 告警好的实践 一、告警疲劳:问题的根源 1.1 告警泛滥的典型表现 某团队告警统计(一周): ┌──────────────────────────┬────────┬──────────┐ │ 告警类型 │ 数量 │ 实际处理 │ ├──────────────────────────┼────────┼──────────┤ │ CPU 使用率 > 80% │ 156 │ 3 │ │ 磁盘使用率 > 70% │ 89 │ 2 │ │ Pod 重启 │ 34 │ 5 │ │ HTTP 5xx 错误率 > 1% │ 12 │ 4 │ │ 数据库连接数 > 80% │ 8 │ 1 │ │ 证书即将过期 │ 3 │ 1 │ │ 服务不可达 │ 2 │ 2 │ ├──────────────────────────┼────────┼──────────┤ │ 总计 │ 304 │ 18 │ └──────────────────────────┴────────┴──────────┘ 有效告警率:18/304 = 5....

August 19, 2024 · 8 分钟 · 1647 字 · 徐保金

消除琐事:SRE 的事务性工作治理

概述 Google SRE Book 中有一条经常被引用的原则:SRE 团队的琐事工作不应超过总工作时间的 50%。这条原则看似简单,但在实践中,很多 SRE 团队的琐事比例远超 50%——有的甚至达到 80% 以上。 为什么 SRE 要如此认真地对待"琐事"?因为琐事是可靠性的隐形杀手: 琐事占用大量时间,让工程师没有精力做真正提升可靠性的事 琐事通常是手工操作,容易出错,反而引入新的故障 琐事导致 burnout,优秀工程师流失 琐事无法规模化——系统增长 10 倍,琐事也增长 10 倍 从 toil 的定义与判定、琐事来源分析、自动化消除路径、50% 上限原则、度量与追踪方法到团队实践,详细梳理如何治理事务性工作。 关于 toil 的系统论述,可参考 Google SRE Book - Eliminating Toil。 一、Toil 的定义与判定 什么是 Toil Google SRE 对 toil 的定义是: 与运行生产服务相关的、手动的、重复的、可自动化的、战术性的、无持久价值的、与服务规模成正比增长的工作。 这个定义包含六个关键特征,缺一不可: 特征 含义 示例 手动的 需要人工操作而非自动执行 手动扩容、手动清理日志 重复的 不是一次性的,会反复出现 每次发布都需要手动修改配置 可自动化的 有明确的规则和步骤,机器能做 手动检查磁盘空间并清理 战术性的 被动响应而非主动规划 救火式处理告警 无持久价值的 做完之后没有产生可复用的产出 手动重启服务(没有改进自愈机制) 与规模成正比 系统增长,工作量同步增长 每增加一台服务器就需要手动配置一次 什么不是 Toil 识别 toil 的同时,也要识别什么不是 toil,避免把有价值的工作误判为琐事:...

July 24, 2024 · 9 分钟 · 1800 字 · 徐保金

Runbook 编写指南:让运维知识可复现

概述 凌晨三点被告警叫醒,面对一个不熟悉的服务,你能多快恢复?如果你需要翻聊天记录、问同事、翻代码才能弄清楚怎么处理,那说明你的团队缺一样东西——Runbook。 Runbook 是 SRE 体系中最基础但也最容易被忽视的工程实践。它是连接"告警"和"行动"的桥梁——告警告诉你"出问题了",Runbook 告诉你"该怎么办"。一个好的 Runbook 能让任何有基础技术能力的工程师在 On-Call 时有效响应告警,而不依赖特定人员的"脑内知识"。 从 Runbook 的作用、结构设计、自动化、版本管理、质量评审到实战模板,详细梳理如何编写和维护高质量的 Runbook。 关于 Runbook 的好的实践,可参考 Google SRE Workbook - Operational Overload 和 PagerDuty - Runbook Guide。 一、Runbook 的作用与价值 什么是 Runbook Runbook 是一份标准化的操作手册,描述了如何诊断和处理特定的运维场景。它的核心特征是: 任何有基础技术能力的工程师,按照 Runbook 的步骤操作,都能正确处理对应的告警或运维任务——而不需要依赖特定人员的主观经验。 没有 Runbook 的代价 没有 Runbook 的故障响应: 告警触发 → On-Call 工程师看到 "Redis 内存使用率 90%" → 不知道该怎么办 → 打电话问张三(张三写过这个服务) → 张三不在线 → 打电话问李四 → 李四说 "可能是某个 key 太大了,你看看" → 工程师花 20 分钟找大 key → 最终处理了,但用了 40 分钟 有 Runbook 的故障响应: 告警触发 → On-Call 工程师看到 "Redis 内存使用率 90%" → 告警中附带 Runbook 链接 → 打开 Runbook,按步骤操作 → Step 1: 执行 redis-cli --bigkeys 扫描大 key → Step 2: 发现 user_session:xxx 占用 500MB → Step 3: 执行 DEL user_session:xxx → 10 分钟恢复 Runbook 的价值 维度 价值 降低 MTTR 按步骤操作比从零排查快得多 降低 On-Call 压力 有文档可依,不需要"什么都知道" 知识沉淀 把个人经验转化为组织知识 新人友好 新人 On-Call 不再需要"跟班学习"数周 驱动自动化 Runbook 中的步骤是自动化的候选对象 Runbook 与其他文档的区别 文档类型 目的 读者 场景 Runbook 处理特定告警/故障的操作步骤 On-Call 工程师 告警触发时 Architecture Doc 描述系统架构和设计决策 所有工程师 理解系统设计 API Doc 描述 API 接口规范 开发者 对接 API Postmortem 分析故障根因和改进项 团队 故障复盘 Playbook 更广泛的工作流程指南 团队 执行复杂任务 二、Runbook 的结构设计 标准结构 一个好的 Runbook 应该包含以下部分:...

June 11, 2024 · 10 分钟 · 1945 字 · 徐保金