专业书籍精读 · SRE · 第 27 章
Site Reliability Engineering · Ch 27 · Google · 2016
你手机上那个 App,昨天突然多了个新功能;或者某天打开一个大网站,首页挂出了一个全新产品的入口。从「工程师把代码写完」到「几亿人同时用上」,中间隔着一段最容易出事的路。这一章讲的就是:怎么把一件新东西交到海量用户手上,而不把它变成一场事故。
上线翻车,最常见的原因往往不是代码写错了。代码的错,测试能测出来;真正要命的是「一下子涌进来太多人」——机器不够、缓存是空的、旁边那个被你调用的团队压根不知道今天有人要用他们。这些事在测试环境里一个都复现不出来,因为测试环境没有真实的人。
「怎么安全上线」这件事,过去长在个别老员工的脑子里:老王发布过二十次,知道要提前两周申请机器、要提前通知下游、要留一条能一键退回去的路;新人不知道,于是把老王踩过的坑再踩一遍。公司只有一个团队时还好,有几百个团队时,同一个坑就要被踩几百遍。经验不可复制,就等于没有经验。
这一章给的办法有四件:
其一,养一支专门帮别人上线的小队。他们自己不写你的产品,角色更像婚礼策划或搬家公司——菜不是他们做的,但每个环节会在哪出岔子,他们比你清楚,因为他们一年经手上百场。
其二,一份检查单,就像飞行员起飞前逐条念的那张。上面每一条,都是过去某一次真实事故留下的教训。清单的妙处是:它不考验你记性好不好,只要求你一条条过。
其三,别一次全放开。先让一百个人里的一个用上,观察一天没事,再放给十个人里的一个,最后才是所有人。就像新菜先给一桌客人试,而不是当天就写进整本菜单。
其四,先悄悄跑一遍。把真实客人的订单抄一份给新厨房,让它照着做一遍,做完直接倒掉——客人吃的还是老厨房出的菜,但你已经知道新厨房会不会手忙脚乱了。
上线从「看运气、看有没有老师傅在场」变成了一件可以照着做的事。还有个反直觉的讲究:这份检查单要会变瘦——某一条如果已经被工具默认解决了(比如系统自带了限流),就该从单子上划掉。代价也很实在:这一整套是有成本的,小团队照搬容易把上线拖慢,最后检查单沦为走过场的签字。
上线的风险不在代码里,在第一分钟涌进来的人数、和那个没被通知到的依赖方身上。办法是把老师傅的经验做成一份会新陈代谢的检查单,再配上「先给一小撮人用」和「先悄悄空跑一遍」。
想进到具体机制、放量比例和示意图? → 切到精读版
SRE 第 27 章的主张是:「安全上线」不是个人能力,而是一套可以被工程化、被复用、被规模化的流程。Google 的答案是一支专职的发布协调工程(Launch Coordination Engineering, LCE)顾问团队,加上一份不断新陈代谢的发布检查单——检查单上的每一条都是某次真实事故的墓志铭,而一条问题一旦能被共享基础设施默认解决,就该从单子上删掉。
本章是 SRE 全书 Part III「实践」的收官章:前面二十多章讲的是系统跑起来之后怎么保住可靠性(告警、事故响应、过载、级联失败、共识、管线、数据完整性),这一章回到系统第一次跑起来的那一刻——上承 Ch25 / Ch26(运行期的管线与数据可靠性),下启 Part IV「管理」(SRE 怎么组织与协作)。对应现实里的角色,就是今天平台团队做的生产就绪评审(production readiness review)与发布工程。
一次面向上亿用户的发布,牵动的从来不止一个团队:你的服务要扩容、你依赖的三四个内部服务要为你加配额、监控要提前配好、客户端要发新版本、法务与客服要准备好。任何一环漏掉,事故就在上线后的第一个小时里等你。
而 Google 的规模让两件事同时成立:发布很多——任何「每次都拉一堆专家开会评审」的方案都撑不住;单次发布很猛——新产品挂到主页入口上,流量从 0 直接跳到峰值,没有自然的爬坡期,这直接推翻了「先小范围上线、让用户自然增长」的常规假设。
不解决会怎样?发布知识永远停留在口口相传,同一个错误在几百个团队里被重复发明几百遍。这一章要做的,就是把「发布经验」从个人属性变成组织属性。
本章给的定义故意宽到有点过分:发布 = 任何引入外部可见变化的新代码。为什么不只管「大版本」?因为翻车的往往恰恰是那些没人觉得它算发布的变更——一个上线了的实验开关、一个悄悄换掉的后端实现。定义窄了,风险就从流程的缝里漏出去。
管它的是 LCE:一支专职的 SRE 顾问团队,同时扮演五个角色——审计可靠性、联络互不认识的团队、驱动发布的技术面、作为把关人签字放行、教育开发者。
关键是它只做顾问、不接管:LCE 不替你改代码、不替你扩容。这不是客气,是算术——一支十几人的团队要覆盖全公司的发布,唯一可扩展的姿势是把知识沉淀成制度,而不是把人塞进每个项目。一旦 LCE 开始动手替人做事,它立刻变成整个公司的发布瓶颈。
本章给发布流程立了五条标准:轻量(对开发者的负担要小)、健壮(有明确标准,不靠评审人当天心情)、彻底(重要的事一件不漏)、可扩展(能覆盖大量发布)、可适应(大改版和加个按钮共用一套流程,但深度不同)。
这五条互相打架:越彻底越不轻量,规则越死越不可适应。解法不是取中间值,而是两招:按风险分档(高风险走全程评审、低风险走自助),以及把问题推给基础设施——能被框架默认解决的,就不该再问人。
检查单按九个板块组织:架构与依赖、集成、容量规划、故障模式、客户端行为、流程与自动化、开发流程、外部依赖、上线计划。抽几条看它到底在问什么:
最反直觉的一条机制在这里:检查单必须会瘦身。一个问题若能靠共享基础设施默认解决(框架自带限流、客户端库自带退避、平台自动申请配额),它就该从检查单里删掉、变成默认行为。只增不减的检查单会越来越长,最后所有人闭眼打勾——那一刻它就从安全网变成了官僚仪式。这也给了 LCE 一个真正的产出物:推动全公司收敛到共同的基础设施。
分阶段放量:新版本先接一小撮真实流量。Google 在公开的 CRE 文章里给过具体档位——起步 1%–10%(看用户基数),内部二进制发布走 1% → 10% → 100%;1% 用来抓「九成请求都报错」这种粗错误,10% 用来抓小一个数量级的错误率或延迟上升。每级之间至少留 24 小时,因为流量有昼夜周期,不跨一整天就看不到峰值时段的表现。
影子发布(dark launch):把线上真实请求复制一份打给新实现,丢弃它返回的结果,用户看到的仍是旧实现的响应,只收集新实现的错误率与延迟。它的价值是在零用户影响的前提下拿到真实流量的形状——真实请求的分布永远比压测脚本刁钻。代价也明确:写操作不能影子跑(会产生真实副作用),且下游负载会翻倍。
特性开关(feature flag):把部署和放量拆成两件事。代码先上线但关着,出事时关开关(秒级)而非回滚二进制(分钟级,还要重启);按比例 / 按地区放量也变成一行配置。
过载行为与压测:上线前把服务压到崩溃点,确认它是优雅降级还是雪崩——一个在 120% 负载下开始拒绝多余请求、保住 100% 那部分的服务是健康的;一个在 105% 负载下响应时间飙到 30 秒、把上游拖垮的服务不是。
四件套不是并列的备选项,它们挡的是不同类型的风险——真正的选型问题是「这次发布的主要风险是什么」。
表 1 · 四种降风险手段:各自挡住什么、挡不住什么
| 手段 | 挡住什么 | 挡不住什么 | 代价 |
|---|---|---|---|
| 分阶段放量 | 错误率、延迟、资源用量的回归;有真实用户反馈 | 只在全量规模才出现的问题(热点、锁竞争、配额触顶) | 放量周期长(每级 ≥24h);期间要维持新旧两版并存 |
| 影子发布 | 新实现在真实流量形状下的崩溃、超时、内存泄漏 | 写操作;与用户体验相关的一切(结果对不对没人看) | 下游负载翻倍;需要流量复制设施 |
| 特性开关 | 止损速度——出事秒级关掉,不用回滚二进制 | 已经写坏的数据;开关本身的组合爆炸 | 开关会堆积成技术债,旧开关不清理就是定时炸弹 |
| 压测 / 过载演练 | 崩溃点在哪、过载时是降级还是雪崩 | 真实用户的行为分布;长尾请求与缓存冷启动 | 需要贴近生产的环境,否则结论不可信 |
第二个权衡是流程该多重。本章的「可扩展 + 可适应」两条约束,落地就是按风险分档——一支专职团队不可能全程陪跑每一次发布。
表 2 · 发布评审的三档,以及各自的代价
| 档位 | 适合 | 拿到什么 | 代价 |
|---|---|---|---|
| 全程协调评审 | 全新产品、跨十几个团队、流量从 0 直跳峰值 | 跨团队的盲区被外部视角逐条捞出来 | 贵;协调团队一旦成为必经关卡就是全公司的瓶颈 |
| 自助检查单 + 抽查 | 已有产品的大功能、架构有实质变更 | 覆盖面广、成本低,团队自己长出发布意识 | 没人盯着就容易闭眼打勾,质量随团队浮动 |
| 免检(走标准平台) | 常规迭代,且全程走公司统一的框架与流水线 | 发布频率最高,风险由平台默认兜住 | 要求基础设施真的默认安全;平台自身出问题是全局的 |
本章立场很明确:目标是让越来越多的发布掉到第三档。检查单不是终点,是通往「默认安全的基础设施」的路标——这也是为什么「删检查项」和「加检查项」一样重要。
今天你不需要一支 LCE,但这套机制被拆散进了标配工具:Spinnaker / Kayenta 把「放量 + 人工看图」自动化成自动金丝雀分析;Argo Rollouts、Flagger 把放量阶梯写进 Kubernetes 的声明式配置;LaunchDarkly、OpenFeature 把特性开关做成独立产品;GitHub 的 Scientist 把影子发布做成函数级的库。LCE 这个角色本身,演化成了平台团队的生产就绪评审。
1%–10%、内部发布走 1% → 10% → 100%、每级至少 24 小时(因为流量有昼夜周期)。Google Cloud Blog「How release canaries can save your bacon」, CRE Life Lessons ↗50 个时序数据点统计才有意义,这意味着一次金丝雀分析常常要跑几个小时。Google Cloud Blog「Canary analysis: lessons learned from Google and Waze」↗use、新实现放进 try,运行时永远返回旧实现的结果,同时随机化两者执行顺序、比对结果、吞掉并记录新实现抛的异常——用生产真流量验证重构,而用户一无所感。github/scientist README ↗① 一句话:安全上线不是个人能力,是可工程化、可复用、可规模化的流程——本章把它做成了「一支顾问团队 + 一份会新陈代谢的检查单」。
② 发布的定义故意宽:任何引入外部可见变化的新代码;出事的常是那些「没人觉得它算发布」的变更。
③ LCE 的五个角色:审计、联络、驱动技术面、把关签字、教育;只做顾问不接管是它能规模化的前提。
④ 流程的五条约束:轻量 / 健壮 / 彻底 / 可扩展 / 可适应;它们互相打架,解法是按风险分档 + 把问题推给基础设施。
⑤ 检查单九板块:架构与依赖、集成、容量规划、故障模式、客户端行为、流程与自动化、开发流程、外部依赖、上线计划。
⑥ 最反直觉的一条:检查单要会瘦身——问题被基础设施默认解决后就该删掉;只增不减必然腐烂成橡皮图章。
⑦ 四件套各挡一类风险:分阶段放量(1% → 10% → 100%,每级 ≥24h)、影子发布(真流量、丢响应)、特性开关(部署与放量解耦、秒级止损)、压测(确认过载时是降级不是雪崩)。
⑧ 落到今天:Kayenta / Argo Rollouts / Flagger / LaunchDarkly / Scientist 各自吃掉了一块,LCE 的角色演化成了平台团队的生产就绪评审。
⑨ 边界:深层架构问题检查单救不了;全新产品没有灰度可打;客户端的坏行为发出去就收不回来。