将近六个月,一切看起来都正常。危险恰恰藏在这段漫长的平静里。
一个 Web 应用使用 Amazon EFS 作为共享存储。多个应用实例访问同一批文件,部署简单,也没有明显迹象让人怀疑这套架构。
后来,网站似乎毫无预兆地慢了下来,最后几乎无法响应。服务器负载异常升高,请求超时,应用进程不断积压。
乍看像是计算资源不够,真正的原因却是 EFS 突发积分耗尽。
为什么故障看起来像 CPU 问题
Linux 平均负载过高,是最容易误导人的现象之一。平均负载高,不一定意味着 CPU 已经跑满;等待存储 I/O、处于不可中断状态的进程,也会计入其中。
应用的工作进程在等待 EFS 完成操作,新请求却继续进入。越来越多的进程被阻塞,请求队列也越来越长:
- EFS 吞吐量受到限制。
- 文件操作花费了更长的时间。
- 工作进程被占住,等待 I/O。
- 新请求持续积压。
- 系统负载增加。
- 该网站最终几乎不可用。
单纯增加 CPU,并不能解决这个根本问题。
为什么六个月后才出问题
当时,EFS 文件系统使用的是 突发吞吐量 模式。可持续的基准吞吐量与 EFS Standard 中存储的数据量有关。负载低于基准时积累积分,高于基准时消耗积分。
对存储量较小的文件系统,这很容易造成错觉:
- 存储量不大,因此可持续的基准吞吐量较低。
- 现有的积分余额暂时允许更高的吞吐量。
- 即使长期负载超过基准,应用暂时也能正常运行。
- 只要还有积分,这种不匹配就不容易被察觉。
- 当余额达到零时,性能会急剧下降。
这次,积累的积分大约支撑了六个月。架构可持续提供的吞吐量其实早已不足,只是被余额掩盖了。这不是突发的新问题,而是缓慢累积的容量缺口——一颗延迟六个月才引爆的定时炸弹。
临时突发能力,不等于可以长期依赖的吞吐量。
选择适当的 EFS 吞吐量模式
突发吞吐量
如果吞吐量需求随存储量自然增长,而且高负载之后有低负载时段补回积分,突发模式就有用。但它需要持续监控。积分不断下降,说明负载在这种模式下无法长期维持。
弹性吞吐量
面对难以预测或波动较大的需求,弹性吞吐量往往更合适。它随负载自动扩展,不依赖有限的突发积分,因而减少了一种故障风险。不过,持续高吞吐量场景仍需仔细评估按量计费的费用。
预置吞吐量
当吞吐量需求明确,且高于现有存储量所能提供的基准时,可以考虑预置模式。它让容量不再受文件系统大小限制,但需求变化后仍要复核配置。
还要注意区分 EFS 的 吞吐量模式 和 性能模式。前者决定如何提供吞吐容量,后者涉及延迟与并行度等特征。
当时本该具备的监控
对突发吞吐量模式的 EFS,最重要的 CloudWatch 指标是 BurstCreditBalance。此外,还应监控:
BurstCreditBalance识别积分是否持续耗尽PercentIOLimit确定文件系统何时接近其 I/O 限制PermittedThroughput了解当前的可用吞吐量MeteredIOBytes和TotalIOBytes观察 I/O 消耗- 应用程序延迟、请求持续时间、超时和错误率
- 结合系统负载与实际 CPU 利用率,查看等待 I/O 的进程
只看 CPU、内存和 HTTP 状态码还不够。共享存储是关键的应用依赖,应放进整个服务的监控中,而不能只当作独立的基础设施组件。
告警应该在余额归零前触发,更要关注趋势。即使绝对余额看起来还不少,只要它数周持续下降,就已经说明存在结构性的吞吐量缺口。
如果再来一次,我会怎么做
- 估算设计阶段的可持续吞吐量。
- 根据实际访问模式选择吞吐量模式。
- 把突发积分当作临时缓冲,而不是正常容量。
- 针对积分余额和 I/O 限额创建 CloudWatch 警报。
- 监控积分消耗速度。
- 在负载测试中包括 EFS 吞吐量和延迟。
- 对照 Linux 平均负载、CPU 利用率与 I/O 等待。
- 检查高频访问的文件,是否确实需要放在共享网络存储上。
- 在存储限制和延迟增加的情况下测试应用程序的行为。
- 记录切换吞吐量模式的操作流程。
最后想说
EFS 可以是很好的共享存储方案,但实际表现取决于吞吐量模式和负载特征。
这次事件的危险,不只是性能下降,而是问题暴露前系统已平稳运行了六个月。积分把故障推迟得足够久,让人误以为这套容量设计能够长期支撑负载。
更好的办法不是单纯攒更多积分,而是选择合适的模式、算清可持续吞吐量,并在用户察觉之前,从指标中发现不断扩大的缺口。


云 / 系统