Google SRE 运维解密笔记

本书对于 SRE 的定义

我们认为如果软件工程师主要专注于设计和构建软件系统,那么应该有另外一种职业专注于整个软件系统的生命周期管理。从其设计一直到部署,历经不断改进,最后顺利退役。这样一种职业必须具备非常广泛的技能,但是和其他职业的专注点都不同。Google 将这个职业称为站点可靠性工程师(SRE: Site Reliability Engineering)。

“无论对一个软件系统运行原理掌握得多么彻底,也不能阻止人犯意外错误。”

只有靠着对细节的不懈关注,做好充足的灾难预案和准备工作,时刻警惕着,不放过一切机会去避免灾难发生。这就是 SRE 最重要的理念!

第一章:介绍

不能将碰运气当成战略。 - SRE 俗语

SRE 团队要承担以下几类职责

  • 可用性改进
  • 延迟优化
  • 性能优化
  • 效率优化
  • 变更管理
  • 监控
  • 紧急事务处理
  • 容量规划与管理

在保障服务 SLO 的前提下最大化迭代速度

在企业中,最主要的矛盾就是迭代创新的速度与产品稳定程度之间的矛盾。 在 SRE 模型中,我们选择正面面对这种矛盾,使用的工具是错误预算

“错误预算”起源于这样一个理念: 任何产品都不是,也不应该做到 100%可靠(显然这并不适用于心脏起博器和防抱死系统等)。因为对最终用户来说,99.999%和 100%的可用性是没有实质区别的。

多少才是?

  • 基于用户的使用习惯,服务可靠性要达到什么程度用户才会满意?
  • 如果这项服务的可靠程度不够,用户是否有其他的替代选择?
  • 服务的可靠程度是否会影响用户对这项服务的使用模式?

如果一个服务的可靠性目标是 99.99%,那么错误预算就是 0.01%。这意味着产品研发部门和 SRE 部门可以在这个范围内将预算用于新功能上线或产品创新等任何事情。

监控系统

监控系统是 SRE 团队监控服务质量和可用性的一个主要手段。

一个需要人工阅读邮件和分析警报来决定目前是否需要采取某种行动的系统本质上就是错误的。监控系统不应该依赖人来分析警报信息,而是应该由系统自动分析,仅当需要用户执行某种操作时,才需要通知用户。

一个监控系统应该只有三类输出

  • 紧急警报 - alert > 意味着收到警报的用户需要立即执行某种操作,目标是解决某种已经发生的问题,或者是避免即将发生的问题。
  • 工单 - ticket > 意味着接受工单的用户应该执行某种操作,但是并非立即执行。系统并不能自动解决目前的情况,但是如果一个用户在几天内执行这项操作,系统不会受到任何影响。
  • 日志 - logging > 平时没有人需要关注日志信息,但是日志信息依然被收集起来以备调试和事后分析时使用。正确的做法是平时没人会去主动阅读日志,除非有特殊需要。

变更管理

大概 70%的生产事故由某种部署的变更而触发。变更管理的最佳实践是使用自动化来完成以下几个项目

  • 采用渐进式发布机制
  • 迅速而准确地检测到问题的发生
  • 当出现问题时,安全迅速地回退改动

需求预测和容量规划

自然增长 - 随着用户使用量上升,资源用量也上升。 非自然增长 - 新功能的发布、商业推广以及其他商业因素在内。

容量规划有几个步骤是必需的

  • 必须有一个准确的自然增长需求预测模型,需求预测的时间应该超过资源获取的时间
  • 规划中必须有准确的非自然增长的需求来源的统计
  • 必须有周期性压力测试,以便准确地将系统原始资源信息与业务容量对应起来

第二章:Google 生产环境: SRE 视角

为了区分物理服务器和软件服务器的概念,本书采用以下两种说法

  • 物理服务器 - machine > 代表具体的硬件(有时候也代表一个 VM 虚拟机)
  • 软件服务器 - server > 代表一个对外提供服务的软件系统

一个典型的 Google 数据中心的拓扑结构

  • 约 10 台物理服务器组成一个机柜 - Rack
  • 数台机柜组成一个机柜排 - Row
  • 一排或多排机柜组成了一个集群 - Cluster
  • 一般来说,一个数据中心 - Datacenter包含多个集群
  • 多个相邻的数据中心组成了一个园区 - Campus

存储

  • Lustre
  • HDFS

网络

带宽控制器(Bandwidth Enforcer, BwE)负责管理所有可用带宽。优化带宽的使用的目的不仅仅是降低成本。利用中心化的路由计算,可以解决以前在分布式路由模式下难以解决的流量迁移问题。

研发环境

除了一些开源项目之外(Android 和 Chrome 等),其他 Google 软件工程师全部使用同一个共享软件仓库开发。这同时也对日常工作流带来一些挑战

  • 如果一个工程师遇到了他工作的项目之外的一个基础组件问题,他可以直接修改这个问题,向管理者提交一份改动申请(changelist, CL),等待代码评审,最后直接提交。
  • 任何对自己项目代码的改动也需要代码评审。

任务和数据的组织方式

假设压力测试的结果现实,我们的软件服务器可以每秒处理大概 100 个请求(100 QPS)。通过对用户进行的调查显示,我们预计峰值流量会达到 3470 QPS,为了处理这些流量,至少需要 35 个任务实例。但是,由于以下几点考量,我们最终决定至少采用 37 个实例,也就是N+2模式

  • 在更新过程中,有一个任务实例将会短暂不可用,只有 36 个实例可提供服务。
  • 如果另外一个物理服务器同时也出现问题,那么另外一个任务实例也受到影响,只剩 35 个实例可以对外服务,刚好可以满足峰值要求。

第三章:拥抱风险

过分追求稳定性限制了新功能的开发速度和将产品交付给用户的速度,并且很大程度地增加了成本,这反过来又减少了一个团队可以提供的新功能数量。 此外,用户通常不会注意到一项服务在高可靠性和极端可靠性直接的差异,因为用户体验主要是受较不可靠的组件主导,例如手机移动网络或他们正在使用的设备。用户在一个有着 99%可靠性的智能手机上是不能分辨出 99.99%和 99.999%的服务可靠性的区别的!

SRE 旨在寻求快速创新和高效的服务运营业务之间的风险的平衡,而不是简单地将服务在线时间最大化。这样一来,我们可以优化用户的整体幸福感,平衡系统的功能、服务和性能

管理风险

可靠性进一步提升的成本并不是线性增加的 - 可靠性的下一个改进可能比之前的改进成本增加 100 倍。 高昂的成本主要存在与

  • 冗余物理服务器/计算资源成本 > 通过投入冗余设备,我们可以进行常规的系统离线或其他预料之外的维护性操作。又或者可以利用一些空间来存储奇偶校验码块,以此来提供一定程度的数据持久性保证。
  • 机会成本 > 这类成本由某一个组织承担。当该组织分配工程资源来构建减少风险的系统或功能,而非那些用户直接可用的功能时需要承担这些成本。这些工程师不能再从事为终端用户设计新功能和新产品的工作。

我们会努力提高一项服务的可靠性,但不会超过该服务需要的可靠性。当设定了一个可用性目标为 99.99%时,我们即使要超过这个目标,也不会超过太多,否则会浪费为系统增加新功能、清理技术债务或降低运营成本的机会。

度量服务的风险

为了使问题在我们运行的各种类型的系统中易于处理,并且保持一致,我们选择主要关注计划外停机这个指标。

