English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

访客
1 / ?
返回课程列表

值得了解的三种失效概念

欢迎

分布式系统在模式上失败。学会这些模式后,每个postmortem都将成为一种认可练习而不是谜。

关注的内容覆盖了生产故障分析的大部分:

单点故障(SPOF):一个组件的故障会导致整个系统停机。通常隐藏:所有人都依赖的DNS服务器;所有东西都续订的证书;所有的数据库主节点。

级联故障:一个组件的故障触发另一个组件的故障,然后触发另一个组件的故障。一个慢数据库会导致API层面的超时,然后导致重试,然后导致数据库更慢,然后导致更多的超时。爆炸蔓延。

爆炸半径:一个部分失败时,整个系统中会有多少部分停机。架构选择有助于限制或扩大半径。一个SPOF具有无限的爆炸半径。一个防护墙服务具有有限的半径。

在本课结束时,你将:

- 通过检查识别出架构中的SPOF

- 认识到级联故障模式:雷鸣式群起,重试风暴,队列死亡

- 阅读一个真实的时间线并将触发器与触发器暴露的潜在缺陷分开

- 编写无责的行动项目,目标是系统而不是人,覆盖预防/检测/恢复

- 对防护墙和电路断开器作为限制爆炸半径的工具进行推理

发现单点故障

层次化架构检查

考虑一个小型的Web架构:

- DNS: api.example.com -> 一个DNS提供商托管的单个IP地址 203.0.113.10

- CDN: 在 api.example.com 前面有一个单一CDN供应商

- 入口: 一个负载均衡器后面有两个反向代理机器

- 后端: 在两个可用性区域内的六个API副本(每个区域三个)

- 数据库: 在同一个可用性区域内的主数据库和一个只读副本

- 缓存: Redis集群,三个节点分布在两个可用性区域内

问题:哪些组件是SPOF?提示: SPOF不一定是显而易见的'单个机器'类型。一个所有机器都在一个可用性区域内的三台机器是一个区域故障的SPOF。

在这个架构中至少识别出三个SPOF。对于每个SPOF,都要说明当它失败时会发生什么,并提出一个具体的更改,将SPOF移除(不重写应用程序)

三种经典级联模式

故障通过依赖关系传播

模式1:雷阵雨。 一个共享资源(缓存、锁定、数据库)失败或重启。依赖于它的每个客户端都会同时重试。重试洪流压倒了重新上线的任何部分;重试比恢复可以吸收的速度更快;恢复永远无法完成。

模式2:重试风暴。 下游服务运行速度变慢。上游调用者,而不是失败,会重试。重试将原始负载乘以多倍。慢服务变慢,触发更多重试。最终负载超过了服务的健康版本。

模式3:死亡队列。 无背压的处理队列收到比它处理得更快的输入。队列无限增长。内存耗尽;消费者崩溃;重新启动;发现一个更大的队列;再次崩溃。

共同点: 一小的初始扰动触发了正反馈循环。系统的响应加剧了失败,而不是减轻它。

减小机制

指数退避加随机波动。 等待重试的客户端每次等待的时间越来越长,且有随机偏差。防止同步重试波浪。

断路器。 调用者跟踪下游故障率。超过阈值,调用者停止调用,进入cool down期,并立即失败自己的请求。防止浪费工作,让下游恢复。

舱壁。 为依赖分隔资源。数据库连接池A,缓存连接池B。慢数据库无法挤压所有连接;缓存调用继续。

负载削减。 当过载时,在边缘放弃请求,而不是接受并缓慢失败。1毫秒内的429比30秒内的500好。

背压。 当消费者无法跟上时,慢生产者。队列变为有限;发送者阻塞;原始工作来源感受到摩擦。

级联故障:触发器->放大->坍塌,减小机制

诊断级联

团队的API层在数据库故障转移期间发生熔断。时间线:

- 14:00:00 — 操作员将备库提升为主库。预期不可用时间约为10秒。

