Google SRE

2022-06-21
3分钟阅读时长

第4章 服务质量目标

服务质量术语

  • 服务质量指标(SLI)—— indicator
  • 服务质量目标(SLO)——Objective
  • 服务质量协议(SLA)——Agreement

单词

  • 指标(indicator)
  • 持久性(durability)
  • 可用性(availability)
  • 目标(Objective)
  • 协议(Agreement)

第5章 减少琐事

4个黄金指标

延迟、流量、错误和饱和度(saturation)

第7章 自动化

自动化工具:Puppet、Chef、cfengine

Chubby服务器

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

监控:Pormetheus、Riemann、Heka和Bosun

  • time-series

第11章 on-call轮值

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

最重要的资源有:

● 清晰的问题升级路线。

● 清晰定义的应急事件处理步骤。

● 无指责,对事不对人的文化氛围。

第12章 有效的故障排查手段

1.对通用的故障排查过程的理解(不依靠任何特定系统)。

2.对发生故障的系统的足够了解

读者应该小心避免:

● 关注了错误的系统现象,或者错误地理解了系统现象的含义。这样会在错误的方向上浪费时间。

● 不能正确修改系统的配置信息、输入信息或者系统运行环境,造成不能安全和有效地测试假设。

● 将问题过早地归结为极为不可能的因素(例如认为是宇宙射线造成数据变化,虽然有可能发生,但是并不应该在解决问题初期做这个假设),或者念念不忘之前曾经发生过的系统问题,认为一旦发生过一次,就有可能再次发生。

● 试图解决与当前系统问题相关的一些问题,却没有认识到这些其实只是巧合,或者这些问题其实是由于当前系统的问题造成的。(比如发现数据库压力大的情况下,环境温度也有所上升,于是试图解决环境温度问题。)

Dapper

提供了非常有用的了解分布式系统工作情况的一种方式

单词

监测指标(telemetry)

定位(triage)

相关性(correlation)

因果关系(causation)

问题分解(Divide & Conquer)

对分法(bisection)

第14章 紧急事故管理

一次流程管理良好的事故

第17章 测试可靠性

  • 传统测试
    • 单元测试(unit test)
    • 集成测试(integration test)
      • 依赖注入(dependency injection)
    • 系统测试(system test)
      • 冒烟测试(smoke test):冒烟测试有时也被称为理性测试
      • 性能测试(performance test)
      • 回归测试(regression test)
  • 生产测试
    • 变更发布与测试
    • 配置测试
    • 金丝雀测试

第18章 SRE 部门中的软件工程实践

Auxon 案例

基于意图的容量规划

1.我需要50个CPU的资源,必须在集群X、Y、Z中,为服务Foo使用。

这是一个具体的资源请求,但是,为什么我们需要这么多资源,同时一定要在这三个集群中?

2.我需要50个CPU的资源,在地理区域YYYY中的任意三个集群中,为服务Foo使用。

这项请求增加了更多的选择自由度,也更容易被满足,但是仍然没有解释这项请求背后的原因,为什么我们需要这么多资源,以及为什么需要三个集群?

3.我想要满足Foo在每个地理区域的需求增长,同时保障N+2冗余度。

现在有了更多的选择自由度,同时我们可以更好地理解如果Foo没有获得相应的资源,究竟会造成什么后果。

4.我们想要将Foo以99.999%的可靠度运行。

这是相对更抽象的一个需求,当容量得不到满足时的后果也更清楚了:可靠性会下降。而且我们从中获得了更多的选择自由度:也许以N+2运行并不是足够的,或者并不是最优化的情况,其他的某种部署计划是更合适的。

短语

  • 需求编排(bin-pack)

  • 最佳压缩(bin-packing)

  • 电子表格(spreadsheet)

  • 过期(stale)

  • 数据信息(Performance Data)

  • 资源供给(Resource Supply)

  • 资源价格(Resource Pricing)

  • 意图配置信息(Intent Config)

  • 通用型人才(generalist)

  • 专家(specialist)

  • 运维研究(operation research)

  • 量化分析(quantitate analysis)