计划外停机时间是由服务预期的可用性水平所体现,通常我们愿意用提供的”9”系列的数字来体现,比如可用性为99.9%/99.99%或99.999%。每个额外的”9”都对应一个向100%可用性的数量级上提高。对于系统服务而言,这个指标通常是基于系统正常运行时间比例的计算得出的。

公式 3-1: 基于时间的可用性

可用性=系统正常运行时间/(系统正常运行时间+停机时间)

公式 3-2: 合计可用性

可用性=成功请求数/总的请求数

例如,一个每天可用性目标为 99.99%的系统,一天要接受 2.5M 个请求。它每天出现少于 250 个错误即可达到预计的可用性目标。

服务的风险容忍度

辨别消费者服务的风险容忍度

消费者服务通常会有一个对应的产品团队,是该服务的商业所有者。比如说,Search、Google Maps 和 Google Docs,它们每一个都有自己的产品经理。这产品经理负责了解用户和业务,在市场上塑造产品的定位。存在产品团队时,我们能够更好地通过这个团队来讨论服务的可靠性要求。在没有专门的产品团队情况下,建立系统的工程师们经常在知情或不知情的情况下扮演了这个角色。 评价服务的风险容忍度时,有许多需要考虑的因素。

  • 需要的可用性水平是什么?
  • 不同类型的失败对服务有不同的影响吗?
  • 我们如何使用服务成本来帮助风险曲线上定位这个服务?
  • 有哪些其他重要的服务指标需要考虑?
可用性目标

对于某个 Google 服务而言,服务的可用性目标通常取决于它提供的功能,以及这项服务在市场上是如何定位的。下面列出了要考虑的一些问题

  • 用户期望的服务水平是什么?
  • 这项服务是否直接关系收入(我们的收入或我们的客户的收入)?
  • 这是一个有偿服务,还是免费服务?
  • 如果市场上有竞争对手,那些竞争对手提供的服务水平如何?
  • 这项服务是针对消费者还是企业的?

例如Google Apps for Work,这个服务的主要用户是企业类用户,包括大型企业和中小型企业。服务的中断不仅会影响 Google 本身,也会影响到这些企业。对于这类服务,我们可能会设置季度性的外部可用性目标为 99.9%。同时,我们会设置一个更高的内部可用性目标,以及签署一份如果我们未能达到外部目标的处罚性协议。 Youtube 则需要截然不同的考虑。尽管当时 Youtube 已经有了一个很出色的产品,但它仍然在不断变化和快速发展着。因此,我们为 Youtube 设定了相比我们企业的产品更低的可用性目标,因为快速发展更加重要。

故障的类型

假设有一个联系人管理应用程序,一种情况是导致用户头像显示失败的间歇性故障,另一种情况是将 A 用户的私人联系人列表显示给 B 用户的故障。第一种情况显然是一个糟糕的用户体验,SRE 会努力去快速地解决这个问题。然而,在第二种情况下,暴露私人数据的风险可能会破坏基本的用户信任。因此,在第二种情况下,在进行调试和事后的数据清理时,完全停止该服务更加恰当。 有时候,我们可以接受计划内的常规服务中断。Ads 前端曾经就是这样的一种服务。因为这项工作大部分发生在正常工作时间内,我们认为维修窗口中发生的偶然的、正常的、计划之中的故障是可以接受的,并且我们把这些故障看作计划内停机(不影响 SLO?),而不是计划外停机时间。

成本

在为每一项服务确定可用性目标时,可以考虑如下问题

  • 构建和运维可用性再多一个”9”的系统,收益会增加多少?
  • 额外的收入是否能抵消为了达到这一可靠性水平所付出的成本?

可靠性目标: 99.9% -> 99.99%

增加的可用性: 0.09%

服务收入: 100 万美元

改进可用性后的价值: 100 万美元 x 0.0009 = 900 美元

在这种情况下,如果可用性提高一个”9”的成本不到 900 美元,那就是合理的投资。如果成本超过 900 美元,那么成本将超过预计增加的收入。

错误预算的构建过程

  • 产品管理层定义一个 SLO,确定一项服务在每个季度的正常运行时间。
  • 实际在线时间是通过一个中立的第三方来测算的: 我们的监控系统。
  • 这两个数字的差值就是这个季度中剩余的不可靠性预算。
  • 只要测算出的正常在线时间高于 SLO,也就是说,只要仍然有剩余的错误预算,就可以发布新版本。

本章关键点

  • 管理服务的可靠性主要在与管理风险,而且管理风险的成本可能很高。
  • 100%可能永远都不是一个正确的可靠性目标: 不仅是不可能实现的,而且它通常比一项服务的用户期望的可靠性大得多。我们要将服务风险和愿意承担的业务风险相匹配。
  • 错误预算在 SRE 和产品研发团队之间调整激励,同时强调共同责任。错误预算使得讨论发布速率更容易,同时可有效地减少任何关于事故的讨论。这样,多个团队可以毫无怨言地对生产环境风险度达成一致。

第四章: 服务质量目标

这个过程中,我们需要利用一些主观判断结合过去的经验对服务的理解来定义一些服务质量指标(SLI)服务质量目标(SLO),以及服务质量协议(SLA)。这三项分别是指该服务最重要的一些基础指标、这些指标的预期值,以及当指标不符合预期时的应对计划。

服务质量术语

指标(Indicator)

SLI 是指服务质量指标 - 该服务的某项服务质量的一个具体量化指标。 大部分服务都将请求延迟(处理请求所消耗的时间)作为一个关键 SLI。其他常见的 SLI 包括错误率(请求处理失败的百分比)、系统吞吐量(每秒请求数量)等。这些度量通常是汇总过的: 在某一个度量时间范围内将原始数据收集起来,计算速率、平均值、百分比等汇总数据。 可用性(availability)是另外一个重要的 SLI,代表服务可用时间的百分比。对于数据存储系统来说,持久性(durability) - 数据能够完整保存的时间也是一个重要指标。

目标(Objective)

SLO 是服务质量目标 - 服务的某个 SLI 的目标值,或者目标范围。 SLO 的定义是 SLI ≤ 目标值,或者范围下限 ≤ SLI ≤ 范围上限

协议(Agreement)

SLA 是服务质量协议 - 指服务与用户之间的一个明确的,或者不明确的协议,描述了在达到或者没有达到 SLO 之后的后果。 区别 SLO 和 SLA 的一个简单的方法就是问”如果 SLO 没有达到目标,有什么后果?” 如果没有定义明确的后果,那么我们就肯定是在讨论一个 SLO,而不是 SLA。

指标在实践中的应用

运维人员和最终用户各关心什么

我们不应该将监控系统中的所有指标都定义 SLI;只有理解用户对系统的真实需求才能真正决定哪些指标是否有用。一般来说四五个具有代表性的指标对系统健康程度的评估和关注就足够了。 常见的服务,根据它们的相关 SLI 通常会归类为以下几个大类。

  • 用户可见的服务系统,例如莎士比亚搜索服务的前端服务器通常关系可用性、延迟,以及吞吐量。换句话说: 是否能正常处理请求?每个请求花费的时间是多少?多少请求可以被处理?
  • 存储系统通常强调: 延迟、可用性和数据持久性。换句话说: 读写数据需要多少时间?我们是否可以随时访问数据?数据是否一段时间内还能被读取?
  • 大数据系统,例如数据处理流水线系统,一般来说关系吞吐量和端到端延迟。换句话说: 处理了多少数据?数据从输入到产出需要多少时间?(某些流水线任务还会关注某个单独处理阶段的延迟)
  • 所有的系统都应该关注: 正确性。是否返回了正确的回复,是否读取了正确的数据,或者进行了正确的数据分析操作。正确性是系统健康程度的一个重要指标,但是它更关注系统内部的数据,而不是系统本身,所以这通常不是 SRE 直接负责的。

