Cloudflare 11-18 宕机事故深度复盘:当数据库变更击穿全球网关 Verified

版权所有 © [2025 - 2026] Vannik 未经作者授权,禁止转载 | ⛓ 已链上存证
Bitcoin Timestamp Verified BTC Verified
Doc Map
  1. 影响分析 (Impact analysis)
  2. 事故时间线 (Timeline)
  3. 根因深度分析 (Technical Root Cause)
  4. 响应与恢复过程 (Response and recovery process)
  5. 经验教训总结 (Lessons Learned)
  6. 结语 (End)

2025 年 11 月 18 日,Cloudflare 遭遇了一次严重的全球性服务中断。事故导致全球大量依赖 Cloudflare 代理的服务(包括 ChatGPT, Spotify 等)出现 HTTP 5xx 错误,核心服务如 Workers KV、Access 和 Dashboard 均受到波及。

一句话定性:这是一起典型的配置变更引发的蝴蝶效应。一个旨在加强数据库安全的权限变更,意外导致配置文件体积翻倍,进而触发了边缘节点代理服务(Proxy)中的硬编码内存限制,导致核心进程陷入无限崩溃循环(Crash Loop)。官方已确认并非外部网络攻击。

影响分析 (Impact analysis)

服务 / 产品影响描述
核心 CDN & 安全服务HTTP 5xx 错误,大量请求失败。 (The Cloudflare Blog)
Bot Management配置文件崩溃,导致 bot 检测能力受损。
Turnstile (挑战 /验证)Turnstile 无法加载。 (The Cloudflare Blog)
Workers KVKV 请求失败 (“front end” 门户的服务请求返回 5xx),因为其依赖核心代理。 (The Cloudflare Blog)
Dashboard虽然主面板部分可用,但用户登录失败,因为 Turnstile 不可用。 (The Cloudflare Blog)
Access (身份验证)大量认证失败,尝试登录的请求返回错误页面。 (The Cloudflare Blog)
Email 安全邮件服务受限 — 部分 IP 声誉源暂时不可用,一些垃圾邮件检测准确度下降,但没有重大信息丢失或破坏。 (The Cloudflare Blog)

此外,恢复期间还观察到延迟 (latency) 上升,部分因为调试和可观测性系统 (observability) 在捕获错误时占用了大量 CPU。

事故时间线 (Timeline)

所有时间均为 UTC

  • 11:05 - 工程师开始部署一项针对 ClickHouse 数据库集群的权限控制变更 (ACL Change)。
  • 11:20 - 变更生效。此时,用于生成 Bot 管理配置文件的后端查询开始返回重复的元数据(同时返回了 default 和 r0 两个数据库的元数据)。
  • 11:25 - Bot Management 系统按计划生成了新的“Feature 配置文件”。由于数据重复,文件体积意外翻倍。
  • 11:30 - [故障爆发] 新的配置文件被推送到全球边缘节点。核心代理进程(Rust 编写)在加载该文件时,因超出硬编码的条目限制(~200条)触发 Panic。看门狗机制尝试重启进程,导致全球节点陷入崩溃-重启循环。
  • 11:35 - 监控系统检测到流量异常,SRE 团队介入。初步误判为大规模 DDoS 攻击(因大量 5xx 错误和流量特征)。
  • 13:05 - 确认根因并非攻击,而是内部配置问题。
  • 13:37 - 定位到 Bot Management 配置文件异常。
  • 14:24 - 停止生成新文件,并强制回滚至“最后已知正常(Last Known Good)”版本。
  • 14:30 - [核心恢复] 代理服务成功加载旧版配置,全球核心流量开始恢复。
  • 17:06 - 所有积压队列处理完毕,附属服务(如 Email Security、日志分析)完全恢复。

根因深度分析 (Technical Root Cause)

这次事故是三个独立因素完美“对齐”造成的系统性崩溃:

第一张骨牌:数据库查询的“幽灵数据”