- 14:00:08 — 主库不可用。API层开始出现数据库连接错误的请求失败。

- 14:00:08 — API层重试(默认配置:5次重试,无延迟,100ms之间)。

- 14:00:11 — 备库被提升,开始接受新的连接。

- 14:00:11 — API层同时打开数千个新的数据库连接(每个副本×每个并发请求×每次重试)。

- 14:00:13 — 新主库的连接池已用尽;新连接被拒绝。

- 14:00:13-14:05:00 — API层副本用尽连接池,抛出异常,崩溃,重启,重复。

- 14:05:00 — 操作员手动停止API层流量;数据库稳定。

- 14:10:00 — 渐进式流量恢复完成。总停机时间:约10分钟(预期为10秒)。

识别正在发生的级联模式,将会阻止它的减振机制至少有两个,并解释为什么从主库切换到备库(预期为10秒的停机)反而导致了10分钟的停机。

DNS SERVFAIL: 两种复合缺陷

真实形状的后勤报告

以下是真实事件的清洁版本。供应商名称更改,IP地址匿名;形状,时间线和教训是真实的。

摘要

网站example.com从所有公共DNS解析器返回SERVFAIL,大约3-4小时。同一DNS主服务器上的其他46个区段未受影响。根因:两种复合缺陷。

1. 供应商A(一个辅助DNS提供商)添加了一个不在主库的allow-axfr-ips白名单中的新内部同步IP。

2. example.com区段有一年多的RFC违反CNAME冲突(demo.example.com在同一标签下有CNAME和MX/TXT记录)导致供应商A拒绝该区段进行新鲜AXFR。

时间线(UTC)

- ~15:00 — 供应商A将新同步IP198.51.100.42添加到他们的基础设施中

- 15:02 — first AXFR-out denied for 198.51.100.42 appears in primary DNS logs (no alerting on this signal)

- ~18:00 — SOA expire window reached; Vendor A drops example.com zone from cache

- ~18:30 — SERVFAIL detected externally

- ~19:45 — root cause identified

- 20:00 — 198.51.100.42 added to allow-axfr-ips; primary restarted

- 20:05 — NOTIFY sent; AXFR initiated; zone STILL SERVFAIL (CNAME conflict)

- 20:07 — check-zone reveals 1 error: CNAME conflict on demo.example.com

- 20:09 — CNAME replaced with A record; zone check clean (0 errors)

- 20:10 — NOTIFY sent; AXFR completes; Vendor A begins serving zone

- 20:11 — dig @8.8.8.8 example.com A returns correct IP — RESOLVED

为什么只有 example.com?

所有 47 个域名共享相同的 DNS 主服务器。受影响的 AXFR IP 块影响了所有域名。但只有 example.com 有 CNAME 冲突,并且只有在拒绝执行时,这个域名需要新的 AXFR。其他域名在拒绝执行之前已经刷新了,或者还没有需要刷新。

潜在缺陷

demo.example.com 的 CNAME 冲突已经存在了几年。它可以正常工作是因为主服务器从它的数据库中提供了域名(宽松处理 RFC 违规)以及 Vendor A 从在违规被引入之前从主服务器缓存中获取的陈旧数据。 当 Vendor A 清空了缓存并需要最新数据时,违规才暴露出来。

触发

Vendor A 静默添加了一个新的同步 IP。主服务器的 allowlist 中没有包含它。 AXFR 被拒绝。三小时后(SOA 过期),Vendor A 清空了域名。潜在缺陷在系统尝试恢复时暴露出来。

编写无责行动项

无责:目标系统,不是人

无责行动项目描述的是系统应该如何不同,而不是人应该如何不同。'培训操作员' 是有责的。'在部署之前添加一个自动检查以捕获到这个问题' 是无责的。

好的无责行动项目可以分为三个维度:

- 预防:使坏事变得困难或不可能

- 检测:如果发生了,提前发现

- 恢复:在发生时限制损失

每个项目应该命名(1)具体的系统变更,(2)负责的团队,以及(3)它服务的维度。