目标在实践中的应用

我们应该从思考(或者调研)用户最关心的方面入手,而非从现在能度量什么入手。用户真正关心的部分经常是度量起来很困难,甚至是不可能的,所以我们需要以某种形式近似。如果我们只是从可以简单度量的数值入手,最终的 SLO 的作用就会很有限。 因此,与其选择指标,再想出对应的目标,不如从想要的目标反向推导出具体的指标。

目标的选择

  • 不要仅以目前的状态为基础选择目标 > 了解系统的各项指标和限制非常重要,但是仅仅按照当前系统的标准制定目标,而不从全局出发,可能会导致团队被迫长期运维一个过时的系统,没有时间去推动架构重构等任务
  • 保持简单 > SLI 中过于复杂的汇总模式可能会掩盖某种系统性能的变化,同时也更难以理解。
  • 避免绝对值 > 虽然要求系统可以在没有任何延迟增长的情况下无限扩张,或者”永远”可用是很诱人的,但是这样的要求是不切实际的。就算有一个系统能够做到这一点,它也需要花很长时间来设计和构建,同时运维也很复杂,最关键的是,这可能比用户可以接受的(甚至是很开心接受的)标准要高太多。
  • SLO 越少越好 > 应该仅仅选择足够的 SLO 来覆盖系统属性,一定要确保每一个 SLO 都是必不可少的: 如果我们无法针对某个 SLO 达标问题说服开发团队,那么可能这个 SLO 就是不必要的(如果 SRE 团队无法说服研发团队接受任何一个 SLO,那么这个产品可能压根不需要 SRE 团队的支持)。然而,不是所有的产品属性都能用 SLO 表达,用户的”满意度”就很难。
  • 不追求完美 > 我们可以随着时间流逝了解系统行为之后优化 SLO 的定义。刚开始可以以一个松散的目标开始,逐渐收紧。这比一开始制定一个困难的目标,在出现问题时放松要好的多。

控制手段

SLI 和 SLO 在决策系统运维时也非常有用

  1. 监控并且度量系统的 SLI
  2. 比较 SLI 和 SLO,以决定是否需要执行操作
  3. 如果需要执行操作,则要决定究竟什么操作需要被执行,以便满足目标
  4. 执行这些操作 例如,如果在第 2 步中,请求延迟正在上涨,无操作的话,会在几个小时内超出 SLO 范围。第 3 步则会测试服务器是否是 CPU 资源不够,同时增加一些 CPU 来分散负载。没有 SLO 的话,我们就不知道是否(或者何时)需要执行该操作。

第五章: 减少琐事

如果系统正常运转中需要人工干预,应该将此视为一种 BUG。”正常”的定义会随着系统的进步而不断改变。

SRE 要把更多的时间花费在长期项目研发上而非日常运维中。因为术语日常运维可能会被误解,我们在这里使用一个专门的词语 - 琐事(toil)

琐事的定义

  • 手动性
  • 重复性的
  • 可以被自动化的
  • 战术性的
  • 没有持久价值
  • 与服务同步线性增长

什么算做工程工作

典型的 SRE 活动分为如下几类

  • 软件工程
  • 系统工程
  • 琐事
  • 流程负担

琐事繁多是不是一定不好

琐事不太多的时候,已知的和重复性的工作有一种让人平静的功效。琐事可能是低风险低压力的活动,有些员工甚至喜欢做这种类型的工作。 如果琐事特别繁重,那就应该非常担忧。 琐事有害的因素

  • 职业停滞
  • 士气低落 另外,牺牲工程实践而做琐事会对 SRE 组织的整体发展造成损害
  • 造成误解
  • 进展缓慢
  • 开创先例
  • 促进摩擦产生
  • 违反承诺

本章小结

如果我们都致力于每一周通过工程工作消除一点琐事,就可以持续性地整顿服务。我们就可以将更多的力量投入到扩大服务规模的工程工作上去,或者是进行下一代的服务的架构设计,又或者是建立一套跨 SRE 使用的工具链。让我们多创新,少干琐事吧!

第六章: 分布式系统的监控

术语定义

  • 监控(monitoring) > 收集、处理、汇总,并且现实关于某个系统的实时量化数据,例如请求的数量和类型,错误的数量和类型,以及处理用时,应用服务器的存活时间等。
  • 白盒监控(white-box monitoring) > 依靠系统内部暴露的一些性能指标进行监控。包括日志分析、Java 虚拟机提供的监控接口,或者一个列出内部统计数据的 HTTP 接口的监控。
  • 黑盒监控(black-box monitoring) > 通过测试某种外部用户可见的系统行为进行监控。
  • 监控台页面(dashboard) > 提供某个服务核心指标一览服务的应用程序(一般是基于 Web 的)。该应用程序可能会提供过滤(filter)功能、选择(selector)功能等,但是主要的功能是用来显示系统最重要的指标。该程序同时可以显示相应团队的一些信息,包括目前工单的数量、最高优先级的 Bug 列表、目前的 on-call 工程师和最近进行的生产发布等。
  • 警报(alert)
  • 根源问题(root cause) > 如果这个问题被修复,就可以保证这种问题不会再以同样的方式发生。
  • 节点或机器(node/machine)
  • 推送(push)

为什么要监控

  • 分析长期趋势
  • 跨时间范围的比较,或者是观察实验组与控制组之间的区别
  • 报警 > 某项东西出现故障了,需要有人立刻修复!或者某项东西可能很快会出现故障,需要有人尽快查看。
  • 构建监控台页面
  • 临时性的回溯分析(也就是在线调试) > 我们的请求延迟刚刚大幅增加,有没有其他的现象同时发生?

我们不应该仅仅因为”某些东西看起来有点问题”就发出警报。 高效的警报系统应该提供足够的信息,并且误报率非常低。 虽然平时看看流量图表可能很有意思,但是 SRE 团队通常会小心地避免任何需要某个人”盯着屏幕寻找问题”的情况。 监控系统最重要的一点就是整个”生产故障,人工处理紧急警报,简单定位和深入调试”的过程必须要保持非常简单,必须能被团队中任何一个人所理解。

现象与原因

监控系统应该解决两个问题: 什么东西出故障了,以及为什么出故障。 “什么东西出故障了”即为现象(symptom),”为什么”则代表了原因(可能只是中间原因,并不是根源问题)。

黑盒监控与白盒监控

黑盒监控与白盒监控最简单的区别是: 黑盒监控是面向现象的,代表了目前正在发生的,而非预测会发生的问题,即”系统现在有故障”。白盒监控则大量依赖对系统内部信息的检测,如系统日志、抓取提供指标信息的 HTTP 节点等。

4 个黄金指标

监控系统的 4 个黄金指标分别是 延迟、流量、错误和饱和度(saturation)

  • 延迟 > 服务处理某个请求所需要的时间。这里区分成功请求和失败请求很重要。例如,某个由于数据库连接丢失或者其他后端问题造成的 HTTP 500 错误可能延迟很低。计算总体延迟时,如果将 500 回复的延迟也计算在内,可能会产生误导性的结果。
  • 流量 > 使用系统中的某个高层次的指标对系统负载需求所进行的度量。对于 Web 服务器来说,该指标通常是每秒 HTTP 请求数量,同时可能按请求类型分类(静态请求与动态请求)。针对音频流媒体系统来说,这个指标可能是网络 I/O 速率,或者并发会话数量。针对键值对存储系统来说,指标可能是每秒交易数量,或每秒的读取操作数量。
  • 错误 > 请求失败的速率,要么是显示失败(HTTP 500),要么是隐式失败(HTTP 200 回复中包含了错误内容)。
  • 饱和度 > 服务容量有多”满”。通常是系统中目前最为受限的某种资源的某个具体指标的度量。(在内存受限的系统中,即为内存;在 I/O 受限的系统中,即为 I/O)。