第19章 前端服务器负载均衡

使用DNS进行负载均衡

  • anycast DNS服务器
  • OpenDNS

负载均衡:虚IP

  • 内部采用更大的MTU来避免碎片重组

短语

  • 权威域名服务器(authoritive nameserver)
  • 递归解析器(recursive nameserver)
  • 网络负载均衡器(network loadbalancer)
  • 一致性哈希(consistent hashing)
  • 包封装(encapsulation)
  • 碎片重组(Fragmentation)

第20章 数据中心内部的负载均衡系统

跛脚鸭状态

停止过程通常按照以下步骤进行:

1.任务编排系统发送一个SIGTERM信号给该任务。

2.后端任务进入跛脚鸭状态,同时请求它的所有客户端发送请求给其他后端任务。这通过SIGTERM信号处理程序中调用RPC实现中的API完成。

3.任何在后端进入跛脚鸭状态时正在进行的请求(或者在进入状态之后,但是其他客户端收到通知之前)仍会继续进行。

4.随着请求回复被发送回客户端,该后端任务的活跃请求逐渐降低为0。

5.在配置的时间过后,该后端程序要么自己干净地退出,要么任务编排系统主动杀掉它。该时间应该被设置为一个足够大的值,以便一般的请求可以有足够的时间完成。每个服务的该数值都不同,一般来说取决于客户端的复杂程度,10s到150s是一个不错的选择。

利用划分子集限制连接池大小

子集划分:限制某个客户端任务需要连接的后端任务数量。

确定性算法

def Subset(backends, client_id, subset_size):
    subset_count = len(backends) / subset_size
    
    # 将客户端划分为多轮,每一轮计算使用同样的随机排列的列表
    round = client_id / subset_count
    random.seed(round)
    random.shuffle(backends)
    
    # subset_id 代表了目前的客户端
    subset_id = client_id % subset_count
    
    start = subset_id * subset_size
    return backends[start:start+subset_size]

负载均衡策略

简单轮询算法

分配不均因素:
  • 子集过小

  • 请求处理成本不同

  • 物理服务器的差异

  • 无法预知的性能因素

    • 坏邻居
    • 任务重启

最闲轮询策略

  • 活跃请求的数量并不一定是后端容量的代表
  • 每个客户端的活跃请求不包括其他客户端发往同一个后端的请求

加权轮询策略

加权轮询策略通过在决策过程中加入后端提供的信息,是一个针对简单轮询策略和最闲轮询策略的加强。

加权轮询策略理论上很简单:每个客户端为子集中的每个后端任务保持一个“能力”值。请求仍以轮询方式分发,但是客户端会按能力值权重比例调节。在收到每个回复之后(包括健康检查的回复),后端会在回复中提供当前观察到的请求速率、每秒错误值,以及目前资源利用率(一般来说,是CPU使用率)。客户端根据目前成功请求的数量,以及对应的资源利用率成本进行周期性调节,以选择更好的后端任务处理失败的请求也会记录在内,对未来的决策造成影响

第21章 应对过载

自适应节流的技术来实现客户端节流

具体地说,每个客户端记录过去两分钟内的以下信息:

**请求数量(requests)**应用层代码发出的所有请求的数量总计(指运行于自适应节流系统之上的应用代码)。

**请求接受数量(accepts)**后端任务接受的请求数量。

在常规情况下,这两个值是相等的。随着后端任务开始拒绝请求,请求接受数量开始比请求数量小了。客户端可以继续发送请求直到requests=K * accepts,一旦超过这个限制,客户端开始自行节流,新的请求会在本地直接以一定概率被拒绝(在客户端内部),概率使用公式21-1进行计算:

客户端请求拒绝概率: max(0, (requests-K*accepts)/(requests+1))

对那些处理请求消耗的资源和拒绝请求的资源相差无几的系统来说,允许用50%的资源来发送拒绝请求可能是不合理的。在这种情况下,解决方案很简单:通过修改客户端中算法的accepts的倍值K(例如,2)就可解决:

● 降低该倍值会使自适应节流算法更加激进。

