原文: blog.cloudflare.com/18-november…
基于这篇Cloudflare的事故复盘报告,概括如下:
1. 事件定性:非安全攻击,而是变更引发的系统崩溃
- 性质: 这是一次严重的全球性故障(Cloudflare自2019年以来最严重的一次),导致核心网络流量中断。
- 误判: 初期因症状类似(流量波动、状态页恰好不可用),团队误认为是超大规模DDoS攻击,但最终确认为内部系统问题。
2. 核心根因:蝴蝶效应(权限变更 -> 数据重复 -> 内存溢出)
故障链条非常清晰,展示了微小变更如何导致系统级雪崩:
- 起因(数据库层): 运维团队对ClickHouse集群进行权限变更,本意是允许用户显式查看底层表元数据。
- 触发(数据层): 一个用于生成“Bot管理特征文件”的SQL查询逻辑存在缺陷(未过滤数据库名)。权限变更后,该查询返回了重复的列数据(包含
default库和底层的r0库),导致生成的配置文件内容体积翻倍。 - 崩溃(代码层): 核心代理服务(FL2,Rust编写)读取该配置文件时,由于为了性能优化预设了内存分配上限(限制200个特征,而坏文件超过了此限制),导致代码触发Panic(崩溃),抛出 HTTP 5xx 错误。
3. 故障排查难点:间歇性“震荡”掩盖了真相
- 现象: 故障并非一直持续,而是呈现“时好时坏”的震荡状态。
- 原因: 数据库集群是分批更新的。只有查询命中已更新权限的节点时,才会生成坏文件;命中未更新节点则生成好文件。
- 后果: 这种不确定性极大地干扰了故障定位,让团队误以为是攻击流量导致的系统不稳。
4. 级联影响范围
- 核心受损: 核心CDN服务、安全服务直接返回5xx错误。
- 下游受损: 依赖核心代理服务的内部组件(如 Workers KV、Access、Turnstile、Dashboard)随之崩溃或无法登录。
5. 改进措施与教训
Cloudflare提出的整改方向主要集中在防御性编程和容错机制上:
- 信任边界: 将内部生成的配置文件视为“不可信的用户输入”,加强校验和硬化处理(不能假设内部生成的文件永远正确)。
- 熔断机制: 增加全局性的功能开关(Kill Switches)。
- 资源保护: 优化错误处理逻辑,防止Core dump或错误报告本身消耗过多资源导致系统死锁(故障期间调试系统的高CPU占用加剧了延迟)。
一句话总结: 一次旨在优化数据库权限的常规变更,因SQL查询逻辑不严谨,导致生成的配置文件体积超出代码硬编码的内存限制,进而引发全球核心代理服务崩溃。教训在于:内部配置的输入处理应与外部输入同等严谨,且系统需具备更强的容错与降级能力。