如果我们度量所有这 4 个黄金指标,同时在某个指标出现故障时发出警报(或者对于饱和度来说,快要发生故障时),能做到这些,服务的监控就基本差不多了。

关于长尾问题

区分平均值的”慢”和长尾值的”慢”的一个最简单的办法是将请求按延迟分组计数(可以用来制作直方图): 延迟 0 ~ 10ms 之间的请求数量有多少,30 ~ 100ms 之间,100 ~ 300ms 之间等。将直方图的边界定义为指数型增长(这个例子中倍数为 3)是直观展现请求分布的最好方式。

度量指标时采用合适的精度

系统的不同部分应该以不同的精度进行度量

  • 观察 1 分钟内的 CPU 平均值可能会错失导致长尾延迟过高的某种较长时间的 CPU 峰值现象。
  • 对于一个每年停机时间小于 9 小时的 Web 服务来说(年度可用绿 99.9%),每分钟检测 1 次或 2 次的监控频率可能过于频繁。
  • 对目标可用率为 99.9%的某个服务每 1 分钟或者 2 分钟检查一次硬盘剩余空间可能也是没必要的。

简化,直到不能再简化

设计监控系统时一定要追求简化。在选择需要检测什么的时候,将下列信息记在心里

  • 那些最能反映真实故障的规则应该越简单越好,可预测性强,非常可靠。
  • 那些不常用的数据收集、汇总,以及警报配置应该定时删除(某些 SRE 团队的标准是一个季度没有用到一次即将其删除)。
  • 收集到的信息,但是没有暴露给任何监控台,或者被任何警报规则使用的应该定时删除。

将上述理念整合起来

  • 该规则是否能检测到一个目前检测不到的、紧急的、有操作性的,并且即将发生或者已经发生的用户可见故障?
  • 是否可以忽略这条警报?什么情况可能会导致用户忽略这条警报,如何避免?
  • 这条警报是否确实显示了用户正在受到影响?是否存在用户没有受到影响也可以触发这条规则的情况?例如测试环境和系统维护状态下发出的警报是否应该被过滤掉。
  • 收到警报后,是否要进行某个操作?是否需要立即执行该操作,还是可以等到第二天早上再进行?该操作是否可以被安全地自动化?该操作的效果是长期的,还是短期的?
  • 是否也会有其他人收到相关的紧急警报,这些紧急警报是否是不必要的?

以上这些问题反映了在应对紧急警报上的一些深层次理念

  • 每当收到紧急警报时,应该立即需要我进行某种操作。每天只能进入紧急状态几次,太多就会导致”狼来了”效应。
  • 每个紧急警报都应该是可以具体操作的。
  • 每个紧急警报的回复都应该需要某种智力分析过程。如果某个紧急警报只是需要一个固定的机械动作,那么它就不应该成为紧急警报。
  • 每个紧急警报都应该是关于某个新问题的,不应该彼此重叠。

第七章: Google 的自动化系统的演进

“黑科技”之外,就只剩自动化和机械化了。

自动化的价值

  • 一致性 > 没有人能像机器一样永远保持一致。
  • 平台性
  • 修复速度更快
  • 行动速度更快
  • 节省时间

如果我们持续产生不可自动化的流程和解决方案,我们就继续需要人来进行系统维护。如果我们要雇佣人来做这项工作,就像是用人类的鲜血、汗水和眼泪养活机器。这就像是一个没有特效但是充满了愤怒的系统管理员的 Matrix 世界。

自动化分类的层次结构

自动化演进遵循以下路径

  1. 没有自动化 > 手动将数据库主进程在多个位置之间转移
  2. 外部维护的系统特定的自动化系统 > SRE 在 TA 的主目录中保存了一份故障转移脚本
  3. 外部维护的通用自动化系统 > SRE 将数据库支持添加到了每个人都在使用的”通用故障转移”脚本中
  4. 内部维护的系统特定的自动化 > 数据库自己发布故障转移脚本
  5. 不需要任何自动化的系统 > 数据库注意到问题发生,在无需人工干预的情况下进行故障转移

第八章: 发布工程

发布工程师的角色

Google 是一个数据驱动的公司,发布工程也不例外。各种各样的工具提供各种各样的数据。例如,从代码修改提交到部署到生产环境一共需要多长时间(也就是发布速度)。大部分这些工具都是由发布工程师设计和开发的。

发布工程哲学

  • 自服务模型 > 发布工程师开发工具,制定最佳实践,以便让产品研发团队可以自己掌控和执行自己的发布流程。发布过程是真正的自动化的,工程师仅仅在发生问题时才会进行干预。
  • 追求速度 > 有些团队每小时构建一次,然后在所有可用的构建版本中选择某个版本进行发布。
  • 密闭(hermetic)性 > 构建工具必须确保一致性和可重复性。
  • 强调策略和流程 多层安全和访问控制机制可以确保在发布过程中只有指定的人才能执行指定的操作。我们主要关注的操作有如下
    • 批准源代码改动 - 通过源代码仓库中的配置文件决定
    • 指定发布流程中需要执行的具体动作
    • 创建心的发布版本
    • 批准初始的集成请求(也就是一个以某个源代码仓库版本为基础的构建请求),以及后续的cherry picking请求
    • 实际部署某个发布版本
    • 修改某个项目的构建配置文件

配置管理

  • 使用主分支版本配置文件
  • 将配置文件与二进制文件打包在同一个包中
  • 将配置文件打包成配置文件包
  • 从外部存储服务中读取配置文件

总之,项目负责人在分发和管理配置文件时有多种选择,可以按需决定究竟那种最适合该服务。

小结

当采用合适的工具、合理化的自动化方式,以及合理的政策时,开发团队和 SRE 都无需担心如何发布软件。发布过程可以像一个按钮那么简单。

  • 如何管理包的版本?
  • 应该采用持续构建和部署的模型,还是应该定期构建?
  • 发布的频率应该怎样?
  • 应该使用什么策略管理配置文件?
  • 哪些发布过程的指标比较有用?

任何组织都应该先花一些时间定义自己的发布政策。

第九章: 简单化

可靠性只有靠对最大程度的简化不断追求而得到。

一个对 SRE 管理系统方法的总结是: “我们的工作最终是在系统的灵活性和稳定性上维持平衡”。

乏味是一种美德

关注必要复杂度和意外复杂度之间的区别非常关键。必要复杂度是一个给定的情况所固有的复杂度,不能从该问题的定义中移除,而意外复杂度则是不固定的,可以通过工程上的努力来解决。例如,编写一个 Web 服务器需要处理快速提供 Web 页面的必要复杂度。但是如果我们用 Java 编写该服务器,试图减少 GC 的影响就可能引入意外复杂度。

为了最小化意外复杂度,SRE 团队应该

  • 在他们所负责的系统引入意外复杂度时,及时提出抗议
  • 不断地努力消除正在接手的和已经负责运维的系统的复杂度

我绝对不放弃我的代码

工程师经常对于自己编写的代码形成一种情感依附,这些冲突在大规模清理源代码树的时候并不少见。一些人可能会提出抗议

  • 如果我们以后需要这个代码怎么办?
  • 我们为什么不只是把这些代码注释掉,这样稍后再使用它的时候会更容易吗?
  • 为什么不增加一个功能开关?