● 增加该倍值会使该算法变得不再那么激进。

举例来说,假设将客户端请求的上限从request=2 * accepts调整为request=1.1*accepts,那么就意味着每10个后端请求之中只有1个会被拒绝。

一般来说推荐采用K=2,通过允许后端接收到比期望值更多的请求,浪费了一定数量的后端资源,但是却加快了后端状态到客户端的传递速度。举例来说,后端停止拒绝该客户端的请求之后,所有客户端检测到这个变化的耗时就会减小。

重要性

4个级别

  • 最重要 CRITICAL_PLUS
  • 重要 CRITICAL
  • 可丢弃的SHEDDABLE_PLUS
  • 可丢弃的SHEDDABLE

资源利用率信号

cpu

内存

处理过载错误

数据中心中的大量后端任务都处于过载状态

数据中心中的一小部分后端任务处于过载状态

连接造成的负载

RPC协议需要不活跃的客户端定期执行健康检查。当某个连接空闲一段可配置的时间后,客户端放弃TCP连接,转为UDP健康检查。不幸的是,这种行为会对大量请求率很低的客户端造成问题:健康检查需要比实际处理请求更多的资源。通过仔细调节连接参数(如,大幅降低健康检查频率)或者动态创建和销毁连接可以优化这些场景

处理突发性的新连接请求是另外一个(相关的)问题。我们观察到,超大规模的批处理任务会在短时间内建立大量的客户端。协商和维护这些超大数量的连接可以造成整个后端的过载。在我们的经验里,以下策略可以帮助消除这些问题:

● 将负载传递给跨数据中心负载均衡算法。在这种情况下,过载请求会被转移到其他数据中心。

● 强制要求批处理任务使用某些特定的批处理代理后端任务,这些代理仅仅转发请求,同时将回复转发给客户端。

单词短语

  • 全局过载(global overload)
  • 重要性(criticality)
  • 利用率(utilization)
  • 执行器负载均值(executor load average)
  • 数性衰变算法(exponential decay)

如何利用不同的技术手段(确定性算法、加权轮询、客户端侧的节流、用户配额等)更平均地将负载分散到数据中心。

第22章 处理连锁故障

防止软件服务器过载

  • 提供降级结果
  • 在过载情况下主动拒绝请求
  • 上层系统应该主动拒绝请求
  • 进行容量规划

速率限制可以具体在以下位置实现:

  • 在反向代理层,通过针对请求的某种特性进行数量限制(如IP地址),来缓解和避免拒绝服务攻击,避免攻击性客户端的影响。

  • 在负载均衡器层,在服务进入全局过载时主动丢弃请求。取决于该服务的实现和复杂度,这种丢弃可能是针对所有请求的(丢弃超过X QPS 的所有请求),或者是有选择性的(丢弃非最近用户发来的请求,或者丢弃低优先级请求,如背景同步请求,但是保留用户交互型请求)。

  • 在每个任务自身,避免负载均衡层的随机扰动导致软件服务器过载。

队列管理

流量抛弃和优雅降级

流量抛弃(load shedding)是指在软件服务器临近过载时,主动抛弃一定量的负载。目标是避免该软件服务器出现内存超限,健康检查失败,延迟大幅升高,或者其他过载造成的现象。也就是使软件服务器在负载极限时,尽可能地再多做一些有用的工作。

  • 可控延迟算法(CodDel)

优雅降级(graceful degradation)可在流量抛弃的基础上进一步减少服务器的工作量。在某些应用程序中,是可以通过降低回复的质量来大幅减少所需的计算量或者所需的计算时间的。例如,一个搜索类型的应用可能在过载情况下仅仅搜索保存在内存中的数据,而不是搜索全部存在硬盘上的数据。该服务或者可以采用一种不那么精确(但是更快)的算法来进行结果排序。

评估流量抛弃或者优雅降级时,需要考虑以下几点:

  • 确定具体采用哪个指标作为流量评估和优雅降级的决定性指标(如,CPU用量、延迟、队列长度、线程数量、是否该服务可以自动进行降级,或者需要人工干预)。
  • 当服务进入降级模式时,需要执行什么动作?
  • 流量抛弃或者优雅降级应该在服务的哪一层实现?是否需要在整个服务的每一层都实现,还是可以选择某个高层面的关键节点来实现?