变更本身是善意的(为了收紧权限)。然而,后端 SQL 查询并未显式指定 WHERE database = 'default'。在权限变更前,系统默认只看得到 default;变更后,系统意外“看”到了 r0 副本。 结果:查询结果集包含了两份完全一样的数据。

放大器:缺乏校验的配置生成

Bot Management 系统负责将上述 SQL 结果序列化为二进制的 Feature 文件。 失误:生成脚本缺乏输入验证(Input Validation)。它没有检查生成的条目数量是否在“安全阈值”内,也没有对比新旧文件的大小差异,就直接将其标记为“可用”并推送到生产环境。

致命一击:脆弱的代码假设 (Rust Panic)

Cloudflare 的边缘代理使用 Rust 编写。代码中存在一个关于 Feature 条目数的硬编码上限 (Hard-coded Limit)。 当程序读取这个“翻倍”的文件时:

  • 读取逻辑返回了一个 Error。
  • 错误处理逻辑(或缺乏处理)直接导致了主线程 Panic(崩溃)。由于 Cloudflare 采用无共享架构,所有服务器运行相同的代码和配置,因此它们几乎在同一秒全部崩溃。

响应与恢复过程 (Response and recovery process)

  • 检测与初期响应
    • Cloudflare 在 11:35 UTC 创建了 incident call (事件响应会议) 来集中处理问题。
    • 起初,他们误判症状可能是大规模 DDoS (超大流量攻击),因为错误非常广泛且看起来像服务被压垮。
  • 调查与根因确认
    • 工程团队迅速定位到 Bot Management 模块生成配置文件的问题。
    • 停止 propagating (传播) 新的 feature 文件。
    • 回滚到稳定版本,并重启关键代理。
  • 恢复
    • 截至约 14:30,核心流量大致恢复。
    • 后续几个小时内,各种服务 (代理、KV、Access 等) 完全恢复。
    • 监控团队持续观察,确保没有残留故障。

经验教训总结 (Lessons Learned)

对于 SRE 和架构师而言,这次事故提供了极具价值的参考:

  • 所有的输入都是邪恶的 (All Input is Evil)
    • 永远不要信任配置文件,即使它们是由内部系统生成的。
    • 改进:在配置加载器中必须包含严格的边界检查。如果配置超限,应记录错误并回退到旧配置或进入安全模式,而不是让整个进程崩溃(Graceful Degradation)。
  • 金丝雀发布的颗粒度
    • 虽然 Cloudflare 有分批发布机制,但这次故障传播速度快于监控系统的反馈速度。
    • 改进:配置文件的变更应视为代码变更,必须经过 Staging 环境的自动化测试(包括文件大小、格式校验)。
  • 避免“硬编码”限制
    • 在分布式系统中,硬编码的缓冲区大小或条目限制是定时炸弹。
    • 改进:随着业务增长,这些限制必须动态化,或者至少要有显式的监控报警,在接近阈值时提醒开发人员。
  • 数据库变更的可见性
    • 数据库权限变更往往被视为“低风险”运维操作,但实际上它改变了应用层的数据视图。
    • 改进:任何改变数据“形状”或“可见性”的变更,都应进行全链路的回归测试。

结语 (End)

Cloudflare 的这次宕机再次提醒我们,系统的坚固程度取决于最脆弱的那个假设。即便拥有世界级的防御网络,一行未被捕获的 SQL 重复数据加上一个简单的 .unwrap(),依然足以让半个互联网瘫痪。

作为技术人员,我们需要在追求高性能(Rust/C++)的同时,始终保持对“防御性编程”的敬畏。


附录:

Cloudflare restores services after outage impacts thousands of internet users
Cloudflare resolves outage that impacted thousands, ChatGPT, X and more
Cloudflare outage on November 18, 2025
Cisco ThousandEyes 分析报告

投喂·结缘
WeChatPay
WeChat 长按或识别
AliPay
Ali 长按或识别