这些都是糟糕的建议。版本控制系统更改反转很容易,数百行的注释代码则会造成干扰和混乱(尤其是当源文件继续演进时);

那些由于功能开关没有启用而没有被执行的代码,就像一个定时炸弹一样等待爆炸。

最小 API

不是在不能添加更多的时候,而是没有什么可以去掉的时候,才能达到完美。

第十章: 基于时间序列数据进行有效报警

让查询来得更猛烈些吧,让寻呼机永远保持沉默!

一个大型系统不应该要求运维人员持续关注其中的无数个小组件,而是应该自动汇总所有的信息,自动抛弃其中的异常情况。监控系统应该主要从高级服务质量目标层面进行报警,但是也应该保持足够的粒度,可以追踪到某个具体的组件。

Borgmon

Google 之外的时间序列监控系统

  • Riemann
  • Heka
  • Bosun
  • Prometheus

第十一章: on-call 轮值

安全感

现代理论研究指出,在面临挑战时,一个人会主动或非被动(潜意识)地选择下列两种处理方法之一

  • 依赖直觉,自动化、快速行动
  • 理性、专注、有意识地进行认知类活动

当处理复杂系统问题时,第二种行事方式是更好的,可能会产生更好的处理结果,以及计划更周全的执行过程

在应急事故处理过程中,凭直觉操作和快速反应(例如服务出现问题就先重启服务器)看起来都是很有用的方法,但是这些方法都有自己的缺点。直觉很可能是错误的,而且直觉一般都不是基于明确的数据支持的。因此在处理问题的过程中,on-call 工程师很可能由于凭直觉去解释问题产生的原因而浪费宝贵的时间。快速反应主要是由习惯而产生的,习惯性的快速反应的动作后果一般都没有经过详细考虑,这可能会将灾难扩大。

在应急时间处理过程中,最理想的方法论是这样的: 在有足够的数据支撑的时候按步骤解决问题,同时不停地审视和验证目前所有的假设。

第十二章: 有效的故障排查手段

值得警惕的是,理解一个系统应该如何工作并不能使人成为专家。只能靠调查系统为何不能正常工作才行。

– Brian Redman

系统正常,只是该系统无数异常情况下的一种特例

– John Allspaw

新手们常常不能有效地进行故障排查,是因为这个过程理想情况下同时需要两个条件。

  1. 对通用的故障排查过程的理解(不依靠任何特定系统)
  2. 对发生故障的系统的足够了解

虽然只依靠通用性的流程和手段可以处理一些系统中的问题,但我们发现这样做通常是很低效的。对系统内部运行的了解往往限制了 SRE 处理系统问题的有效性,对系统设计方式的构建原理的知识是不可或缺的。

常见的陷阱

造成低效的故障排查过程的原因通常集中在定位(triage)、检查和诊断环节上,主要由于对系统不够了解而导致。下面列举了一系列常见的陷阱:

  1. 关注了错误的系统现象,或者错误地理解了系统现象的含义。这样会在错误的方向上浪费时间。
  2. 不能正确修改系统的配置信息、输入信息或者系统运行环境,造成不能安全和有效地测试假设。
  3. 将问题过早地归结为极为不可能的因素(例如认为是宇宙射线造成数据变化,虽然有可能发生,但是并不应该在解决问题初期做这个假设),或者念念不忘之前曾经发生过的系统问题,认为一旦发生过一次,就有可能再次发生。
  4. 试图解决与当前系统问题相关的一些问题,却没有认识到这些其实是巧合,或者这些问题其实是由于当前系统的问题造成的。(比如发现数据库压力大的情况下,环境温度也有所上升,于是试图解决环境温度的问题)

要避免第 1 点和第 2 点,需要更详细地学习系统的运行原理,同时了解分布式系统运行的基本模式。

要避免第 3 点,需要记住,不是所有的失败情况的出现概率都相同,就像谚语中说的”当你听到蹄子声响时,应该先想到马,而不是斑马。”,而且尤其要注意的是,当所有的可能都存在的时候,我们应该优先考虑最简单的解释

在某些系统中某一类问题可能完全被排除了。例如,在细心设计的集群文件系统实现中,由于某个磁盘出现问题导致延迟问题是非常不可能的了。

熬卡姆剃刀原理(Occam’s Razor),系统中可能同时存在多个问题,尤其是有的时候是因为系统中存在一系列低危害性问题,联合起来,才可以解释目前的系统状态。而不是系统中存在一个非常罕见的问题,同时造成了所有的问题现象。

理解我们逻辑推理过程中的错误是避免这些问题发生的第一步,这样才能更有效地解决问题。区分我们知道什么,我们不知道什么,我们还需要知道什么可以让查找问题原因和修复问题更容易。

定位

在大型问题中,你的第一反应可能是立即开始故障排查过程,试图尽快找到问题根源,这是错误的!不要这样做。

正确的做法应该是:尽最大可能让系统恢复服务。这可能需要一些应急措施,比如将用户流量从问题集群导向其他还在正常工作的集群,或者将流量彻底抛弃以避免连锁过载问题,或者关闭系统的某些功能以降低负载。缓解系统问题应该是你的第一要务。在寻找问题根源的时候,不能使用系统的用户并没有得到任何帮助。当然,快速定位问题时仍应该及时保存问题现场,比如服务日志等,以便后续进行问题根源分析时使用。

什么哪里为什么