在上述 DNS-SERVFAIL 后事查证中,编写三项无责行动项。将它们分配到预防 / 检测 / 恢复(每个维度一个)中。每个项目必须命名一个具体的系统变更和负责的团队。不要将任何人作为原因进行指责。

不会让整个船沉没的隔舱

从海洋工程借鉴

船舶装有密封隔舱:垂直的墙壁将船舶的船壳分成隔舱。一个隔舱可以发生水淹没有让整艘船沉没;另一个可以失败不会影响其他部分。

分布式系统借用了同一个词和同一个想法。

隔舱模式:根据依赖关系分隔资源。一个服务调用三个下游API使用三个独立的连接池、三个独立的线程池、三个独立的重试预算。一个慢或者失败的下游不能消耗其他两个的资源。

没有隔舱:一个慢的依赖耗尽了共享的线程池;对其他依赖的调用阻塞等待线程;整个服务变得不响应。

有隔舱:一个慢的依赖耗尽了自己的池子;对它的调用快速失败;对其他依赖的调用继续正常;爆炸半径局限在失败的依赖。

集电器断路器

断路器模式:一个对下游依赖的有状态包装,记录故障率。有三个状态:

- 关闭(正常):调用通过。失败次数计算。

- 打开(触发):在过去的30秒内超过故障阈值(比如说50%的故障),断路器打开。调用立即失败,不尝试依赖。避免调用者浪费工作;避免依赖持续接受负载而不健康。

- 半打开(测试):在冷却期间,断路器允许少量的调用通过。如果它们成功,它们关闭回到正常。如果它们失败,它们重新打开另一个冷却。

关键见解:断路器防止在已知不健康期间浪费努力,并给下游一个恢复的机会而不持续负载。

隔舱控制爆炸半径。断路器防止爆炸持续。

控制爆炸半径

您的 API 服务调用了四个下游服务:用户服务、推荐服务、通知服务和第三方支付 API。该团队听说'Recommendation Service'(推荐服务)有时运行不稳定,并希望确保在这种情况下,整个系统都能保持健康。

目前,该服务使用一个共享的 200 个线程的线程池和一个共享的 HTTP 连接池。所有四个下游服务都在竞争这些资源。没有断路器。

为这个API服务提出一个隔舱+断路器设计。具体来说:如何将四个依赖关系的线程/连接池分区,什么断路器阈值对于波动较大的推荐服务是合适的,以及当推荐服务处于打开状态时,用户面向API应该做什么?

设计故障模式审查

合成

您学会了通过检查识别 SPOFs,识别级联故障模式,阅读后事mortem时分离触发器与潜在缺陷,编写无责行动项,跨防御/检测/恢复,使用隔离桶+断路器+优雅退化来限定爆炸半径。

应用所有五个。

您的团队将上线一个新的服务 search.example.com,该服务依赖于三个下游服务:一个主要的搜索索引服务(index.example.com),一个分析服务(analytics.example.com)以及一个推荐服务(recs.example.com)。团队希望您在上线前领导一个'故障模式审查'。

概述您将领导的故障模式审查。包括:如何表面 SPOFs(一种技术),如何防止搜索服务与其三个下游服务之间的级联故障(两种模式),为推荐服务(团队标记为最不靠谱的)制定一个具体的行动项,以及在上线时需要的监控。

课程下一步

下一步课程

您现在可以识别 SPOF,识别级联,阅读后事mortem时有益于阅读,编写无责行动项,通过设计限定爆炸半径。

本课程的最后一课 (cs_distsys_observability_and_capacity) 教授了如何衡量,以便在用户发现问题之前发现问题。健康检查、版本端点、代理层的四个黄金信号,以及如何将应对洪峰容量的决策与观察到的数据联系起来。

配套课程geometry_of_failure_modes_and_blast_radius 探讨了中介中心性(哪个图节点是瓶颈)和最小切割(爆炸半径的上界)。

好样的。继续前进。