当评估和具体实施的时候,还需要考虑以下几点:

  • 优雅降级不应该经常被触发—通常触发条件显示了容量规划的失误,或者是出现了意料之外的负载转移。整个降级系统应该简单、易懂,尤其是在不经常使用的情况下。
  • 记住,代码中平时不会使用的代码路径(通常来说)是不工作的。在稳定运行状态下,优雅降级不会经常触发,意味着在这个模式下的运维经验很少,对这个模式的问题也不够熟悉,这就会升高这种模式的危险性。我们可以通过定期针对一小部分的服务压力测试以便更多地触发这个模式,保证这个模式还能正常工作。
  • 监控系统应该在进入这种模式的软件服务器过多时报警。
  • 复杂的流量抛弃和优雅降级系统本身就可能造成问题—过于复杂的逻辑可能会导致软件服务器以外的服务进入降级模式运行,甚至进入反馈循环。设计时应该实现一种简单的关闭降级模式,或者是快速调节参数的方式。将这个配置文件存储在一个强一致性的存储系统(如 Chubby)中,每个软件服务器都可以订阅改变,可以提高部署速度,但是同时也会增加整个系统的同步性风险(如果配置文件有问题,全部服务器同时都会受到影响)。

重试

当发送自动重试时,需要将如下部分考虑在内:

  • 大部分后端保护策略都适用于此。尤其是,对系统进行测试可以提前显现问题,同时优雅降级可以将重试对后端的影响降低。
  • 一定要使用随机化的、指数型递增的重试周期。
  • 限制每个请求的重试次数。不要将请求无限重试。
  • 考虑使用一个全局重试预算。
  • 从多个视角重新审视该服务,决定是否需要在某个级别上进行重试。
  • 使用明确的返回代码,同时详细考虑每个错误模式应该如何处理。

请求延迟和截止时间

  • 选择截止时间

设置一个截止时间通常是明智的。不设置截止时间,或者设置一个非常长的截止时间通常会导致某些短暂的、已经消失的问题继续消耗服务器资源,直到重启。

  • 超过截止时间

很多连锁故障场景下的一个常见问题是软件服务器正在消耗大量资源处理那些早已经超过客户端截止时间的请求。这样的结果是,服务器消耗大量资源没有做任何有价值的工作,回复已经被取消的RPC是没有意义的。

  • 截止时间传递

与其在发送RPC给后端服务器时自拟一个截止时间,不如让软件服务器采用截止时间传递和取消传递的策略。

请求延迟的双峰分布(Bimodal) 解决指导思想

慢启动和冷缓存

如果缓存对服务造成了很大的影响,[76]可能要采取以下几种策略中的一种或多种

  • 过量配备(overprovision)该服务
  • 使用通用的连锁故障避免手段
  • 当为一个集群增加负载时,需要缓慢增加

连锁故障的触发条件

  • 进程崩溃
  • 进程更新
  • 新的发布
  • 自然增长
  • 计划中或计划外的不可用
  • 请求特征的变化
  • 资源限制

连锁故障的测试

应该针对服务进行压力测试,通过对重载下服务行为的观察可以确定该服务在负载很重的情况下是否会进入连锁故障模式。

  • 测试直到出现故障,还要继续测试
    • 应该测试一个组件在过载之后再恢复到正常水平的行为状态
  • 测试最常用的客户端
  • 测试非关键性后端

解决连锁故障的立即步骤

  • 增加资源

  • 停止健康检查导致的任务死亡

    • 进程任务的健康检查(“这个进程是否响应请求”)
    • 服务级别的健康检查(“该进程是否能够回复这种类型的请求”)
  • 重启软件服务器

    • Java服务器处于GC死亡螺旋中
    • 某些正在处理的请求因为没有截止时间设置而正在消耗资源
    • 死锁
  • 丢弃流量

    1. 解决最初的触发原因(如增加容量)。
    2. 将负载降低到一定水平,使得崩溃停止。考虑在这里激进一些,如果整个服务都在崩溃循环中,那么可以考虑降低流量到1%的水平。
    3. 允许大部分的软件服务器恢复健康。
    4. 逐渐提升负载水平。
  • 进入降级模式

  • 消除批处理负载

  • 消除有害的流量