在一个异常系统中,该系统经常还在执行某种操作,只是这些操作不是你想让系统执行的操作。那么找出系统目前正在执行什么,然后通过询问该系统为什么正在做这些操作,以及系统的资源都被用在了`哪里 可以帮助你了解系统为什么出错。

第十三章: 紧急事件响应

所有的问题都有解决方案

时间和经验一再证明,系统不但一定会出问题,而且会以没有人能够想到的方式出问题,Google 学到的最关键的一课是,所有的问题都有对应的解决方案,虽然对一个面对着疯狂报警的工程师来说,它可能不是那么显而易见。如果你想不到解决方法,那么就在更大的范围寻求帮助。找到更多的团队成员,寻求更多的帮助,做你需要做的一切事情,但是要快。最高的优先级永远是将手头问题迅速解决。很多时候,触发这个事故的人对事故了解得最清楚,一定要充分利用这一点。

非常重要的是,一旦紧急事件过去之后,别忘了留出一些时间书写事后报告。

为事故保留记录

没有什么比过去的事故记录是更好的学习资料了。历史就是学习其他人曾经犯的错误。在记录中,请一定要诚实,一定要事无巨细。尤其重要的是,提出关键的问题。时刻寻找如何能在战术及战略上避免这项事故的发生。公布和维护事后报告,确保全公司的每个人都能从中学到你所学到的知识。

在事故结束后,确保自己和其他人切实完成事故中总结的待办事项。这样能够避免未来再次发生以同样的因素触发的同样的事故。一旦开始仔细学习过去的事故,我们就能更好地避免未来的事故。

提出那些大的,甚至不可能的问题: 假如… …

没有什么比现实更真实的测试了。我们应该提出一些大的、没有确切答案的问题。

  • 假如整栋楼的电源坏了怎么办?
  • 假如网络设备被泡在半米深的水里怎么办?
  • 如果主数据中心突然下线了怎么办?
  • 如果有人入侵了你的 Web 服务器怎么办?

你怎么处理?找谁联系?谁来付钱?有对应的应急计划吗?你知道你的系统会如何应对吗?如上述所说正在发生,你能够立即最小化灾难损失吗?坐在你旁边的人能做到同样的事吗?

第十四章: 紧急事故管理

无流程管理的事故剖析

每个人都在尽力解决问题,起码他们自己看起来是这样。那么问题是怎么变得月来越糟呢?在这次处理的过程中,有几个常见的问题导致了整个事故的失控。

  • 过于关注技术问题

我们倾向于按技术能力指标聘请 Jack。所以他在灾难过程中忙着不断改变系统,英勇地尝试解决服务问题一点也不奇怪。由于他正忙着执行技术操作,所以根本没有时间和精力去思考如何能够通过其他手段缓解当前服务的问题。

  • 沟通不畅

同样的原因,Jack 根本没有时间清晰和有效地与其他人进行沟通,没有人知道他们的同事正在做什么。业务部门领导十分愤怒,最终用户正在面临服务问题,而其他可以帮忙调试和处理问题的工程师却没有被充分地利用起来。

  • 不请自来 > Tom 正在出于善意修改系统,但是他没有通知其他的同事 – 甚至 Jack。严格地讲,Jack 是故障排除的主要负责人,Tom 的操作将服务状况变得更糟了。

紧急事故的流程管理要素

Google 的紧急事故管理系统是基于 ICS - Incident Command System的,这套体系以清晰和灵活性著称。

嵌套式职责分离

在事故处理中,让每个人清楚自己的职责是非常重要的。明晰职责反而能够使每个人可以更独立自主地解决问题,因为他们不用担心怀疑他们的同事都在干什么。

以下是系统中可以分配给某个人的角色

  • 事故总控 incident command
  • 事物处理团队 operational work
  • 发言人 communication
  • 规划负责人 planning

什么时候对外宣布事故

  • 是否需要引入第二个团队来帮助处理问题?
  • 这次事故是否正在影响最终用户?
  • 在集中分析一小时后,这个问题是否依然没有得到解决?

事故流程管理最佳实践

  • 划分优先级
  • 事前准备
  • 信任
  • 反思
  • 考虑替代方案
  • 练习
  • 换位思考

第十五章 事后总结: 从失败中学习

学习是避免失败的最好办法 - Devin Carraway

事后总结条件为

  • 用户可见的宕机时间或者服务质量降级程度达到一定标准
  • 任何类型的数据丢失
  • on-call 工程师需要人工介入的事故(包括回滚、切换用户流量等)
  • 问题解决耗时超过一定限制
  • 监控问题(预示着问题是有人工发现的,而非报警系统)

第二十二章: 处理连锁故障

缓存

应该确保每个新添加的缓存要么是延迟类缓存,要么是经过良好设计的、可安全使用的容量类缓存。有些时候加入缓存是为了提高性能,但是最后却变成了强制依赖。

连锁故障的触发条件

  • 进程崩溃
  • 进程更新
  • 新的发布
  • 自然增长
  • 计划中或计划外的不可用

解决连锁故障的立即步骤

  • 增加资源
  • 停止健康检查导致的任务死亡 > 进程任务的健康检查 - 这个进程是否相应请求。对集群管理系统有用 > > 服务级别的健康检查 - 该进程是否能够回复这种类型的请求。对负载均衡器有用
  • 重启软件服务器
    • Java 服务器处于 GC 死亡螺旋中 > 由于 CPU 资源减少,请求处理速度变慢,内存使用率上升,导致 GC 触发次数增多,导致 CPU 资源进一步的减少。我们将此称之为GC 死亡螺旋
    • 某些正在处理的请求因为没有截止时间设置而正在消耗资源(如正在占用线程)
    • 死锁
  • 丢弃流量
    1. 解决最初的触发原因(如增加容量)
    2. 将负载降低到一定水平,使得崩溃停止。考虑在这里激进一些,如果整个服务都在崩溃循环中,那么可以考虑降低流量到 1%的水平
    3. 允许大部分的软件服务器恢复健康
    4. 逐渐提升负载水平
  • 进入降级模式
  • 消除批处理负载
  • 消除有害的流量

第二十三章 管理关键状态: 利用分布式共识来提高可靠性

一组服务进程可能想要可靠地对以下问题产生共识

  • 哪个进程目前是该组进程的领头人(leader)?
  • 本组中都包含哪些进程?
  • 是否已经将某个消息成功地插入了某个分布式队列?
  • 某个进程目前是否还持有租约(hold a lease)?
  • 数据存储中的某个键值对应的值是什么?

当你需要实现领头人选举(leader election)、关键性共享状态或分布式锁等时,建议采用某种正式证明过的、详尽测试过的分布式共识系统来实现。如果不严肃对待这个问题很可能会导致事故,在更糟的情况下,将造成某种非常难以修复的数据一致性问题。

CAP 理论

  • 一致性 Consistency (等同于所有节点访问同一份最新的数据副本)

  • 可用性 Availability (每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据)

  • 分区容错性 Partition tolerance (以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。) > CAP 理论论述了一个分布式系统不可能同时满足以下三个要求 > > - 每个节点上所见数据是一致的 > - 每个节点都可以访问数据 > - 可以承受网络分区问题 > > 根据定理,分布式系统只能满足三项中的两项而不可能满足全部三项。理解 CAP 理论的最简单方式是想象两个节点分处分区两侧。允许至少一个节点更新状态会导致数据不一致,即丧失了 C 性质。如果为了保证数据一致性,将分区一侧的节点设置为不可用,那么又丧失了 A 性质。除非两个节点可以互相通信,才能既保证 C 又保证 A,这又会导致丧失 P 性质。

对分布式系统的监控

  • 每个共识组中的成员数量,以及每个成员的状态(健康或不健康)
  • 始终落后的副本
  • 领头人角色是否存在
  • 领头人角色变化次数
  • 共识交易记录数字
  • 系统中的提议数量,以及系统中被接受的提议数量
  • 吞吐量和延迟 为了更好地理解系统性能,以及帮助进行故障排查,我们还需要监控以下几点
  • 针对提议接收时间的延迟分布
  • 系统不同部分之间观察到的网络延迟
  • 接收者在持久化日志上花费的时间
  • 系统每秒处理的字节数

第二十五章: 数据处理流水线

  • Google Workflow

第二十六章: 数据完整性 - 读写一致

数据完整性的强需求

什么是数据完整性(data integrity)? 当我们讨论一个面向用户的服务时,一切要以用户为准。

我们可以说数据完整性是指数据存储为了提供一个合理的服务质量,在可访问性和准确性方面必须达到的一个度量标准。但这种定义是不全面的。

例如,如果用户界面上的一个 Bug 导致 Gmail 一直显示收件箱为空,用户可能认为数据已经丢失了。虽然数据并没有真正的丢失,仅仅是无法访问,Google 保管数据的能力还是会因此受到质疑,云计算的可信性也会受影响,当 Gmail 出现错误提示或者是维护信息的时间过长,哪怕我们只是在修复一点点元数据,Google 在用户心中的信任度也会受到影响。

当我们讨论数据完整性的时候,最重要的就是云服务依然对用户可用。用户对数据的可访问性是最重要的

将上述数据完整性定义重新修改成为: 数据完整性意味着用户可以维持对云服务的访问,用户对数据的访问能力是最重要的,所以这种访问能力的完整性非常重要。

假设某个用户数据每年会损坏或者丢失一次。如果这种丢失是无法恢复的,那么对这块用户数据来说,这块数据今年的在线时间就变成 0 了。最好手段就是主动探测加快速修复。

假设在另外一个平行宇宙中,每一次数据的损坏都能在用户被影响之前立即发现,自动被删除、修复,最终在 30 分钟之内恢复访问。那么可以说针对这块用户数据,当年的可用率是 99.99%。

提供超高的数据完整性的策略

大多数云计算应用都是优化一下 5 项的某种组合

  • 在线时间 > 经常也用可用率(availability)指代着某个服务可以被用户使用的比率。
  • 延迟 > 服务对用户的响应时间
  • 规模 > 某个服务的用户数量,已经能够维持正常服务水平的最高负载
  • 创新速度 > 某个服务能够在合理的成本下,为用户提供更好的服务的创新速度
  • 隐私 > 这个名词的定义比较复杂。简单来说,本章将隐私的定义限制为仅仅针对数据删除: 用户删掉服务中的数据后,数据必须在合理时间内被真正摧毁

备份与存档

没有人真的想要备份数据,他们只想恢复数据。

备份与存档最重要的区别就是,备份是可以直接被应用程序重新加载的。因此备份的和存档的使用场景非常不同。

考虑到大多数最新产生的数据直到安全备份结束之前都存在丢失的风险,这就意味着备份(而不是存档)应该至少每天进行一次,或者每小时,甚至更短时间进行一次。备份应该同时使用完整备份、增量备份,或者甚至是流式持续备份手段。

因此,当选择备份策略时,一定要考虑针对某个问题需要的恢复时间,以及可以丢失多少最新数据。

数据完整性是手段,数据可用性是目标

数据完整性指的是在其生命周期中,数据的准确性和一致性。从数据被记录的那一刻开始,一直到数据被访问的时候,数据应该保持正确,不会以某种未预知的方式改变。但是,这样就够了吗?

从用户角度看,仅仅保障数据完整性,而没有保障数据的可用性是没有意义的。

造成数据丢失的事故类型

我们应该针对每种潜在的事故类型来设计数据完整性机制

  • 根源问题 > 某种无法恢复的用户数据丢失是由以下几个因素造成的: 用户行为、管理员的错误、应用程序的 Bug、基础设施中的问题、硬件故障和部署区的大型事故
  • 影响范围 > 有些数据丢失是大规模的,同时影响很多实体。有些数据丢失是非常有针对性的,仅仅是一小部分用户的数据损坏或者丢失
  • 发生速度 > 有些数据丢失是一瞬间造成的(例如,100 万条数据在一次操作中被替换成了 10 条)。而有些数据丢失是缓慢持续进行的(例如,每分钟丢失 10 条数据,但是持续了一周时间)。
根源问题 影响范围 发生速度
用户行为
操作员错误
应用程序 Bug 广泛 快速发生
基础设施缺陷 范围很窄 缓慢持续
硬件故障
自然灾害

维护数据完整性的深度和广度的困难之处

在设计数据完整性保障机制时,必须要认识到复制机制和冗余并不意味着可恢复性

当被问是否有备份的时候,一个经典的错误回答是我们有比备份更好的机制–复制机制!复制机制很重要,包括提高数据的本地性locality,保护某个部署点单点故障等。但是有很多数据丢失场景是复制机制无法保护的。一个自动同步多个副本的数据存储多半会在你发现问题之前,将损坏的数据记录以及错误的删除动作更新到多个副本上。

24 种数据完整性的事故组合

用户删除数据 应用程序删除数据 应用程序清理数据 -> 数据被摧毁
用户可见的垃圾 用户不可见,由应用程序或者存储服务软删除 数据被应用程序或者存储服务摧毁
用户可以还原 可以被用户支持团队或者应用程序管理员恢复 无法还原,还原需要使用备份
预防用户错误 防范应用程序 Bug 和服务管理员错误 备份可以为这个和其他所有场景提供保护
  1. 软删除soft deletion
  2. 备份和相关的恢复方法 > 对恢复提供支持,应该是主导备份系统设计的关键
  3. 早期预警 > 越早检测到数据丢失,数据的恢复就越容易,也越完整

确保数据恢复策略可以正常工作

如果数据恢复测试是一个手工的、分阶段进行的操作,那么就不可避免地成为一个讨人嫌的麻烦事。这样会导致测试过程要么不会被认真执行,要么执行得不够频繁以至于作用有限。因此,在任何情况下,都应该追求完全自动化这些测试步骤,并且保证它们能持续运行。

数据恢复计划中需要覆盖的点是很多的

  • 备份数据是否是完整的、正确的 – 还是空的?
  • 我们是否有足够的物理机资源来完整地完成整个恢复过程: 包括运行恢复任务本身,真正恢复数据,以及数据后处理人物?
  • 整个数据恢复过程能否在合理的时间内完成?
  • 是否在数据恢复过程中监控状态信息?
  • 恢复过程是否依赖于某些无法控制的元素 – 例如某个不是 7x24 随时可以使用的异地存储媒介?

失败是不可避免的,不去主动寻找这些数据恢复失败的场景等于玩火自焚。只有通过进行测试来主动寻找这些弱点,才能够提前修复,关键时刻才不至于追悔莫急!

SRE 的基本理念在数据完整性上的应用

  • 保持初学者的心态
  • 信任但要验证
  • 不要一厢情愿
  • 纵深防御

可靠地进行产品的大规模发布

发布协调工程师 Launch Coordination Engineering, LCE

建立发布流程

好的发布流程具有的一些特征

  • 轻量级 > 占用很少的开发时间
  • 鲁棒性 > 能够最大限度地避免简单的错误
  • 完整性 > 完整地、一致地在各个环节内跟踪重要的细节问题
  • 可扩展性 > 可以应用在很多简单的发布上,也可以用在复杂的发布过程中
  • 适应性 > 可以适用于大多数常见的发布(例如在产品界面上增加新的 UI 组件),以及可以适应全新的发布类型(例如 Chrome 浏览器和 Google Fiber 的第一次上线)

这些需求是互相冲突的。Google 成功地采用了以下几种手段来达到目的

  • 简化 > 确保基本信息正确。不需要为所有的可能性做准备
  • 高度定制 > 有经验的工程师会针对每次发布定制流程
  • 保证通用路径快速完成 > 识别出几类发布流程所具有的共同模式(例如在新的国家发布产品),针对这类发布提供一个快速简化通道

经验证明,工程师会绕过那些过于繁琐,或者附加值不高的流程 – 尤其是在产品上线的压力之下 – 整个发布流程可能被视为另外一个拦路虎。正是由于这样,LCE 必须持续不断地优化整个发布体验,在成本与收益上保持平衡。

起草一个发布检查列表

架构与依赖

  • 从用户到前端再到后端,请求流的顺序是什么样的?
  • 是否存在不同延迟要求的请求类型?

    • 待办事项
    • 将非用户请求与用户请求进行隔离
    • 确认预计的请求数量。单个页面请求可能会造成后端多个请求

集成

  • 给服务建立一个新的 DNS
  • 为服务配置负载均衡系统
  • 为服务配置监控系统

容量规划

  • 本次发布是否与新闻发布会、广告、博客文章或者其他类型的推广活动有关?
  • 发布过程中和之后预计的流量和增速是多少?
  • 是否已经获取到该服务需要的全部计算资源?

故障模式

  • 系统设计中是否包括单点故障源?
  • 该服务是如何处理依赖系统的不可用性的?

    • 待办事项
    • 为请求设置截止时间,防止由于请求持续时间过长导致资源耗尽
    • 加入负载丢弃功能,在过载情况中可以尽早开始丢弃新请求

客户端行为

  • 该服务是否实现了自动保存/自动完成(输入框)/心跳等功能?

    • 待办事项
    • 确保客户端在请求失败之后按指数型增加重试延时
    • 确保自动请求中实现随即延时抖动

流程与自动化

  • 维持服务运行是否需要某些手动执行的流程?

    • 待办事项
    • 将所有需要手动执行的流程文档化
    • 将迁移到另外一个数据中心的流程文档化
    • 将构建和发布新版本的流程自动化

开发流程

  • 待办事项
    • 将所有的代码和配置文件都存放到版本控制系统中
    • 为每个发布版本创建一个新的发布分支

外部依赖

  • 这次发布依赖哪些第三方代码,数据、服务,或者事件?
  • 是否有任何合作伙伴依赖于你的服务?发布时是否需要通知他们?
  • 当我们或者第三方提供商无法在制定截止日期前完成工作时,会发生什么?

发布计划

  • 待办事项
    • 为该服务发布制定一个发布计划,将其中的每一项任务对应到具体的人
    • 针对发布计划中的每一步分析危险性,并制定对应的备用方案

可靠发布所需要的方法论

  • 灰度和阶段性发布
  • 功能开关框架 – 通常需要满足以下几个要求 > 测试是否一小部分用户会喜欢使用某个新功能
    • 可以同时发布多个改动,每个改动仅针对一部分服务器、用户、实体,或者数据中心起作用
    • 灰度发布到一定数量的用户,一般在1%~10%之间
    • 将流量根据用户、对话、对象和位置等信息发送到不同的服务器上
    • 设计中可以自动应对新代码出现的问题,不会影响到用户
    • 在严重 Bug 发生,或者其他副作用场景下可以迅速单独屏蔽某个改变
    • 度量每个改变对用户体验的提升

第二十八章: 迅速培养 SRE 加入 on-call

推荐的培训方式

  1. 设计一个具体的,有延续性的学习体验,以便学员跟进。
  2. 鼓励反向工程,利用统计学来思考问题,以及多思考问题的本质。
  3. 鼓励学员分析失败的案例,分享好的事后总结来阅读。
  4. 创造一些受控的,但是逼真的场景让学员利用真是的监控环境和工具来修复。
  5. 在团队内以角色扮演的形式演习理论上可能发生的问题,让大家在这个过程中交流彼此的解决问题的方式。
  6. 给学员创造条件让他们参与间隙 on-call,和实际轮值的 on-call 工程师交流经验。
  7. 让学员与 sre 老手一起共同修订培训计划中的某个部分。
  8. 帮学员一起找到一个具有一定复杂度的项目,帮助他们在整个技术栈内建立自己的地位。

有抱负的 on-call 工程师的 5 个特点

  • 对事故的渴望: 事后总结的阅读和书写 > 忘记过去的人必然会犯重复的错误
  • 故障处理分角色演习
  • 破坏真的东西,并且修复它们
  • 维护文档是学徒任务的一部分
  • 尽早、尽快见习 on-call

第二十九章: 处理中断性任务

流状态 flow state

在流状态里,可以提升生产力,也可以提升创造性,甚至艺术创造性。进入这个状态可以鼓励员工真正地掌握和优化某个他们负责的任务和项目。但是,如果有其他的事情打断他们,他们就会失去这个状态。我们的目标是尽可能让员工在这个状态下工作。

如果某项工作需要的技能很少,虽然它是可持续性的,但是无法激发创造力。这些任务具有很明确的目标,反馈很及时,控制感很强,同时时间流逝感也很强烈,例如打扫卫生、开车等。

尊重自己,也尊重用户

  • 团队应该为自己的服务设置合理的服务水平
  • 将某些事物推回给客户解决是可以的

第三十章: 通过嵌入 SRE 的方式帮助团队从运维过载中恢复

第一阶段: 了解服务,了解上下文

第二阶段: 分享背景知识

关于事后总结

  1. 对事不对人
  2. 在任何一个交互关系错综复杂的系统中,错误是不可避免的。我相信,在当时你利用了最正确的信息做出了最正确的决定。我想要你写下在这段时间里每一个时间点你所想的事情,这样我们就能发现系统在哪里误导了你,以及过程中什么地方对人的认知能力要求过高。

第三阶段: 主导改变

获取团队成员的帮助

  1. 找到一个可以由某个团队成员完成的有价值的工作
  2. 清晰地解释这项工作是如何以一个永久的方式解决事后总结中出现的问题。就算是一个健康的团队有时候也是比较短视的
  3. 自己亲自作为代码更改和文档修订的评审者
  4. 在两到三个问题上重复这个过程

解释你的逻辑推理过程

  • “我反对最新版本的原因不是因为测试结果有问题,而是因为我们对发布所制定的错误预算已经耗尽了”
  • “发布需要能够安全回退,因为我们的 SLO 非常高。要想达到 SLO 的要求,平均恢复时间必须非常短,这样回退之前进行深入的现场调查是不现实的。”

提出引导性问题

  • “我看到任务失败的警报经常发生,但是 on-call 工程师通常什么都不做。这样会对 SLO 有什么影响?”
  • “这个上线过程看起来非常复杂。你知道为什么创建一个新的服务实例需要这么多配置文件的更新吗“
  • ”这些旧的、停滞的发布是什么情况?“
  • ”为什么某个组件要做这么多事情?“

第三十一章: SRE 与其他团队的沟通与协作

议程

  • 即将到来的生产环境变化
  • 性能指标
  • 故障
  • 紧急警报

第三十二章: SRE 参与模式的演进历程

PRR 模型(生产就绪度评审)Production readiness review

PRR

SRE 参与模型

SRE 承担重要服务的生产运维的责任,是为了提高该服务的可靠性。SRE 会考量该服务的几个方面,统称生产方面。这些方面包括:

  • 系统的体系结构和跨服务依赖
  • 指标的选择、度量和监控
  • 紧急事件处理
  • 容量规划
  • 变更管理
  • 性能: 可用性、延迟和资源效率 0000000

简单的 PRR 模型

  • 验证该服务符合公认的标准的生产部署方式和运维方式,同时该服务负责人已经准备好与 SRE 一起工作,利用 SRE 的知识改进服务。
  • 提升服务生产环境中的可靠性,并最小化可预见的故障的数量和严重程度。PRR 会关注 SRE 所关注的生产环境的所有方面。

参与

  • 为该服务建立一个 SLO/SLA
  • 为可能的、为了提高可靠性的破坏性设计变更制定计划
  • 制定交接计划和培训计划

该流程的目标是对 PRR 流程、最终目标,以及结果形成一个共识

分析

  • 对于服务进行的更新是否立即影响到系统中大比例的部分?是否合理?
  • 服务的依赖是否合理?例如,直接面向用户请求的服务不应该依赖于一个主要用于批处理的系统。
  • 该服务在连接关键的远程服务时,是否需要较高的网络服务质量(QoS)?
  • 该服务是否会将错误报告传送到中央日志记录系统进行分析?该服务是否报告所有的会造成降级回复,或者会造成最终用户请求失败的特殊情况?
  • 所有用户可见的请求失败情况都有度量和监控吗?报警策略是否合适?

改进和重构

  • 将每个改进建议按照其对服务可靠度的影响而排序,制定一个优先级顺序
  • 与研发团队讨论和协商这些建议,达成一个共识
  • SRE 和产品研发团队同时参与,并且协助对方进行重构,或者实现额外的功能。

此阶段所需要的时间和资源在各个项目上有很大不同。这主要取决于工程师花在重构上的时间,评估该服务成熟度和复杂度的时间,以及无数的其他因素。

培训

  • 设计概述
  • 对系统中各种请求的处理流进行深入探讨
  • 生产环境部署模式的描述
  • 对系统运维各个方面的手动练习

附录

  • 可用性时间表 Availability level