专业书籍精读 · SRE · 第 7 章
Site Reliability Engineering · Ch 7 · Google · 2016
你手机上的自动更新、公司里的自动打卡,都是「自动化」。Google 有一群叫 SRE 的工程师,专职让网站别挂;他们要照看的机器是几十万台这个量级,靠人一台台点显然不行。这一章讲的就是:他们花了十几年,怎么把「人照着清单干」一步步变成「系统自己照顾自己」——以及这条路上踩过的坑。
你大概以为自动化最大的好处是省时间。Google 的答案是:不是,省时间排在最后一位。排第一的是每次都一模一样。一件事让一百个人手工做,会做出一百个略有差别的结果;让脚本做一百遍,一百遍都一样(哪怕一样地错)。规模一大,「一样」就比「快」值钱得多——因为排查故障时最贵的那笔账,往往不是「代码写错了」,而是「这台跟那台不一样,可没人知道哪不一样」。
早年 Google 开一个新机房,SRE 得照着一份几百步的清单干上好几周:装这个、配那个、再核对那个。清单是写给人看的,人会跳步、会理解偏差、会累。更糟的是干完之后,没人说得清「正确的样子」到底长什么样——只剩那份会过期的清单,和几个老师傅的记忆。
两步。第一步,把「检查」写成程序:不再问老师傅「这台配好了没」,而是写一堆小检查,一条条验「该有的东西在不在」,哪条不对就直接告诉你哪不对。第二步才是关键,把检查变成修复:程序既然已经知道「应该是什么样」,那就让它顺手改对。
这里有个要紧的性质,叫幂等:不管现在是什么状态,跑一遍就回到该有的样子,跑十遍和跑一遍结果一样。像自动回正的方向盘——你不用知道它现在偏了多少,松手它就归位。有了这个性质,自动化才敢反复跑、随时跑。
顺着这条路往上是一道台阶:人手干 → 某人写了个脚本 → 脚本变成大家通用的工具 → 工具被做进系统、跟着系统一起发布 → 最后一级,系统自己发现出问题、自己修好,没人被惊动。Google 的集群系统 Borg(后来的 Kubernetes 就是学它)站在最后一级:你只管说「我要这个版本的五份」,剩下的它自己盯着。
一句实话:自动化把人从操作里移出去的同时,也把人从理解里移出去了——等到自动化自己出错的那天,值班的人可能既没手感、也找不到手册。
自动化不是「省人力的工具」,是放大器:它把你做对的事放大,也把你做错的事放大,而且放大得比人快得多。所以真正该追的不是「写更多脚本」,而是「把系统做成不需要外挂脚本也能自己照顾自己」。
想看五级阶梯、幂等调谐的机制和示意图? → 切到精读版
这一章讲的不是「该写什么脚本」,而是一条演进路线:自动化的终点不是「人把操作交给脚本」,而是「系统自带自治能力、根本不需要外挂的自动化」。它给出自动化价值的排序(省时间排最后)、一道五级阶梯、Google 从手工清单 → 幂等对齐 → 服务自带接口 → Borg 声明式的实录,以及一条冷静的警告:自动化是力的放大器,而放大力气并不会自动让「力气用对地方」变准。
本章属于 SRE 书 Part II「原则」。上承第 5 章《消除琐务》(说清「为什么要自动化」)与第 6 章《监控》(说清「怎么知道该动手」),下启第 8 章《发布工程》(把自动化用到交付路径上)。对应现实里:Kubernetes 的控制器、Terraform / Ansible 的幂等执行、各家的 GitOps 平台——这一章是它们共同的思想原型。
「手工运维扛不住规模」人人会说,但扛不住的到底是什么?本章指出三处塌方:一致性——人做重复劳动不可能每次一样,几十台还行,几万台时「每台略有不同」就是排障时最贵的那笔债;可重复——清单是自然语言,读的人不同结果就不同,而且清单本身会过期,没人知道哪一步已经没用了;可审计——手工操作不留结构化痕迹,事后复盘只能靠回忆。
更值得抄下来的是本章给「为什么要自动化」排的序,它把最常被拿来当理由的那条放在了最后:
本章开篇那句判断值得贴在墙上:自动化是力的放大器(force multiplier),不是万灵药;而放大力气,并不会自动让「力气用对地方」这件事变准。推论有两条,都反直觉:把一个烂流程自动化,你得到的是一个跑得更快的烂流程;所以动手前该问的不是「这步能不能写成脚本」,而是「这步本来就该存在吗」——第 5 章的答案是先删、再简化、最后才自动化。
顺着这条线,本章真正的主题就不是「怎么写自动化」,而是自动化归谁所有、放在哪一层。
本章拿「把数据库主库从一个机房切到另一个」这一件事,摆出了五个层级:
① 没有自动化:DBA 手工把主库切过去。② 外部的、专用的:某个 SRE 在自己家目录里写了个切换脚本。③ 外部的、通用的:这个能力被并进大家共用的通用工具,数据库只是它支持的一种。④ 内部的、系统自带的:数据库自己就带切换能力,随它一起发布、一起测试。⑤ 不需要自动化:数据库自己发现问题、自己切,没人被惊动。
阶梯的关键不在「自动化程度」,而在这段代码与它所描述的系统之间的距离。第 ②③ 级最常见,也最脆弱:它写在系统外面,系统一改它就错,而改系统的人根本不知道它存在。
早期的集群启用是一份几百步的清单加人工核对。SRE 的第一步不是写「一键启用」,而是写检查:他们扩展 Python 的单元测试框架,让每条测试去验真实生产里的一个事实(这个服务的配置指向对的地方吗?它依赖的东西就绪了吗?),并把测试串成依赖链——前置的那条失败了,后面的直接不跑,报错直指最前面那条断链,而不是甩出一屏互相掩盖的红色。
为什么先做检查、不先做动作?因为你没法自动化一个你还判定不了对错的状态。检查先于修复,是这条路线的第一条判据,也是今天写 operator、写巡检最容易跳过的一步。
有了检查,下一步是让程序顺手把不对的地方改对:每条检查配一个修复动作,要求幂等——无论当前处在什么状态,跑一遍之后都落到期望状态,跑 N 遍与跑一遍等价。
这一步看着只是少了一次人工,实际上是整章的转折点:系统的正确性不再定义为「依次做对了哪几步」,而定义为「现状是否等于期望状态」。命令式脚本必须知道自己跑到了第几步,中途挂掉就难以重入;幂等的对齐动作不关心历史,随时重跑都安全——这正是后来 Kubernetes 控制循环、terraform apply 的内核。
接着冒出来的问题不是技术的。自动化由 SRE 写、系统由开发团队改,于是分工固化成一种专业化:SRE 成了「唯一会开这台机器的人」,而开发团队对自己的服务怎么被拉起来一无所知。结果是自动化持续腐烂——服务每改一点,外挂的自动化就断一次,修它的人越来越像在做考古。
本章的解法是把所有权搬回去:让每个服务自己暴露一套管理接口,集群启用退化成「依次调用各服务自己的接口」。谁的服务谁负责它能不能被自动拉起来,自动化就随服务一起演进、一起测试,不会掉队。这就是从第 ③ 级跳到第 ④ 级的那一步,也是今天「谁构建谁运维」「服务自带 operator」的祖宗。
最后一跳来自平台。Borg 把一堆机器抽象成一台仓库规模的计算机:你不再说「在 A 机器上装 B 程序」,而是声明「我要这个版本的五个副本、每个多少 CPU 和内存」,Borg 负责放置、监控、失败后重新调度。到这一层,第 ⑤ 级不再是个理想——自愈成了平台的默认行为,而不是某人写的脚本。
代价也在这里:平台把复杂度收进去了,但没有消灭它。你换来「操作变简单」,付出的是「出问题时要理解的东西更深了」——「调度器为什么不给我调上来」,比「哪台机器没装上」难懂得多。
五级阶梯不是「越高越好」,而是「按变更频率和规模选」。一年跑两次、只影响三台机器的操作,写自动化多半亏。
| 层级 | 典型形态 | 什么时候选它 | 代价 |
|---|---|---|---|
| ① 手工 | 人照清单操作 | 一次性、低频(年 1–2 次)、十几台以内 | 不一致、不可审计;规模一涨立刻塌 |
| ② 外部专用 | 某人家目录里的脚本 | 应急、验证想法的试验阶段 | 知识私有化;一次服务变更就腐烂 |
| ③ 外部通用 | 共用的切换 / 部署工具 | 一类系统共性强、需跨团队复用 | 离系统最远,服务改了它不知道,最易静默失效 |
| ④ 系统自带 | 服务自带管理接口 / operator | 服务长期存在、变更频繁 | 服务团队要承担运维接口的开发与测试 |
| ⑤ 无需自动化 | 平台声明式 + 自愈 | 有平台可依托(Borg / Kubernetes) | 平台成为新的复杂度与单点,排障门槛更高 |
第二张表是同一件事的另一个切面:你打算怎么定义「对」。
| 命令式脚本 | 幂等调谐(声明式) | |
|---|---|---|
| 正确性定义 | 步骤依次执行成功 | 现状 == 期望状态 |
| 中途失败 | 难重入,要人判断「跑到哪了」 | 直接重跑 |
| 配置漂移 | 发现不了 | 每轮调谐自动纠正 |
| 写起来 | 直观、成本低 | 要先能判定状态,前期贵 |
| 适合 | 真正的一次性动作、数据迁移 | 长期运行的期望状态维护 |
还有一条本章没写成公式、但可以直接拿来拍板的判据:自动化的收益 ≈ 变更频率 × 受影响规模 × 单次人工成本 − 建设成本 − 维护成本。最常被漏掉的是维护成本,而它恰恰就是第 ③ 级自动化大批死亡的原因。
这一章是今天一整类基础设施的思想原型。Kubernetes 的控制器就是「幂等对齐」的工业化:每个控制器不停比较期望与现状,有差就改——而 Kubernetes 正是 Borg 经验的公开后继者。Terraform / Ansible 的卖点也是同一条性质:同一份描述反复 apply,结果收敛到同一处。
反过来,本章的警告也在现实里反复应验:自动化动作快、范围广,一旦目标选错,破坏也是自动化的速度。所以现代实践里,分级发布 + 爆炸半径限制 + 一键回滚不是可选项,而是自动化的配套安全带。
① 一句话:自动化的终点不是「人把操作交给脚本」,而是「系统自带自治、不需要外挂自动化」。
② 自动化是力的放大器,不是万灵药——放大力气不会让力气用得更准;自动化一个烂流程只会得到跑得更快的烂流程。
③ 价值排序是反直觉的:一致性 > 平台 > 更快修复 > 更快动作 > 省时间,省时间垫底。
④ 五级阶梯:手工 → 外部专用 → 外部通用 → 系统自带 → 无需自动化;判据不是自动化程度,而是代码离它管的系统有多远。
⑤ 路线是「先检查、后修复」:判定不了对错的状态没法自动化;检查要串成依赖链,报错直指最前面那条断链。
⑥ 转折点是幂等:正确性从「依次做对哪几步」变成「现状 == 期望状态」,于是随时可重跑——Kubernetes 控制器与 terraform apply 的内核。
⑦ 所有权决定寿命:外挂在系统之外的自动化会静默腐烂;把管理接口做进服务自身,自动化才跟得上演进。
⑧ Borg 把自愈变成平台默认行为,代价是复杂度被收进平台、排障门槛更高。
⑨ 自动化让失败也能规模化:手工擦盘一天擦不了几台,自动化几分钟擦一整批——限幅、确认与回滚是配套安全带。
⑩ 拍板判据:收益 ≈ 变更频率 × 规模 × 单次人工成本 −(建设 + 维护);维护成本最常被漏掉。