小结

在请求失败的时候重试、负载自动转移、自动杀掉不健康的服务器、增加缓存以提高性能或者降低延迟:这些手段原本都是为了优化正常情况下的服务性能,但是也可能会提高大规模的服务故障的几率。

短语

  • 优雅降级(graceful degradation)
  • 流量抛弃(load shedding)
  • 异常请求(malformed request)
  • 截止时间(deadline)
  • 存储系统 Chubby
  • 硬黏性或软黏性(hard/soft stickiness)

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

  • Paxos 协议、Raft、Zab、 Mencius

  • ZooKeeper、Consul,以及 etcd

  • Google的Chubby 服务

ACID数据存储语义(原子性、一致性、隔离性和持久性)

BASE:基本可用、软状态、最终一致性(basically available、soft state、eventual consistency)

拜占庭式问题:指的是当某个进程由于程序Bug或者恶意行为发送不正确的消息的问题,这种问题相对来说处理成本更高,同时也更少见。

分布式共识系统的某些区域需要特别关注,包括以下几项:

  • 始终落后的副本
  • 领头人角色是否存在
  • 领头人角色变化的次数
  • 共识交易记录数字
  • 吞吐量和延迟

为了更好地理解系统性能,以及帮助进行故障排查,我们还要监控以下几点:

  • 针对提议接收时间的延迟分布。
  • 系统不同部分之间观察到的网络延迟。
  • 接收者在持久化日志上花费的时间。
  • 系统每秒处理的字节数。

每当读者见到领头人选举、关键共享状态,或者分布式锁的时候,一定要想起分布式共识:任何其他的方案都是系统中的一枚定时炸弹,随时可能爆炸。

短语

  • 领头人选举(leader election)

  • 多主复制(multimaster replication)

  • 谣言协议(gossip)

  • 异步式分布式共识(asynchronous distributed consensus)

  • 拜占庭式(Byzantine)

  • 崩溃不可恢复(crash-fail)

  • 崩溃可恢复(crash-recover)

  • 角斗士(dueling proposers)

  • 提案(proposal)

  • 提案者(proposer)

  • 接收者(acceptor)

  • 法定租约(qurom leases)

  • 故障域(failure domain)

第24章 分布式周期性任务系统

分布式Cron系统的核心是Paxos协议,一个在分布式不可靠系统达成一致的算法。Paxos协议的使用,以及对大型、分布式环境中任务失败模式的分析使得Google可以构建出一个非常可靠的Cron服务。

惊群效应(thundering herd):根据用户的配置,Cron服务可能会造成数据中心用量的大幅增加。当人们想要配置一个“每日任务”时,他们通常会配置该任务在午夜时运行。这个配置可能在一台机器上还可行,但是当你的任务会产生数千个MapReduce工作进程的时候就不可行了。尤其是当其他30个团队也按照同样的配置在同一个数据中心来运行每日任务呢?

短语

  • 惊群效应(thundering herd)

第25章 数据处理流水线

本章提出了一个崭新的、更可靠的、更易扩展的领头人-追随者模式,可以用来替代处理海量数据的周期性流水线模型

惊群效应

对一个足够大的周期性流水线来说,每一次运行,可能有几千个工作进程立即启动。如果一个服务器上有太多工作进程,或者这些工作进程配置错误,并用错误的方法重试等,可能会导致所运行的物理服务器过载,甚至于分布式集群服务也会过载,网络基础设施等也会出现问题。

摩尔负载模式

该问题是指两个或者更多的流水线任务同时运行时,某些执行过程重叠,导致它们同时消耗某个共享资源。这个问题也会在持续性流水线中出现,但是在负载比较平衡的时候就很少出现了。

