




第Ⅱ部分 指导思想
第3 章 拥抱风险 23
管理风险 23
度量服务的风险 24
服务的风险容忍度 25
辨别消费者服务的风险容忍度 26
基础设施服务的风险容忍度 28
使用错误预算的目的 30
错误预算的构建过程 31
好处 32
第4 章 服务质量目标 34
服务质量术语 34
指标 34
目标 35
协议 36
指标在实践中的应用 37
运维人员和最终用户各关心什么 37
指标的收集 37
汇总 38
指标的标准化 39
目标在实践中的应用 39
目标的定义 40
目标的选择 40
控制手段 42
SLO 可以建立用户预期 42
协议在实践中的应用 43
第5 章 减少琐事 44
琐事的定义 44
为什么琐事越少越好 45
什么算作工程工作 46
琐事繁多是不是一定不好 47
小结 48
第6 章 分布式系统的监控 49
术语定义 49
为什么要监控 50
对监控系统设置合理预期 51
现象与原因 52
黑盒监控与白盒监控 53
4 个黄金指标 53
关于长尾问题 54
度量指标时采用合适的精度 55
简化,直到不能再简化 55
将上述理念整合起来 56
监控系统的长期维护 57
Bigtable SRE :警报过多的案例 57
Gmail :可预知的、可脚本化的人工干预 58
长跑 59
小结 59
第7 章 Google 的自动化系统的演进 60
自动化的价值 60
一致性 60
平台性 61
修复速度更快 61
行动速度更快 62
节省时间 62
自动化对Google SRE 的价值 62
自动化的应用案例 63
Google SRE 的自动化使用案例 63
自动化分类的层次结构 64
让自己脱离工作:自动化所有的东西 66
舒缓疼痛:将自动化应用到集群上线中 67
使用Prodtest 检测不一致情况 68
幂等地解决不一致情况 69
专业化倾向 71
以服务为导向的集群上线流程 72
Borg :仓库规模计算机的诞生 73
可靠性是最基本的功能 74
建议 75
第8 章 发布工程 76
发布工程师的角色 76
发布工程哲学 77
自服务模型 77
追求速度 77
密闭性 77
强调策略和流程 78
持续构建与部署 78
构建 78
分支 79
测试 79
打包 79
Rapid 系统 80
部署 81
配置管理 81
小结 82
不仅仅只对Google 有用 83
一开始就进行发布工程 83
第9 章 简单化 85
系统的稳定性与灵活性 85
乏味是一种美德 86
我绝对不放弃我的代码 86
“负代码行”作为一个指标 87
最小 API 87
模块化 87
发布的简单化 88
小结 88
第Ⅲ部分 具体实践
第10 章 基于时间序列数据进行有效报警 93
Borgmon 的起源 94
应用软件的监控埋点 95
监控指标的收集 96
时间序列数据的存储 97
标签与向量 98
Borg 规则计算 99
报警 104
监控系统的分片机制 105
黑盒监控 106
配置文件的维护 106
十年之后 108
第11 章 on-call 轮值 109
介绍 109
on-call 工程师的一天 110
on-call 工作平衡 111
数量上保持平衡 111
质量上保持平衡 111
补贴措施 112
安全感 112
避免运维压力过大 114
运维压力过大 114
奸诈的敌人—运维压力不够 115
小结 115
第12 章 有效的故障排查手段 116
理论 117
实践 119
故障报告 119
定位 119
检查 120
诊断 122
测试和修复 124
神奇的负面结果 125
治愈 126
案例分析 127
使故障排查更简单 130
小结 130
第13 章 紧急事件响应 131
当系统出现问题时怎么办 131
测试导致的紧急事故 132
细节 132
响应 132
事后总结 132
变更部署带来的紧急事故 133
细节 133
事故响应 134
事后总结 134
流程导致的严重事故 135
细节 135
灾难响应 136
事后总结 136
所有的问题都有解决方案 137
向过去学习,而不是重复它 138
为事故保留记录 138
提出那些大的,甚至不可能的问题:假如…… 138
鼓励主动测试 138
小结 138
第14 章 紧急事故管理 140
无流程管理的紧急事故 140
对这次无流程管理的事故的剖析 141
过于关注技术问题 141
沟通不畅 141
不请自来 142
紧急事故的流程管理要素 142
嵌套式职责分离 142
控制中心 143
实时事故状态文档 143
明确公开的职责交接 143
一次流程管理良好的事故 144
什么时候对外宣布事故 144
小结 145
第15 章 事后总结:从失败中学习 146
Google 的事后总结哲学 146
协作和知识共享 148
建立事后总结文化 149
小结以及不断优化 151
第16 章 跟踪故障 152
Escalator 152
Outalator 153
聚合 154
加标签 155
分析 155
未预料到的好处 156
第17 章 测试可靠性 157
软件测试的类型 158
传统测试 159
生产测试 160
创造一个构建和测试环境 163
大规模测试 165
测试大规模使用的工具 166
针对灾难的测试 167
对速度的渴求 168
发布到生产环境 170
允许测试失败 170
集成 172
生产环境探针 173
小结 175
第18 章 SRE 部门中的软件工程实践 176
为什么软件工程项目对SRE 很重要 176
Auxon 案例分析:项目背景和要解决的问题 177
传统的容量规划方法 177
解决方案:基于意图的容量规划 179
基于意图的容量规划 180
表达产品意图的先导条件 181
Auxon 简介 182
需求和实现:成功和不足 183
提升了解程度,推进采用率 185
团队内部组成 187
在SRE 团队中培养软件工程风气 187
在SRE 团队中建立起软件工程氛围:招聘与开发时间 188
做到这一点 189
小结 190
第19 章 前端服务器的负载均衡 191
有时候硬件并不能解决问题 191
使用DNS 进行负载均衡 192
负载均衡:虚拟IP 194
第20 章 数据中心内部的负载均衡系统 197
理想情况 198
识别异常任务:流速控制和跛脚鸭任务 199
异常任务的简单应对办法:流速控制 199
一个可靠的识别异常任务的方法:跛脚鸭状态 200
利用划分子集限制连接池大小 201
选择合适的子集 201
子集选择算法一:随机选择 202
子集选择算法二:确定性算法 204
负载均衡策略 206
简单轮询算法 206
最闲轮询策略 209
加权轮询策略 210
第21 章 应对过载 212
QPS 陷阱 213
给每个用户设置限制 213
客户端侧的节流机制 214
重要性 216
资源利用率信号 217
处理过载错误 217
决定何时重试 218
连接造成的负载 220
小结 221
第22 章 处理连锁故障 223
连锁故障产生的原因和如何从设计上避免 224
服务器过载 224
资源耗尽 225
服务不可用 228
防止软件服务器过载 228
队列管理 229
流量抛弃和优雅降级 230
重试 231
请求延迟和截止时间 234
慢启动和冷缓存 236
保持调用栈永远向下 238
连锁故障的触发条件 238
进程崩溃 239
进程更新 239
新的发布 239
自然增长 239
计划中或计划外的不可用 239
连锁故障的测试 240
测试直到出现故障,还要继续测试 240
测试最常用的客户端 241
测试非关键性后端 242
解决连锁故障的立即步骤 242
增加资源 242
停止健康检查导致的任务死亡 242
重启软件服务器 242
丢弃流量 243
进入降级模式 243
消除批处理负载 244
消除有害的流量 244
小结 244
第23 章 管理关键状态:利用分布式共识来提高可靠性 246
使用共识系统的动力:分布式系统协调失败 248
案例1 :脑裂问题 249
案例2 :需要人工干预的灾备切换 249
案例3 :有问题的小组成员算法 249
分布式共识是如何工作的 250
Paxos 概要:协议示例 251
分布式共识的系统架构模式 251
可靠的复制状态机 252
可靠的复制数据存储和配置存储 252
使用领头人选举机制实现高可用的处理系统 253
分布式协调和锁服务 253
可靠的分布式队列和消息传递 254
分布式共识系统的性能问题 255
复合式Paxos :消息流过程详解 257
应对大量的读操作 258
法定租约 259
分布式共识系统的性能与网络延迟 259
快速Paxos 协议:性能优化 260
稳定的领头人机制 261
批处理 262
磁盘访问 262
分布式共识系统的部署 263
副本的数量 263
副本的位置 265
容量规划和负载均衡 266
对分布式共识系统的监控 270
小结 272
第24 章 分布式周期性任务系统 273
Cron 273
介绍 273
可靠性 274
Cron 任务和幂等性 274
大规模Cron 系统 275
对基础设施的扩展 275
对需求的扩展 276
Google Cron 系统的构建过程 277
跟踪Cron 任务的状态 277
Paxos 协议的使用 277
领头人角色和追随者角色 278
保存状态 281
运维大型Cron 系统 282
小结 283
第25 章 数据处理流水线 284
流水线设计模式的起源 284
简单流水线设计模式与大数据 284
周期性流水线模式的挑战 285
工作分发不均造成的问题 285
分布式环境中周期性数据流水线的缺点 286
监控周期性流水线的问题 287
惊群效应 287
摩尔负载模式 288
Google Workflow 简介 289
Workflow 是模型—视图—控制器(MVC)模式 290
Workflow 中的执行阶段 291
Workflow 正确性保障 291
保障业务的持续性 292
小结 294
第26 章 数据完整性:读写一致 295
数据完整性的强需求 296
提供超高的数据完整性的策略 297
备份与存档 298
云计算环境下的需求 299
保障数据完整性和可用性:Google SRE 的目标 300
数据完整性是手段,数据可用性是目标 300
交付一个恢复系统,而非备份系统 301
造成数据丢失的事故类型 301
维护数据完整性的深度和广度的困难之处 303
Google SRE 保障数据完整性的手段 304
24 种数据完整性的事故组合 304
第一层: 软删除 305
第二层:备份和相关的恢复方法 306
额外一层:复制机制 308
1T vs. 1E :存储更多数据没那么简单 309
第三层:早期预警 310
确保数据恢复策略可以正常工作 313
案例分析 314
Gmail—2011 年2 月:从GTape 上恢复数据( 磁带) 314
Google Music—2012 年3 月:一次意外删除事故的检测过程 315
SRE 的基本理念在数据完整性上的应用 319
保持初学者的心态 319
信任但要验证 320
不要一厢情愿 320
纵深防御 320
小结 321
第27 章 可靠地进行产品的大规模发布 322
发布协调工程师 323
发布协调工程师的角色 324
建立发布流程 325
发布检查列表 326
推动融合和简化 326
发布未知的产品 327
起草一个发布检查列表 327
架构与依赖 328
集成 328
容量规划 328
故障模式 329
客户端行为 329
流程与自动化 330
开发流程 330
外部依赖 331
发布计划 331
可靠发布所需要的方法论 332
灰度和阶段性发布 332
功能开关框架 333
应对客户端滥用行为 334
过载行为和压力测试 335
LCE 的发展 335
LCE 检查列表的变迁 336
LCE 没有解决的问题 337
小结 338
第Ⅳ部分 管理
第28 章 迅速培养SRE 加入on-call 341
新的SRE 已经招聘到了,接下来怎么办 341
培训初期:重体系,而非混乱 344
系统性、累积型的学习方式 345
目标性强的项目工作,而非琐事 346
培养反向工程能力和随机应变能力 347
反向工程:弄明白系统如何工作 347
统计学和比较性思维:在压力下坚持科学方法论 347
随机应变的能力:当意料之外的事情发生时怎么办 348
将知识串联起来:反向工程某个生产环境服务 348
有抱负的on-call 工程师的5 个特点 349
对事故的渴望:事后总结的阅读和书写 349
故障处理分角色演习 350
破坏真的东西,并且修复它们 351
维护文档是学徒任务的一部分 352
尽早、尽快见习on-call 353
on-call 之后:通过培训的仪式感,以及日后的持续教育 354
小结 354
第29 章 处理中断性任务 355
管理运维负载 356
如何决策对中断性任务的处理策略 356
不完美的机器 357
流状态 357
将一件事情做好 358
实际一点的建议 359
减少中断 361
第30 章 通过嵌入SRE 的方式帮助团队从运维过载中恢复 363
第一阶段:了解服务,了解上下文 364
确定最大的压力来源 364
找到导火索 364
第二阶段:分享背景知识 365
书写一个好的事后总结作为示范 366
将紧急事件按类型排序 366
第三阶段:主导改变 367
从基础开始 367
获取团队成员的帮助 367
解释你的逻辑推理过程 368
提出引导性问题 368
小结 369
第 31 章 SRE 与其他团队的沟通与协作 370
沟通:生产会议 371
议程 372
出席人员 373
SRE 的内部协作 374
团队构成 375
高效工作的技术 375
SRE 内部的协作案例分析:Viceroy 376
Viceroy 的诞生 376
所面临的挑战 378
建议 379
SRE 与其他部门之间的协作 380
案例分析:将DFP 迁移到F1 380
小结 382
第32 章 SRE 参与模式的演进历程 383
SRE 参与模式:是什么、怎么样以及为什么 383
PRR 模型 384
SRE 参与模型 384
替代性支持 385
PRR :简单PRR 模型 386
参与 386
分析 387
改进和重构 387
培训 388
“接手”服务 388
持续改进 388
简单PRR 模型的演进:早期参与模型 389
早期参与模型的适用对象 389
早期参与模型的优势 390
不断发展的服务:框架和SRE 平台 391
经验教训 391
影响SRE 的外部因素 392
结构化的解决方案:框架 392
新服务和管理优势 394
小结 395
第Ⅴ部分 结束语
第33 章 其他行业的实践经验 398
有其他行业背景的资深SRE 399
灾难预案与演习 400
从组织架构层面坚持不懈地对安全进行关注 401
关注任何细节 401
冗余容量 401
模拟以及进行线上灾难演习 402
培训与考核 402
对详细的需求收集和系统设计的关注 402
纵深防御 403
事后总结的文化 403
将重复性工作自动化,消除运维负载 404
结构化和理性的决策 406
小结 407
第34 章 结语 408
附录A 系统可用性 411
附录B 生产环境运维过程中的最佳实践 412
附录C 事故状态文档示范 417
附录D 事后总结示范 419
附录E 发布协调检查列表 423
附录F 生产环境会议记录示范 425
参考文献 427
索引 439__
1. 主旨
之前有人问我这本书的中心思想是什么?忘记了当时给出的答案了,但是让我现在在回答一次的话,应该是——漫步 Google 运维,讲述 Google 这个拥有海量数据、多数据中心的公司,是如果通过各种手段来保证它的可用性。接下来将会分为四部分对该书进行总结:
- 概览——了解 SRE 的定义,以及该职位与传统运维的区别
- 指导思想——讨论 SRE 的工作模式、行事方法,以及日常运维工作中关注的焦点
- 具体实践——理解 SRE 日常工作背后的理念,讨论具体的构建与运维大型分布式系统的实践。
- 管理——探索 Google 在培训,内部沟通,以及在会议方面的最佳实践。(ps 这篇笔记里针对这部分是没有描述的,感兴趣建议自己阅读
2. 概览
2.1 提出 SRE 的动力?
很多公司开发部(Dev)和运维部(Ops)是分属两个不同部门的,这种 Dev/Ops 分离的团队模型,无法避免的带来两类成本问题:
- 直接成本:随着系统复杂度的增加,部署规模的扩大,团队的大小基本与系统负载成线性关系,共同增长。
- 间接成本:研发团队和运维团队背景各异,技术能力和工具使用习惯上差距巨大,工作目标存在不可调和分歧点,而这些会严重影响产品的迭代。
但SRE 团队的作为解决上分歧点被提出来,在快速迭代与系统稳定性直接做了平衡。SRE 团队的出现也大大节省了公司的开支。

DevOps 这个名词的核心思想是尽早的将 IT 相关的技术与产品设计和开发过程结合起来,着重强调自动化而不是人工操作,以及利用软件工程手段执行运维任务等。可以认为 DevOps 是 SRE 核心理念的普适版,SER 是 DevOps 模型在 Google 的具体实践。
2.2 SRE 聚焦问题点
- 确保长期关注研发工作:Google 将 SRE 团队的运维工作限制在 50% 以内,剩余的时间花在研发项目上。你需要拥有上帝视角,才能更好的解决问题。
- 在保障服务 SLO 的前提下最大化迭代的速度:Dev 和 Ops 不可调和的矛盾点是
。 但是请记住,任何软件都不应该一味地追求 100% 可靠,因为大多数情况下 99.999% 和 100% 的可靠性是没有实质区别的。所以你需要「错误预算」。
你需要思考的是,花费巨大的精力将系统变为 100% 可靠是否能为用户带来实质意义上的好处?
从用户终端到服务器之间有很多中间系统(用户终端、家庭 wifi、网络提供商和输电线路等)这些系统综合起来的可靠性要远远低于 99.999%。
- 监控系统:一个监控系统应该只有三类输出:紧急警报、工单、日志
不论是如何优秀研发人员,过多的噪音,一定会影响你对事物的判断。狼来的故事教育我们,你要在狼真的来的时候才去呼救。
- 应急事件处理:任何需要人工操作的事情都只会延长恢复时间。一个可以自动恢复的系统即使有更多的故障发生,也要比事事都需要人工干预的可用性更高。
- 变更管理:制定符合自己公司的强管控的变更管理策略,会是一件很有趣、很有意义的事情。比如 Google 最佳实践:
- 采用渐进式的发布机制
- 迅速而准确的检测到问题的发生

- 当出现问题时,安全迅速地回退变更
- 需求预测和容量规划:这两点是保障一个业务有足够的容量和冗余度去服务预测中的未来需求。一个盈利性公司的特点是业务不会停滞不前,所以用户群一定会增长并且必然会增长。请为你设计的系统预留这部分容量,如果不能预留,也请能发现增长的趋势,及时作出应急举措。
- 资源部署:确保新上线的容量能够正确地服务而用户,你需要为自己的系统设计合适的上线验收策略。
- 效率与性能:高效地利用各种资源是任何盈利性组织都要关心的。SRE 和产品研发团队应该共同监控和优化整个系统的性能,这就相当于给服务增加了容量和提升了效率。从公司的角度看,云产品的超卖策略不就是个很棒的 idea 嘛。
2.3 庖丁解牛
你看到的跟我看到的世界虽然是同一个,但是感受却截然不同。
作为用户,我看到的只是一个可用的系统,这个系统能够完成我需要的功能。我不需要知道背后的原理。
作为研发,除了功能外,你需要做到能够快速理解底层的实现。能够看到截然不同的世界,我觉得这是很有趣的事情。
2.3.1 你看到了什么?
- 物理服务器:代表具体的硬件(有时候也代表一个 VM 虚拟机)
- 软件服务器:代表一个对外提供服务的软件系统。
注:物理服务器上可以运行任何类型的软件服务器。
2.3.2 如何管理你看到的?
管理物理服务器的系统管理软件:
- 管理物理服务器:Borg -> Kubernetes (Apache Mesos) 重新编排资源,以求达到最大化的资源利用率。
- 存储:熟悉各类存储类型,了解目前公司支持的存储类型,为你的系统找到最合适的存储。
- 网络:合理分配网络带宽,可以达到降低成本的目的。Google 使用 OpenFlow 协议的软件定义网络(SDN),是一种普通的非智能交换组件结合集中化的控制器连接方式。该控制器负责计算网络中的最佳路径。
个人觉得这个设计思路的实践可以看下这个 饿了么异地多活技术实现(二)API-Router的设计与实现
所有好的技术都应该是从真实的业务场景中提炼出来的。「生命存在的价值应该是能够创造更多的价值,技术亦是如此」,不要为了使用而使用,而是你的业务场景真的需要它,比如系统软件分布式锁服务、监控与报警系统。
Google 底层软件基础设施的设计目的是最高效的使用 Google 的硬件基础设施。站在巨人的肩膀上的你,才有可能看的更高,所以不要重复造轮子,而是学会基于轮子去造车。
2.3.3 能否站在全局视角上看问题?
一叶障目,不辩春秋。
你需要足够熟悉自己负责的业务,但同时你也需要全局视角。可以看看这个 一个完整的 Web 请求到底发生了什么
- 用户请求的处理过程:用户 -> dns -> 反向代理(负载均衡)-> 网关(鉴权) -> 业务 -> 缓存 -> DB
- 任务和数据的组织方式:发生未可知的故障的时候,你的系统是否仍能正常提供服务,你的冗余度设计的是否合理。
3. 指导思想
3.1 学会拥抱风险
「过分追求稳定性不仅限制了新功能的开发、产品交付给用户的速度,同时也很大程度的增加了成本。」
风险,定义为遭受伤害或损失的可能性。
3.1.1 如何度量服务的风险?
- 基于时间的可用性—— 可用性 = 系统正常运行时间 / (系统正常运行时间+停机时间)
- 请求成功率——可用性 = 成功请求数 / 总请求数
在 Google 基于时间的可用性毫无意义,Google 所采用的故障隔离手段保证在任何时候,任何地方对于一个给定的服务,总是可以处理一定的用户流量(ps 总结,有钱任性
请求成功率的计算需要注意一点:不是所有请求都是平等的,一个新的用户注册请求失败和一个单纯的后台接口调用请求失败是不同的
3.1.2 判断服务风险的容忍度?
- 用户的容忍度:
- 用户可用性目标

- 用户可接受的故障类型(持续低故障率或者偶尔发生的全网中断哪一个更糟糕?
- 其他服务指标(比如,广告的投放是否会拖慢用户的搜索体验,这很重要
- 服务自身对所依赖基础设施的要求:
- 了解所依赖基础设施的可用性目标
- 在基础设施的故障时候,是否有逃逸策略
- 尝试优化基础设施的成本
3.1.2 设计合适的「错误预算」
便利店收银员在收银的时候,公司会规定一个「收银差异」,在差异内的误差金额由公司承担。而「错误预算」的作用和功能类似于「收银差异」,它的使用使得讨论发布速率更容易,同时可有效的减少任何关于事故的讨论。
为发布和稳定性之间设计一个平衡点,而这个点的体现就是「错误预算」。如果你的错误预算足够的多,你可以快速迭代发布新版本,反之,请保持基于「错误预算」的契约精神,谨慎对待每一次发布。
3.2 服务质量目标
对于可靠的运维系统来说,你需要了解系统中各种行为的重要程度。Google 倾向于用不同的语义描述不同的行为,服务质量指标(SLI)、服务质量目标(SLO)以及服务质量协议(SLA)。
- 服务质量指标:请求延迟(处理请求所消耗的时间)、错误率(请求处理失败的百分比)、系统吞吐量(每秒请求数量)、可用性。
- 服务质量目标:服务某个服务质量指标的目标范围值。比如,定义搜索请求的服务质量目标为延迟小于 100ms。
SLO 的选择和公布可以帮助设立用户对于服务质量的预期,以及应对那些没有根据抱怨「服务太慢了」的行为。
- 服务质量协议:指服务与用户之间的一个明确的,或者不明确的协议,描述了在达到或者没达到服务质量目标之后的后果。
3.2.1 指标在实践中的应用
运维人员和最终用户各关系什么:只有理解用户对系统的真实需求才能真正决定哪些指标是否有用,盲目将监控系统中的所有指标都定义为 SLI 不是一个好方法
- 用户可见的服务系统 : 可用性
- 存储系统:延迟、可用性和数据持久性
- 大数据系统:关系吞吐量和端到端的延迟
- 所有系统:正确性
指标的收集、汇总、标准化
平均值 + 累计概率分布是个好建议
3.2.2 目标在实践中的应用
思考用户关系的方面入手,而不是现在能度量什么入手(如何正确做好,而非现在能做什么
- 目标定义:清晰具体为上
- 目标选择:不要仅以目前的状态为基础选择目标、保持简单、避免绝对值、SLO 越少越好、不要追求完美
- 有效的控制手段:比较指标和目标,以决定是否执行什么操作(突然想起切多活了……
- 不要建立过高的用户预期:留出一定的安全区、实际 SLO 也不要过高。
3.3 减少琐事
3.3.1 定义琐事
琐事被定义为手动性、重复性的、可以被自动化的、突发的、没有持久价值的、与服务同步线性增长的。
3.3.2 正确理解工程工作的定义
工程工作(Engineering)是一种新型的,本质上需要主观判断的工作。典型的活动主要包括:
- 软件工程:编写或修改代码,以及所有其他相关的设计和文档的工作。
- 系统工程:配置生产系统、修改现有配置,或者用一种通过一次性工作产生持久的改进的方法来书写系统文档。
- 琐事:与运维服务相关的重复性的、手工的劳动。
- 流程负担:与运维服务不直接相关的行政负担。
3.3.3 平衡
平衡琐事的比重,Google 建议琐事所占有的比例要低于 50%。 「救火很重要,但是疲于一直救火,会阻止你去思考该如何从本质上去解决问题。」
3.4 分布式系统的监控
如果你需要随时随地知道服务的运行状况,那么你需要构建一个适合自己业务的监控系统。知道服务运行状况的最终目的是保证服务的稳定性,确保其能更好的服务用户。
3.4.1 监控的 4 个黄金指标
- 延迟:服务处理某个请求所需要的时间。能够区分请求成功和失败这点很重要。
- 流量:使用系统中的某个高层次的指标针对系统负载进行度量。比如每秒 HTTP 请求数量等。
- 错误:请求失败的速率,显示、隐式、或者是策略性导致的失败。
- 饱和度:系统中某种受限资源的具体度量,比如 CPU 、内存等。
度量指标不是目的,需要为所度量指标设置合适的阈值,以期在指标不符合预期的时候,能迅速做出反应,缩小影响范围,这才是重点。
3.4.2 现象 vs 原因
你以为你以为的就是你以为的嘛?并不是
现象:什么东西出了故障
原因:为什么出了故障
例子:
- 现象:返回 HTTP 500 或者 404 回复速度很慢
- 原因:数据库服务器拒绝连接 or CPU 被某个排序操作占满,某根网线被压在机柜下面造成断续的网络丢包。
「在一切合理的答案都被逻辑排除后,剩下的那个因为主观,所以被贴上“不可能”的标签的答案 即为正解。」
3.4.3 降噪
过多监控会带来负面效应,对比一天收到几条和几百条监控你的前后变化,会发现这是一件「被人忽略,但是收益很高」的事情。
3.5 Google 的自动化系统的演进
你会变老,反应也会变慢,但机器不会,机器永远会按照你给的指令「周而复始,循环往复」的运行。
3.5.1 自动化的价值
- 一致性:任何一个人或者一群人执行数百次动作时,不可能保证每次都用同样的方式进行,但机器可以。
- 平台性:平台性的延伸价值意味着其通用性更高。
- 修复速度更快:一个自治系统恢复的速度从概率上来说会小于手工接入使其恢复的速度。
3.5.2 可靠性
你要确保的第一要素是可靠性,系统能够按照你预期的自动化方式运行,不能有分毫差距。大规模自动化运维的带来的风险是,允许大规模故障的爆发。收益和风险的正相关关系,很重要。
3.5.3 痛苦是可以被表达的
如果在推动自动化的过程中受阻,请牢记以下两点:
- 一个不亲自运行自动化的团队是没有动力去建设一个能够很容易自动化的系统的,
- 一个产品经理的时间表如果不受低质量的自动化影响,他永远优先新功能的开发,而不是简化和自动化
3.6 发布工程
发布工程是 Google 内部的一项具体工作。主要负责定义软件发布的全部步骤——包括软件是如何存储于源代码仓库,构建时是如何执行编译的,如何测试、打包、最终部署。
Google 的体量之大,所以类似流水线的精细化分工会为其带来相应的收益。但对于体量相对较小的公司,发布工程这部分工作常常由对应的研发人员或者运维人员所负责。所以我们可以借鉴 Google 在发布工程中的设计哲学,减少发布所带来的稳定性问题。
3.6.1 发布工程哲学
- 自服务模型:开发工具,制定最佳实践,让产研团队可以自己掌握和执行自己的发布流程。
- 追求速度:「面向用户的软件组件重新构建非常频繁,因为我们的目标是让用户可见的功能越快上线越好」。
- 密闭性:构建工具必须确保一致性和可重复性。
- 强调策略和流程:可回溯的历史,能够在系统发生问题的时候,让你快速回退。
3.6.2 持续构建与部署
构建、分支、测试、打包、部署,请确保每个节点所做的事情符合预期。
3.7 简单化
可靠性只有靠对最大程度的简化不断追求而得到。「凡是复杂的即为易错的」
3.7.1 系统的稳定性与灵活性
清楚地知道自己需要先探索以及失败才能真正理解需要完成的任务。一个对于 SRE 管理系统方法的建议是:「始终在系统的灵活性和稳定性上维持平衡」。SRE 通过创造流程、实践以及工具,来提高软件的可靠性。同时,SRE 需要最小化这些工作对于开发人员的灵活性造成的影响。
3.7.2 乏味是一种美德
我们总是希望程序按照设计的进行执行,可预见性的完成目标,但是生产环境中的意外是 SRE 最大的敌人。
必要复杂度和意外复杂度:
- 必要复杂度是一个给定的情况所固有的复杂度,不能从问题中移除。比如,编写一个 Web 服务器需要处理快速提供 Web 页面的必要复杂度。
- 意外复杂度可以通过工程上的努力来解决。比如,我们使用 Java 编写服务器代码,试图减少 GC 的影响。
3.7.3 我绝对不放弃我的代码
因为工程师也是人,他们经常对于自己编写的代码形成一种情感依附,这种冲突在大规模清理源代码树的时候并不常见。但是那些没有使用的源代码,会变成一颗颗会在未知时间引爆的炸弹,为了世界和平,所以请深呼吸,然后删除它。
3.7.4 「负代码行」作为一个指标
「软件膨胀」用来描述软件随着时间的推移不停地增加新功能而变得更慢和更大的趋势。这种趋势从直观上看就是不可取的,所以:
最终让你的代码结构看起来清清爽爽,这是目的
3.7.5 最小 API
设计 API 请遵循这样一个原则「不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美」。
3.7.6 模块化 + 发布简单
很多年以后,你会明白解耦合你的代码,确保「高内聚,底耦合」,是多么朴实但是又有深意的一句话。
4. 具体实践
这本书还是挺良心的,告诉了你思想是什么,同时又告诉了你基于这些思想,Google 是如何实践的。具体包含书中 10 - 27 章,将会被分为以下 7 个部分进行介绍:
- 监控:10 章
- 应急事件的处理:11 - 14 章
- 事后总结和问题根源分析:15 - 16 章
- 测试:17 章
- 容量规划:18 - 22 章
- 软件研发:23 - 26 章
- 产品设计:27 章
4.1 基于时间序列数据进行有效的报警
离开监控系统,我们就没有能力辨别一个服务是不是在正常提供服务。Google 的监控系统不仅要分析一些简单的系统指标(比如某一台在欧洲 Web 服务器的平均响应时间),还要分析更高抽象级别的指标(比如整个欧洲地区 Web 服务器的响应时间分布情况)。
4.1.1 监控系统是什么?
监控系统从本质上来说就是一个可编程的计算器。并且加入了一些语法糖,从而可以让它产生报警信息。通常由数据收集、数据存储、数据分析三个部分。
4.1.1.1 数据收集
在数据收集部分需要解决的问题是:
- 需要针对什么指标进行收集?
- 收集的时间范围是多久?
- 精细化收集的粒度需要多高?
4.1.1.2 数据存储
从单台机器上收集到的数据,需要汇总处理,那么数据如何存储,才会更好分析?
- 一般选择的都是基于时间序列的数据库,从回溯历史和硬件读写的角度,该类型的数据库都合适。
4.1.1.3 数据分析
汇总全部的数据,针对服务质量指标(SLI)设置阈值,不满足阈值的时候,通知对应负责人,人工介入解决问题。
4.1.2 报警
从日常生活中来看,我们报警,是因为遇到了需要警察帮忙才能解决的问题,系统的报警也应该如此。频繁报假警的会被处罚,对于一个具有仲裁机制的监控系统来说也应该如此,对于那种多次发出报警但是没有人响应的报警,删除它或许不是一件坏事情。
「提升专注力是一件好事情,可有可无的东西请统统忽视它。」
4.2 应急事件的处理
4.2.1 on-call 轮值
on-call 是一种手段,目的是为了保障服务的可靠性和可用性。为了 on-call 的工程师能够快速的解决问题,平衡 on-call 工作是一个很好的建议。
- 从数量上平衡:SRE 团队 50% 的时间花在软件工程上,在其余的时间中,不超过 25% 的时间用来 on-call
- 从质量上平衡:每 12 个小时的轮值最多只能处理两个紧急事件。
避免「过度联想」和「习惯性思维」。
事物的本质很少呈现表象上。脱脂牛奶也可以假装护肤霜。
4.2.2 有效的故障排查手段
系统正常,只是该系统无数异常情况下的一种特例。 —— John Allspaw (ps 这句话和「幸存者偏差」理论都很让我震惊
4.2.2.1 理论
故障排障过程被定义为反复采取假设-排除手段的过程。大致就是通过观察系统的监测指标和日志信息了解系统目前的状态。再结合我们对于系统构建的原理、运行机制,以及失败模型的了解,提出一些可能的失败原因。
4.2.2.2 实践
书写系统的故障报告是个好习惯,系统故障报告里要写清楚:
- 系统预期是什么
- 系统实际的结果是什么
- 如何重新故障
- 如何修复故障等
定位 -> 检查 -> 诊断 -> 测试和修复 这个一个完整排障的链路。
定位问题的过程中,最重要的不是排障,是如何尽最大可能让系统恢复服务。
4.2.3 紧急事件响应
东西早晚要坏的,这就是生活。
4.2.3.1 当系统出现问题时怎么办?
- 测试导致的紧急事故:停止测试,恢复数据
- 变更部署带来的紧急事故:回滚
- 流程导致的严重事故:终止流程
所有系统不但一定会出问题,而且会以没有人能想到的方式出现问题。Google 从这些中学到的最关键的一课是,所有的问题都会有对应的解决方案。
4.2.3.2 向过去学习,而不是重复它
为事故保留记录,历史就是学习其他人曾经犯过的错。
4.2.4 紧急事故的管理
管理要素:
- 嵌套式职责分离:事件总控、事件处理团队、发言人、规划负责人
- 通知中心:受到事故影响的部门或者负责人需要实时跟事故总控负责人联系
- 实时事故状态文档:确保关联的每个人知道事故的进展
- 明确公开的职责交接文档:确保后续处理的人能够最快投入处理
4.2.4.1 认错
害怕犯错是人类的本性,如果不小心犯了错,那么请一定记得要认错。
先宣布事故的发生,随后找到一个简单解决方案,然后宣布事故结束,要比事故已经在持续了几个小时以后才想起来流程管控更好。如果事故满足以下任何一条标准,建议及时宣布:
- 是否需要引入第二个团队来帮助处理问题?
- 这次事故是否正在影响到最终用户?
- 在集中分析了一个小时候,这个问题是否依然没有得到解决?
4.3 事后总结和问题根源分析
4.3.1 事后总结:从失败中学习
Google 事后总结的哲学:协作和知识共享,建立事后总结文化。
事后总结的主要目的是为了保证事件被记录下来,理清所有的根源性问题,同时关键的是,确保实施有效的措施使得未来重现的几率和影响得到降低,甚至避免重现。
「对事不对人」的事后总结不应该简单地指责或者抱怨某个团队,而应该确实提出服务如何能够获得进步。
4.3.2 跟踪故障
提高可靠性的唯一方法论是建立一个基线(baseline),同时不断跟踪改变,Google 使用 Outalator——一个故障跟踪分析工具来做这件事情。
该系统可以获得如下信息:
- 每次 on-call 轮值发生的报警次数是多少
- 上个季度中可操作的报警和不可执行的报警的比例是多少
- 哪个消耗的人工最多等
能够对历史数据进行定量的分析,找出可优化的方向,这些是将来可改进的基本点 。
4.4 测试
4.4.1 测试可靠性
如果你还没有亲自试过某件东西,那么就假设它是坏的。
测试,是一个用来证明变更前后系统的某些领域相等性的手段,其目的是维持系统的稳定性。软件测试的类型:
- 传统测试:单元测试、集成测试、系统测试(包括冒烟测试、性能测试、回归测试)。
- 生产测试:配置测试、压力测试、金丝雀测试等。
4.4.1.1 创造一个构建和测试环境
测试重点集中在「用最小的力气得到最大收益的地方」。所以在测试一个系统的时候,请从以下方面开始思考:
- 能否将源代码按照重要程度区分出优先级
- 是否某些函数或者类是非常关键的,或者对业务运营极为重要
- 哪些 API 是其他团队需要集成使用的
4.4.1.2 大规模测试
- 测试大规模测试使用的工具(确保你的工具是正确的
- 针对灾难的测试
- 确保测试的准确率
- 生产环境和测试环境分离(分离会带来不一致的问题,导致你没有办法在测试环境稳定重新问题,但是危险总是不可能完全消除的,你需要做取舍
- 允许测试失败(缩短反馈周期
- 集成(针对配置文件集成测试也很重要
- 生产环境探针(监控和测试并不能完美的刻画出一个正在运行的实时变化的系统,所以你需要探针,总觉得跟古代试毒的操作很像……
4.5 容量规划
4.5.1 SRE 部门中的软件工程实践
SRE 开发的工具也是一个完整的软件工程项目,它打破了团队大小与用户服务规模成比例增长的关系。
4.5.1.1 Auxon 工具的实践
Auxon 是 SRE 内部开发的一个自动化容量规划工具。因为传统的容量规划方法,不可靠、耗时巨大,同时不够精确。所以提出了设计并开发了基于意图的自动化容量规划工具。自动化所带来的附加价值是,能够更快速的应对容量规划的变更。「意图是服务服务责任人对如何运维该服务的理性表达。」
基于意图的容量规划:列出你的要求,而不要拘泥于具体的实现细节。
用户不需要关系存储的数据在哪个区域、哪个机房、哪个机柜,哪个磁盘上,他需要你确保的是存储的数据一定不会丢失。同理,能够提供满足用户意图的自动化容量规划工具也应该被设计的如此。
A:我需要 50 个 CPU 的资源,必须在集群 X、Y、Z 中,为服务 Foo 使用
B:我想要满足 Foo 在每个地理区域的需求增长,同时保障 N+2 的冗余度
很明显 B 的意图更加清晰,表达产品意图的先决条件是:
4.5.1.2 推动你的工具
- 持续的和完整的推广方案
- 用户的拥护
- 资深工程和管理层的支持,前提是你的项目是有实力潜力的
不要过于关注完美和解决方案的纯粹性,尤其是当解决问题的边界不够清晰的时候。我们应该更快速的发布和迭代。
4.5.2 前端服务器的负载均衡
4.5.2.1 有的时候硬件并不能解决问题
用户流量负载均衡系统是用来决定数据中心中的这些机器中的哪一个用来处理用户的某个请求的。理想情况下,用户流量应该最优地分布于多条网络链路上、多个数据中心中,以及多台服务器上。
问题是:如何理解最优的分布?
- 逻辑层(是在全局还是在局部)
- 技术层(硬件还是软件)
- 用户流量的天然属性
以简单的搜索请求和视频上传场景来考虑最优问题。搜索系统用户最关心的是「延迟」而视频上传系统用户关心的则是「吞吐」。所以在最优化负载的时候:
- 搜索请求将会被发往最近的、可用的数据中心。判断条件是包的 RTT。
- 视频上传流则会选择——目前带宽没有占满的链路,来达到最大化网络吞吐量。
4.5.2.2 负载均衡
- 基于 DNS 进行负载均衡
- 基于 VIP 进行负载均衡(虚拟 VIP 不是绑定在某一个特定的网络接口上的,它是由很多设备共享的
4.5.3 数据中心内部的负载均衡策略
包括将请求路由给某个具体服务器的应用级别策略。
4.5.3.1 健康管理
- 如果某个后端任务过载了,请求处理开始变慢,负载均衡应该能自动避开这个后端,从而将任务分配给其他的后端。
- 需要正确识别跛脚鸭状态——后端服务正在监听端口,并且可以服务请求,但是已经明确要求客户端停止发送请求。
4.5.3.2 划分集群
- 鸡蛋不要放在一个篮子里一直都很好的建议。
- 在后端任务滚动重启的操作,需要对客户端透明,同时连接的变动最少(一致性 hash 是个好方案。)
4.5.3.3 负载均衡策略
简单轮询算法、最闲轮询策略、加权轮询策略等。
4.5.4 应对过载
避免过载,是负载均衡策略的一个重要目标。构建能良好处理资源限制的客户端和对应的后端任务是最好的:在可能的情况下重定向请求,在必要时返回降级回复,同时在最差的情况下,能够妥善地处理资源受限而导致的错误。
- 以可用资源来衡量可用容量
- 给每个用户设置限制:当全局过载情况真的发生时,使服务只针对某些「异常」客户返回错误是非常关键的,这样其他用户则不受影响。
- 客户端侧节流:当某个客户端检测到最近的请求错误中大部分都是由于「配额不足」错误导致时,该客户端可以开始自行限制请求速度。
- 基于重要程度给请求打标签:最重要的、重要、可丢弃的
- 资源利用率信号:随着资源利用率的上升,可以根据请求的重要性来拒绝一些请求(最重要的请求对应最高的阈值)
- 处理过载:区分是全局过载还是局部过载,局部过载重试则有一定概率会成功,全局过载重试则会加重过载问题。
- 连接造成的负载:定期检查连接资源(批量代理任务,随机断连接
4.5.5 处理连锁故障
「为什么人们总是忘记增加一点点抖动因素呢?」 —— Ade Oshineye,Google 开发者布道师
连锁故障是由于正反馈循环导致的不断扩大规模的故障。比如,服务的一个实例由于过载而出现故障,导致其他实例负载升高,从而导致这些实例像多米诺骨牌一样一个一个全部出现故障。
4.5.5.1 防止软件服务器过载
- 队列管理:队列 + 线程池的方案(队列中排队的个数建议小于线程数
- 流量抛弃和优雅降级:学会避免处理那些不值得处理的请求
- 重试:基于指数型 + 抖动因子
- 请求延迟和超时时间:任何时候设置超时是个好习惯,google GRPC 框架的第一个参数是 context ,应该是因为这个
4.5.5.2 连锁故障的触发条件
- 进程崩溃、进程更新、新的发布、自然增长、计划中或计划外的不可用
4.5.5.3 解决连锁故障的步骤
- 增加资源、停止健康检查导致的任务死亡、重启软件服务器、丢弃流量、进入降级模式、消除批处理负载、消除有害的流量。
4.6 软件研发
4.6.1 管理关键状态:利用分布式共识来提高可靠性
分布式共识系统主要解决:在不稳定的通信环境下一组进程之间对某项事情达成一致的问题。
4.6.1.1 使用共识算法的动力
分布式系统系统协调失败,产生脑裂问题或者需要人工干预灾备切换。
4.6.1.2 分布式共识的系统架构模式
分布式共识算法很底层,很原始,它们仅仅可以让一组节点一次共同接收一个值。
- 可靠的复制状态机(RSM):复制状态机是实现在共识算法逻辑层之上的一个系统。
- 可靠的配置数据存储和配置存储:复制数据存储是复制状态机的一个应用。
- 使用领头人选举机制实现高可用的处理系统:复制多份系统并使用一个唯一的领头人来进行某种类型的工作是很常见的设计。唯一的领头人是一种保证粗粒度互斥的方法。
- 分布式协调和锁服务:屏障(barrier)在分布式计算机中是一种原语,可以用来阻挡一组进程继续工作,,直到某种条件被满足。
- 可靠的分布式队列和消息传递
4.6.1.3 分布式共识系统的性能问题
世界上没有某个性能「最优」的分布式共识和状态机复制算法,因为算法的性能通常取决于系统负载有关的多个因子,以及系统性能的目标和系统的部署情况。
- 应对大量的读操作:复制数据存储的优势在于数据同时在多个地点可用,如果不是所有的读请求都需要强一致,数据就可以从任意一个副本来读取。
- 分布式共识系统的性能与网络延迟:
- 共识系统在局域网中的性能和主从模式类似。 然而,分布式共识系统通常需要副本运行在「较远」距离上,这样可以保障副本处于多个不同的故障域中
4.6.1.4 分布式共识系统的部署
大体量公司的烦恼也很多啊,如果只有一个机房的话,这些问题对于你来说根本就不存在……
4.6.2 分布式周期性任务系统
问题:Cron 系统不可能知道要执行任务是否具有幂等性?比如垃圾回收操作或者批量发送 E-mil 通知的操作。系统设计的实现需要在跳过这次任务的执行和执行两次之间做取舍。
答案:一般情况下都会选择跳过这次任务的执行。
分布式 Cron 系统的核心是 Paxos 协议,一个在分布式不可靠系统达成一致的算法。
4.6.3 数据处理流水线
周期性的数据流水线是很有价值的,但是如果一个数据处理问题本身是持续性的,或者会自然增长为持续性的,那么就不要采用周期性的设计方式,而是采用一种类似 Workflow 的设计特点的系统。这样一个系统能够周期性地提供用户所依赖的结果,并且是一个非常可靠且稳定的可运维系统。
Workflow 通过将工作进程进一步划分为更小的任务组,而将流水线的「深度」随意增加。每个任务组负责处理该阶段执行的数据,可以对谋一小块数据进行任意操作(比如 Mapping、Shuffing、排序、分割以及合并等操作。
本质理解的不是很好,后面有空需要重新回顾下。
4.6.4 数据完整性:读写一致性
数据完整性:是指数据存储为了提供一个合理的服务质量,在可访问性和准确性方面必须达到的一个度量标准。
保障超高数据完整性的手段是主动探测和快速修复的能力。没有人真的想要备份数据,他们只是想恢复数据。所以交付一个恢复系统,而非备份系统。
4.6.4.1 造成数据丢失的事故类型
根源问题:用户行为、管理员的错误、应用程序的 Bug、基础设施中的问题、硬件故障和部署区的大型事故。
影响范围:全部用户 or 局部用户
发生速度:瞬间 or 缓慢持续进行
4.6.4.2 Google SRE 保障数据完整性的手段
SRE 假设任何一种数据保护机制都可能在最不合适的时间出现问题,同时没有一种银弹可以同时保护所有事故类型,所以需要分级进行。
- 用户删除:使用软删除策略,用户可以自主还原数据,预防用户错误。
- 应用程序删除数据:使用被备份策略,可以被用户支持团队或者应用程序管理员恢复,预防应用程序 Bug 和服务管理员错误。
- 数据被摧毁:使用备份策略,无法还原需要使用备份,备份可以为这个和其他所有场景提供保护。
Google 在 2012 年 3 月 Google Music 被意外删除事件中,使用基于磁带的备份,恢复了用户被误删除的数据。嗯,是真的。
完美的理论不代表实现也是完美的。
4.7 产品设计
4.7.1 可靠地进行产品的大规模发布
还记得前面说过的发布工程师嘛?嗯,我觉得除了像 Google 这么大体量的公司以外,很多公司应该都没有这个岗位吧…… ( ps 突然想起了全局架构组,感觉好像有点类似的样子
4.7.1.1 起草一个发布检查列表
- 架构与基础设施的依赖
- 容量规划:容量规划与冗余度和可用性都有直接关系
- 故障模式:是否是单点,依赖服务不可用如何处理
- 客户端行为:客户端的滥用行为是否会影响到服务的稳定性
- 流程与自动化:流程文档应该能够做到使任何一个团队成员都可以在紧急事故中处理问题
- 开发流程:代码的版本控制
- 外部依赖:是否依赖三方代码,是否有合作伙伴依赖你的服务,发布时是否周知他们等
- 发布计划
4.7.1.2 可靠发布所需要的方法论
- 灰度和阶段性发布:任何改动都具有一定的危险性,而任何危险性都应该被最小化,这样才能保障系统的可靠性。
- 功能开发框架:创建一个可控更新的机制允许我们在真实负载情况下观察系统的整体行为。用工程的力量和时间换取一些可靠性的保障是很划算的。
- 应对客户端滥用行为:服务器端控制客户端行为的能力是一个很重的功能。
- 过载行为和压力测试:你的服务在未来的使用中会遭受最坏的处境时,仍能够符合你的预期是很好的。