一个“空值”引爆。。。谷歌云。。。全球崩溃

2025 年 6 月 14 日,谷歌云发布了《突发!谷歌云、Cloudflare 崩了。。。全球互联网服务大瘫痪!》、《引爆谷歌云。。。瞬间封锁全球流量。。。到处 503 。。。根因系统自动推送「无效配额」。。。》的故障报告。
 
谷歌和谷歌云 API 通过谷歌 API 管理和控制平面提供服务。
这些管理和控制平面区域分布,负责确保每个传入的 API 请求都经过授权,并策略和适当的检查机制比如配额)满足端点。
作为该策略检查系统一部分的核心二进制组件名为 Service Control服务控制Service Control 是一项区域性服务,它有区域数据存储区,可以从中读取配额和策略信息。数据存储区的元数据几乎立即在全球范围内加以复制,为谷歌云和我们客户管理配额策略。

2025 年 月 29 日,Service Control 添加了一项新功能,用于进行额外的配额策略检查。代码变更和二进制版本通过区域部署分发到了我们的区域,但由于需要触发代码的策略变更,因此在部署过程中从未执行过失败的代码路径。

为了安全起见,代码变更附带一个红按钮red button,用于关闭这条特定策略服务路径。

变更的问题在于它没有适当的错误处理机制,也没有功能标志保护。

由于没有适当的错误处理机制,空指针导致二进制代码崩溃。

功能标志用于逐步按项目逐个区域启用该功能,从内部项目开始,以便我们能够发现问题。如果该功能已受功能标志保护,该问题原本试运行(staging阶段就被发现。

2025 年 月 12 日太平洋夏令时间上午 10 点 45 左右,一策略变更被插入到 Service Control 用于策略的区域 Spanner 表中。

鉴于配额管理的全局性,元数据在几秒钟内就被复制到了全球。

策略数据包含意外的空白字段。

Service Control 随后对每个区域数据存储中的策略执行了区域配额检查。

这为这个相应的策略变更引入空白字段,并执行命中空指针的代码路径,导致二进制代码陷入崩溃循环。

鉴于每个区域部署,这变成了全球问题

 2 分钟内,我们的站点可靠性工程团队对事件进行了排查分类。

 10 分钟内,我们确定了根本原因,并部署了红按钮(用于禁用服务路径)。

红按钮在事件发生后约 25 分钟内即可启用。

在事件发生后的 40 分钟内,红按钮启用完成,我们开始看到各区域逐渐恢复,较小的区域开始恢复

在我们的一些较大的区域(比如us-central-1),随着 Service Control 任务重新启动,它对其依赖的底层基础设施(即Spanner表)产生了群体效应,导致基础设施过载。

Service Control 没有实施适当的随机指数退避机制来避免这种情况。

由于我们任务创建作了限制,以最大程度地减对底层基础设施的影响,并将流量路由到多区域数据库以降低负载,因此在 us-central-1 整整耗时 2 小时 40 分钟完全解决问题。

此时,所有区域的 Service Control  API 服务已完全恢复。

相应的谷歌谷歌云产品也开始恢复,但部分产品需要更长时间,长短取决于其架构。

故障简要逻辑:

(1)一个新功能上线了(2025 年 5 月 29 日),用于检查 API 的配额策略,但这段代码在测试时没有真的被触发过,所以也就没发现里面有“空指针”问题(即程序里有个地方该填的数据没填,结果让程序崩溃了)。

(2)6 月 12 日 10:45 AM 左右,Google 向全球推送了一条新的“配额政策”数据。这个数据里意外包含了空值(空字段),结果引发之前那段“未曾被触发的故障代码”,让整个系统陷入“崩溃循环”(crash loop)。

(3)因为这套系统是全球部署的,所以这个崩溃瞬间波及全球,所有地区都出现了故障。