Workflow为正确性提供了4点保障:

  • 配置文件本身作为屏障,这样可以保障工作进程的输出永远与配置一致。
  • 所有的工作结果都必须由当前拥有租约的进程提交。
  • 工作进程的输出文件都是全局唯一命名的。
  • 客户端和服务器会在每次操作的时候校验“主任务”的令牌。

短语

  • 数据流水线(data pipeline)
  • 协程(coroutine)
  • DTSS通信文件
  • UNIX管道
  • ETL 管道
  • “简单的,单相流水线”(simple,one-phase pipeline)
  • 多相流水线(multiphase pipeline)
  • 深度(depth)
  • “令人羞愧的并发性”(embarrassingly parallel)
  • 挤走(preemption)
  • “摩尔负载模式”(Moiré Load Pattern)
  • “流式系统”(system prevalence)

第26章 数据完整性:读写一致

大多数云计算应用都是优化以下5项的某种组合:在线时间、延迟、规模、创新速度和隐私。

备份与存档最重要的区别就是,备份是可以直接被应用程序重新加载的。

存档的目的是将数据长时间安全保存,以满足审核、取证和合规要求。在这些情况下的数据恢复通常不需要满足服务的在线率要求。例如,我们可能需要将财务交易数据保存七年时间。为了达到这个目标,我们可以每个月将累积的审核日志迁移到离线的、异地的长期存档存储系统上。读取和恢复这样的信息可能需要一周时间,在长达一个月的财务审核过程中,这是可以接受的。

相对存档,当灾难来临的时候,数据必须从真实的备份中快速恢复,最好能维持服务在线率的要求。否则的话,受影响的用户将由于数据问题无法使用应用程序,直到数据恢复过程完成。

云计算环境下的需求

云计算环境引入了一系列独特的技术难点:

  • 如果该环境使用了混合交易型和非交易型的备份和恢复方案,那么最终恢复的数据不一定是正确的。
  • 如果某个服务必须在不停机的情况下更新,那么不同版本的逻辑可能同时并行操作数据。
  • 如果所有其他有交互关系的服务不是同步更新的,那么在更新过程中各服务的不同版本之间可能会有多种组合,那么就更加增大了数据意外丢失和损坏发生的概率。

API又必须可靠,以及支持以下特性:

  • 数据本地性和缓存。
  • 本地和全局的数据分布。
  • 强一致性与/或最终一致性。
  • 数据持久性、备份与灾难恢复。

Google SRE保障数据完整性的手段

第一层是软删除(soft deletion)(或者是某些API提供的“懒删除”机制)。

这种类型的保护在实践中被证实是针对意外数据删除的有效手段。

  • 应用中实现一个回收站机制,作为用户错误的主要防护手段。
  • 软删除机制是针对开发者错误的主要防范手段,以及用户错误的次要防范手段。
  • 在面向开发者的服务中,懒删除机制是针对内部开发者错误的主要防范手段,是针对外部开发者错误的次要防范手段。

第二层,是备份和对应的恢复机制。

  • 使用哪种备份和还原方法。
  • 通过全量或者增量备份建立恢复点(restore point)的频率。
  • 备份的存储位置。
  • 备份的保留时间。

第三层:早期预警。

数据完整性的挑战:

  • 不同的数据存储之间的引用完整性。
  • 数据结构的改变。
  • 旧代码。
  • 无停机时间的数据迁移。
  • 与其他服务接口的不断变化。

一个有效的带外数据校验系统需要下列元素:

  • 校验任务的管理。
  • 监控、报警和监控台页面。
  • 限速功能。
  • 调试排错工具。
  • 生产应急应对手册。
  • 校验器容易使用的数据校验API。

在这三层中,如果同时还有复制机制,那么在某些场景下对数据恢复是很有用的(但是数据恢复计划不应该依赖于复制机制)。

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

只有真正进行恢复操作之后,才能确定到底能否恢复最新数据。

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

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

短语

  • 数据完整性(data integrity)
  • 可用率(availability)
  • “引用完整性”(referential integrity)

关注公众号获得更多精彩文章

公众号:程序员大兵