《SRE:Google运维解密》读后感 & 摘抄
这篇书评可能有关键情节透露
一、概述
也发布在:https://blog.laisky.com/p/srebook/
描述了谷歌 SRE 这一岗位的工作职责和工作实践。
和传统的运维(OPS)不同,谷歌非常重视运维人员的开发能力, 希望运维人员能够在处理日常工作之余,尽可能的开发自动化工具减少人力需求, 所以提供了新的 SRE 的岗位需求。 一位 SRE 工程师应该最多仅投入 50% 的精力到日常工作之中, 另有 50% 的精力从事自动化工具的开发,依靠尽可能少的 SRE 工程师, 提供最大的服务支撑能力。
此书中还有一个特别让我感触的地方在于, 作者用语非常平和,字里行间并没有谷歌身为世界第一流的科技公司的狂傲之情。 反而非常务实的,提出不要依赖工程师和管理人员的个人素质, 而应该尽可能的做好预案的演练,设置多层次的纵深防御和响应体系, 依靠制度与体系,构建高可用的服务架构。
二、摘抄
SRE 职责:可用性改进、延迟优化、性能优化、效率优化、变更管理、监控、紧急事务处理、容量规划与管理
SRE 的成员由纯开发和运维开发组成,从事开发工作的时间应该不小于 50%。这个需要由领导出面对工作进行协调,以保证这一比例。长远目标应该是消除一切运维工作。
peer bonus:允许每个人有一次机会给任何人发送奖金,以鼓励其杰出贡献
服务可靠性不是越高越好,而是要综合考量,计算“错误预算”:
- 基于用户习惯,达到多少用户才会满意
- 用户是否有其他替代选择
- 可靠性是否会影响用户的使用模式
MTTF:平均失败时间 MTTR:平均恢复时间
作为手册非常重要,值得花费大量时间进行维护。
数据中心拓扑:
- 机柜 rack
- 集群 cluster
- 数据中心 datacenter
- 园区 campus
谷歌采用 clos 连接的虚拟交换机 jupiter,实现 1.3 pb/s 的带宽
Borg 名称解析系统(BNS),类似于 marathon-lb,将域名及 path 解析为 ip 和端口
一个缓慢的不断重启的实例要好过一个永不重启一直泄露资源的容器。
采用 openflow 定义的 SDN,而不采用高价的智能路由
采用中心化的路由计算,BwE(bandwidth enforcer)负责分配带宽资源
使用 D 抽象底层磁盘,使用 Colossus 抽象覆盖整个集群的文件系统,然后在这个文件系统上构建数据库,包括:Spanner、Blobstore、Bigtable
分布式锁 chubby,功能类似于 zk,不过使用 paxos。
用户通常不会注意到一项服务在高可靠性和极端可靠性之间的差别,因为用户体验主要是受不可靠组件主导。
- 可用性 = 系统正常运行的时间 / (系统正常运行的时间+停机时间)
- 可用性 = 成功请求数 / 总得请求数
计算增加可用性目标后带来的成本增加和预期收益
研发团队关注研发速度,SRE 团队关注可用性。为了更好的找到平衡点而不是依靠办公室政治, 可以引入“错误预算”,产品管理层定好一个 SLO,然后根据监控数据来判定是否还有剩余,有则允许发布,快没了就减缓发布甚至停止发布。
服务质量评价:
- 服务质量指标 SLI(indicator):量化指标,包括延迟、吞吐量、错误率、可用性、持久性等
- 指标不宜过多,应关注用户的真实需求
- 常用的指标度量应该尽量标准化(如时间间隔、频率等)
- 服务质量目标 SLO(Objective):对特定 SLI 的目标值
- 服务质量协议 SLA(Aggrement):与用户间的明确协议,一般伴随着代价。
计划内停机:当某个服务在一定周期内超过了预期的 SLO 后,可以故意的令其停机,使其维持在略高于 SLO 的状态。这样可以强迫其他服务不要过于依赖该服务的稳定性。
大部分指标都应该以“分布”,而不是平均值来定义。
监控系统的 4 个黄金指标:延迟、流量、错误和饱和度
监控中的紧急警报要尽可能的少,一切不需要智力解决的问题都不应该成为紧急。
监控系统要尽可能的简单,“自动化”虽然可能减少工作量,但是会隐藏真正的问题,所以不要滥用。
自动化的系统可以提供一个可以扩展的、广泛适用的,甚至可能带来额外收益的平台。而且更容易发现和修复错误
一但全球计算机的增长超过了一定规模时,它必须是自我修复的,因为根据统计学来说,每秒它都会发生大量故障
自动化操作都应该具有幂等性,不可逆操作应进行速率控制
“软件膨胀”用来描述软件随着时间的推移不停地增加新功能而变得更慢和更大的趋势
- 删除不用的代码,而不是注释
- 在测试中增加代码膨胀检查
谷歌内部的每一个可执行文件中都默认包含一个我 HTTP 服务,提供标准得监控接口
(在内存中实现时间序列数据的存储,y轴是时间)
SRE 确保他们做的每一件事都能扩展到更大规模。
我们强调至少将 SRE 团队 50% 的时间花在软件工程上,在其余时间中,不超过 25% 的时间用来 on-call,另外 25% 的时间用来处理其他运维工作
过大的压力会导致人们倾向于采取直觉性的行动,而不是深思熟虑的理性行为。最理想的方法论是这样的:在有足够数据支撑的时候按步骤解决问题,同时不停地审视和验证目前所有的假设。
on-call 升级:
- 清晰的问题升级路线
- 清晰定义的应急事件处理步骤
- 无指责、对事不对人的文化氛围
环境过于稳定导致 SRE 压力不够也是一个需要注意和预防的问题。
值得警惕的是,理解一个系统应该如何工作并不能使人成为专家。只能靠调查系统为何不能正常工作才行。 —— Brain Redman
排查故障的技能需要:
- 对通用的故障排查过程的理解
- 对发生故障的系统的足够了解
故障排查过程:反复使用假设-排除法
不要过早的归因于小概率事件,“当你听到蹄子声响时,应该先想到马,而不是斑马”
在监控图表上,叠加上重要事件的持续时间:
在进行故障模拟测试前,应先测试回滚机制(幂等)
在事故处理中,让每个人清楚自己的职责是非常重要的。明晰职责反而能够使每个人可以更独立自主的解决问题,因为他们不用怀疑和担心他们的同事都在干什么。
基于 Incident Command System 的事故流程管理系统角色:
- 事故总控 incident command
- 组建团队、确认优先级、分配任务
- 事故处理团队 operational work
- 干活
- 发言人 communication
- 更新事故文档、发送通知
- 规划负责人 planning
- 后勤
对事不对人,避免指责,提供建设性意见。
谷歌内部使用 escalator 管理报警,使用 outalator 追踪 bug。需要的功能有:
- 发送报警,监听报警状态,若没有人响应则自动升级
- 聚合:能够将数个报警归类为同一次故障
- 标签:提供人工分类的功能
- 历史统计和操作回顾
- 还可以允许手动创建报警
测试 :
- 单元测试
- 集成测试(主要依赖 mock)
- 系统测试(联调)
- 冒烟测试
- 性能测试
- 回归测试
- 生产测试
- 黑盒测试
- 金丝雀测试
团队大小不应该与用户服务规模成比例增长
需求不明确或技术方案不明确时,其实反而更助于模块化的开发。因为模块化使得未来可能的替换更为简单。
MVP 非常重要
建立可信度的方式是在合理的时间内交付一些实用价值。
返回最优解析 IP 的问题:
- 可以请求一个 anycast 的 DNS 请求,到达的最近的 DNS 返回附近的服务器 IP
- 但是 DNS 往往会经过数层的递归解析,导致权威 DNS 难以判断用户的位置。使用 ENDS0 协议可以携带用户的子网段
负载均衡的实现方式:
- 一致性哈希的连接保持
- 直接服务器响应(DSR):LB 修改入流包的目标 mac,这样服务端可以不通过 lb 直接响应客户端。但是这样要求后端和 lb 属于同一个广播域
- 包封装:通过通用路由封装协议(GRE),将原请求封装为新的 IP 包。但这会导致包体积变大,可能超过 MTU 而导致碎片(fragmentation),所以数据中心内部一般采用更大的 MTU
跛脚鸭状态(优雅退出):当一个服务仍在工作,但因为负载等原因决定不再接受更多的请求时,称之为跛脚鸭状态。此时应广播给所有的客户端,不要再继续发送请求。不活跃的客户端,也应该定期发送 UDP 健康检查包。
当连接闲置时间过长时,RPC 框架应将其转换为 UDP。
为服务端计算一个子集,然后发送给客户端,作为可以链接的服务端列表。每个(或每一轮)客户端都会拿到完全不同的服务端子集,当第一个服务端无法连接时,就会连接第二个,以此类推。因为每个客户端拿到的列表都不一样,就实现了很好的客户端负载均衡。
round robin 的缺点在于:不同请求间的成本不同。所以数量上的均衡不代表负载上的均衡。
对异构资源做管理时,可以引入虚拟资源单位。比如用某个量化定义的虚拟 CPU 来计算物理 CPU 的数量。
地漏效应(sinkhole effect):针对 lb 中的最闲轮询,如果一个服务目前不健康,错误返回的速度可能会非常快,导致 lb 认为其负载更低反而给其更多的流量。
加权轮询可以缩小最大负载机器和最小负载机器间的差距。
按照 QPS 或者其他某种静态属性来规划容量一般是错误的选择(早晚会发生变化的)。大部分时候简单的使用 CPU 来配置资源就足够好了,因为其他资源的短缺很大程度上也会反应在 CPU 上。
协作式分布式客户端节流系统 doorman
可以为请求增加属性——重要性。后端按级别来实施降级。
- 最重要 CRITICAL_PLUS:拒绝这些请求会有严重后果
- CRITICAL:会造成用户可见问题
- SHEDDABLE_PLUS:可重试的请求
- SHEDDABLE:无关紧要
基于后端的资源利用率的指数衰变算法(exponential decay)平滑后,来决定是否要拒绝服务。
在客户端中使用针对服务器的全局重试计数,防止对已过载服务器发起过多重试。
链式服务调用,因为服务栈非常深,所以应该在每一层进行重试,一旦重试失败,则返回客户端“已过载,勿重试”。
连锁故障(cascade)是由于正反馈循环导致的不断扩大规模的故障。
流量抛弃(load shedding):
- 接近过载时,将 FIFO 队列改为 LIFO(因为用户会刷新!)
- 使用可控延迟算法
优雅降级(graceful degradation)降低回复质量来大幅减少所需的计算量或所需的计算时间。
请求延迟的双峰分布(bimodal):个别请求永不结束,导致这些请求逐渐的占满后端的整个队列。由于其他请求都很快,所以整体延迟只是略微上升。
后端服务间调用栈较复杂时,尽可能的保持调用栈永远向下(单向垂直调用,而不是水平),避免分布式死锁。(就像管理模块调用关系一样管理服务调用)
压测时,应该一直测到服务崩溃为止。一个合格的服务,应该在过载时针对多余的负载返回错误或者降级,而不应该降低它成功处理的速率。同时也应该测试一个服务从过载情况下如何恢复。
应该清晰的区分两种健康检查:
- 进程级:是否响应
- 服务级:是否能够回应特定请求
CAP:一致性、可用性、分区,三选二。
ACID:原子性、一致性、隔离性、持久性
BASE:
- basically available
- soft state
- eventual consistentency
最终一致性可能会带来很多问题:
- 时钟漂移和网络分区(分区间失联)时
- 应用层代码需要考虑数据过期等
数据一致性的问题应该在数据层面解决
分布式共识可以区分为:
- 异步分布式共识 asynchronous distributed consensus:消息传递可能会无限延迟的环境下
- 同步分布式共识
也可以按照节点类型分为:
- 崩溃可恢复的 crash-recover
- 崩溃不可恢复的 crash-fail
还可以区分为:
- 一般分布式共识:节点间网络不可靠
- 拜占庭共识:节点也不可靠,可能会故意返回错误的响应
在不稳定的网络下,没有任何一种异步分布式共识算法可以保证一定能够达成共识。 —— Dijkstra
常见分布式共识算法有:paxos、raft、zab、mencius
分布式共识算法用于确保一组节点按同样的顺序一次共同接受一个值。
大多数分布式共识算法的一个问题在于,每个节点都没有数据的完整视图。
复制状态机(replicated state machine,RSM)是一个能在多个进程中用同样的顺序执行同样的一组操作的系统。RSM 的实现依赖于分布式共识提供的有序共识。
分布式系统的 leader 选举跟分布式共识是等价问题。唯一的领头人是一种保证粗粒度锁的方法。
分布式屏障(barrier):用于阻挡一组进程继续工作,直到某种条件被满足。
分布式锁:应该尽可能使用可续租约(renewable lease),而不是无限时间锁
原子性广播(和分布式共识是等价问题):整个系统的参与者都可以可靠地接受到消息,并且以同样的顺序来处理这些消息。
分布式队列:需要考虑消息是否真正的被处理了,所以最好也使用某种租约(如 kafka 的 comkit 机制)。还需要注意,队列容易成为整个系统的瓶颈和薄弱点。
复合式 paxos,采用 strong leader,只需要对满足法定人数的进程发送一次消息就可以保障系统达成共识。但是要注意“提议者决斗”问题。
法定租约(qurom lease):在分布式共识中,用于降低延迟和提高读操作吞吐。
一组 2f+1 的节点可以容忍 f 个节点故障。若需要承受节点返回错误响应(拜占庭式失败),则需要 3f+1 个节点才能忍受 f 个失败。
惊群效应(thundering herd):比如大量定时任务在一个时间点启动,造成机器超载
对大数据进行周期性的或者是持续性的变形操作程序通常被称为简单流水线(simple,one-phase pipeline)。串联后一般称为多相流水线(multiphase pipeline)。一个流水线中串联的程序数量多少称为流水线的深度(depth)。
大数据处理的 embarrassingly parallel 算法:将巨大的工作集切割为一个个可以装载在单独的物理机器上的小块。
摩尔负载模式(moire load pattern):指两个或者更多的流水线任务同时运行时,某些执行过程重叠,导致它们同时消耗某个共享资源。
谷歌的研究表明:最常见的用户可见数据丢失场景是由于数据删除和软件 bug 造成的引用完整性问题。
数据删除逻辑的 bug 非常常见,以至于快速复原一定时间内的删除操作是针对永久性数据丢失的第一道防线。
- 软删除:业务代码不要真的删除数据
- 懒删除:由一些离线脚本延期删除超过一定期限的已被数据。
为了避免用户可见的数据质量下降,以及在无法恢复之前检测到低级的数据损坏以及数据丢失,我们需要一整套带外(out-of-band)检查和修复系统来处理数据存储内部和相互之间的数据问题。
一个有效的带外数据校验系统需要下列元素:
- 校验任务的管理
- 监控、报警和监控台任务
- 限速功能
- 调试排错工具
- 生产应急应对手册
- 校验器容易使用的数据校验 API
纵深防御:多个保障手段彼此覆盖,能够用合理的成本来覆盖非常广泛的失败场景。
发布协调工程师(Launch Coordination Engineering, LCE)
LCE 会维护一份交给项目发布方的检查清单。
一个好的发布流程应具备:
- 轻量级
- 鲁棒性
- 完整性
- 可扩展性
- 适应性
(错误认知)坏苹果理论:认为整个系统大体都是好的,只要摘除其中的坏苹果,系统就将会一直运行良好。但实际上,在任何一个交互关系错综复杂的系统中,错误是不可避免的。
会议最好都要有一个主席。当两个体量差距很大的团队开会时,建议从较小的团队挑选主席。
当业务团队准备发布服务时,SRE 团队会介入进行生产就绪程度评审(PRR)。按照 SRE 可以接受的方式对服务进行改造和优化后,才会交付给 SRE 进行生产运维。
并不是所有线上服务都需要 SRE 接管,一开始可以由开发自行负责。等负载较高时,再移交给 SRE。
PRR 的问题在于 SRE 介入于产品晚期,修改成本和阻力较大。所以可以考虑在项目早期就让 SRE 参与。
隐形发布:利用流量拷贝,而不让新服务对用户可见。
在任何事故和业务灾难发生之前,类似事故都曾发生过好几次,只是没有造成任何后果。这些事故在发生的时候都被忽略了。潜伏的错误,加上某个适合的时机,就会导致事故的发生。
结构化和理性的决策:
- 某项决策的基本方向是事先决定的,而不是事后得出的
- 决策时考虑的信息源是清楚的
- 任何假设都应该明确说明
- 数据驱动