回复架构师获取资源
大家好,我是你们的朋友架构君,一个会写代码吟诗的架构师。
在软件开发的世界中,一个看似微不足道的细节往往能够引发连锁反应,最终导致严重的生产问题。本文作者就讲述了一个这样的故事:在一个大型项目中,仅因 8 个字节的请求体大小限制,导致整个生产环境崩溃。
原文链接:https://hetarth02.hashnode.dev/how-8-bytes-broke-production
前阵子我接到了一个项目,这个项目属于教育技术领域,许多大公司都在使用,我们姑且称之为“革命性教育知识技术(Revolutionary Education Knowledge Technology)”,简称“REKT”。
关于 REKT 项目,我有许多故事想与大家分享,但还是先从这个故事开始吧:一个关于区区 8 个字节如何导致整个生产环境崩溃的故事。
背景介绍
那是计划进行生产部署的前一天,如果我没记错的话,也是我正在玩的一款游戏进行重大内容更新的前一天。我无法详细介绍项目的实际细节,但可以简单概括一下:你可以把这个项目想象成一个类似于 Udemy(一个在线学习和教学市场)的平台,但它提供的是针对企业的专有领域内容。在 REKT 项目中,我负责实现许多功能,其中之一就是创建一个管理/内容管理面板来管理教育模块——就像在 Udemy 中,一个模块包含许多讲座和内容供用户选择,REKT 也是如此。
明确一点,我并不是这个项目的第一批开发人员。在我之前,有一组开发人员已经搭建了整个“架构”,并为 REKT 选择了技术栈。我认为,掌握基础知识比学习特定语言更为重要,因为对我来说,解决问题的方法过程比知道解决问题的语法更重要。举个简单的例子,如果我需要重复做某件事情,我就会想到用循环。在这个例子中,循环就是解决问题的方法,至于如何具体实现循环,则因不同的编程语言而异。
// For loop in javascript
for (let i = 0; i < 10; i++) {
console.log(i);
}
# For loop in python
for x in range(0, 10):
print(x)
// For loop in PHP
for ($x = 0; $x <= 10; $x++) {
echo $x;
}
因此,我更注重培养解决问题的思维方式,而不是专注于解决问题的特定语言或语法。
言归正传,当时我对前端、后端和服务器从 MVC 和单体架构的角度有一些了解。幸运的是,我通过 SvelteJs 学到了组件驱动的前端方面知识,不幸的是,我还没机会深入研究 ReactJs 和 VueJs 等框架。你猜猜 REKT 项目的技术栈是什么......前端使用 ReactJs,后端使用 Fastify,并紧密依赖于 AWS 生态系统;最重要的是,这一切都是无服务器的,由 Terraform 进行管理。
而当时,我对无服务器的了解仅仅是:一旦出现请求,它就会启动一个类似服务器的运行时环境。
此外,关于整个架构的构成以及使用了哪些服务的文档非常少,几乎可以说是没有。
部署顺利,开始大胆玩游戏
我不再过多提及过往,快进到功能部署的那一天。我和质量保证团队对这个功能进行了详尽的测试,几乎覆盖了所有可能的情景。一切看起来都很顺利,在得到经理的批准后,我们当天一早就将其推送到生产环境中。部署完成后,质量保证团队开始在实时环境中进行测试。可能是由于过度自信、天真,也可以说是缺乏经验,我毫无顾虑地安装了我正在玩的游戏更新并开始玩了起来。经理也看到了我在玩游戏,但由于一切都很顺利,他也就没太在意。上午的时间进行得非常顺利,生产环境中的所有功能都在正常工作,我们对其进行了多次验证和测试,并告知客户可以开始使用了。
、
突然搞砸(REKTed)了!
接下来是工作日的后半段。我刚吃完午饭回来,正准备处理其他工单,突然团队聊天里弹出了一条消息:
“保存内容时问题没有添加。” —— 客户
当下,我的脑海中立刻涌上了无数想法:这怎么可能?我们在开发、预发布和生产环境都进行了多次测试,这肯定是个误会。
“可以让他们清除浏览器缓存或强制刷新后再试一次吗?” —— 我
“我们已经在隐身模式下运行了。” —— 客户
我想,这下糟了!我立即打开了开发部署环境并进行检查:我创建了一个课程,添加了一些带有几个问题和选项的内容(测验),点击提交按钮,结果——成功保存了!
我立刻又试了一次,每次都能完美保存。我把我的操作过程录频并发到了团队群里:
“我刚试了一下,它没问题啊。” —— 我
紧接着,客户也发送了一段显示问题的屏幕录制。此时,我和质量保证团队已经在三个不同的环境中逐一尝试了每一种情景,一切都运行得非常好。
时间一分一秒地流逝,尽管我们努力寻找问题所在,但始终没有取得任何突破。为了找出为什么客户无法保存内容的原因,我和质量保证团队甚至还发现了一些其他的重大 Bug。
明明上午都一切顺利,我还有空去玩游戏;但到了那天晚上 11 点,我们都还在找那个 Bug。当时我们太累了,什么都想不起来,就算试着添加一些补丁也都不起作用,我们开始感到绝望。
“我们似乎没有什么进展。今天就到这里吧,明天带着清醒的头脑再来看这个问题。” —— QA
他说得对,我们一整天都在反复做同样的事情,已经陷入了思维定势。
、
得到资深开发者的帮助
就在我们准备结束这一天的工作时,发生了一件有趣的事情,为我最终解决这一系列问题铺平了道路。一位资深开发者在完成自己的工作后还有一些时间,他主动提出帮助我们调试这个问题。我们尽力总结了当前的情况,但实际上连我们自己都不知道为什么内容无法保存(至于其他 Bug,我和 QA 决定先保密!)。
这位资深开发者愿意加班来帮我们,我们既觉得不好意思,又有些许宽慰。他并没有做出什么特别惊人的举动,只是打开了开发工具,让我输入数据并保存,一切都正常保存。接着,他又让我重现客户遇到的问题,我也欣然照做。
“我觉得问题就在这里……” —— 资深开发者
他向我们展示了两种情况下的请求负载大小:
成功情况下:~8,192 字节
失败情况下:~8,200 字节
“不,这不可能是问题所在。” —— 我
“这肯定不是问题,只不过 8KB 而已,一定还有其他原因。” —— 我再次强调
尽管心里这么想,我们还是决定结束这一天的工作。不过,这个线索给了我们一个新方向去思考第二天该如何解决这个问题。
求求了,下次记得写点文档!
我知道大多数情况下,开发者都不愿意写文档。但至少,请花 5 分钟时间考虑一下未来维护你代码的人吧!你真的希望他们写一篇约 2 千字的文章来抱怨因缺少基本文档而感到无比抓狂吗?
好吧,再次言归正传。第二天早上 7 点左右,我们带着焕然一新的心情早早来到办公室。有一件事让我耿耿于怀,那就是 8KB 的负载大小简直是胡扯(没有对那位资深开发者不敬的意思)。于是我尝试在 fastify 中扩展 POST 主体的大小,重新部署了后端,但同样的问题依然存在。
既然如此,我心想先把这个问题放一放,解决一些其他 Bug,以免它们被客户发现。于是,我暂时搁置了这个问题,转而解决了其他 Bug。虽然这些 Bug 也并不容易解决,但它们并不是完全的黑盒,我可以轻松地追踪代码、找到逻辑中的一个小缺陷。就这样,我逐一修复了其他 Bug,虽然主要问题仍未解决,但我终于有了一些希望:有时候,小小的胜利能够改变你的心理状态,调整你的思维方式——对我来说,确实如此。
后来,我又一直在查看前台和后台的错误日志,但没有发现任何错误的迹象。对我来说,这真是一个黑盒,就算我们可以重现 Bug,它也没留下任何可追踪的线索。
换个角度思考
……等等,为什么我没有收到这个特定请求的日志?它明明从前端发出,有时甚至能到达后端。假设 8KB 的问题是对的,这就说明有什么东西阻止了请求到达后端。那会是什么原因呢?我们并没有设置任何服务器...
“我们应该撤销允许所有 CORS 设置的更改,因为那不安全。”——QA
对了!!!安全,安全性,我们是否有针对路由的安全组或任何具有特定规则的东西?我登录 AWS 面板,进入了安全组,但什么也没找到,就连谷歌搜索也没帮上太多忙。然后我突然想到,既然请求没有到达后端,那么肯定有什么东西在保护它。因为是无服务器架构,我认为安全组不太可能是问题所在。那还有什么可能呢?我在谷歌上搜索“AWS 网站安全产品”,然后在传输层/网络层中寻找阻止请求的东西,结果发现它就是这个问题的罪魁祸首......
AWS Web 应用防火墙(WAF)
随后,一切开始变得明朗起来。我搜索了“aws waf 主体大小限制”,并找到了答案。
“我们也使用了 AWS WAF 吗?”—— 我、QA、经理,可能还有客户
不仅我感到意外,整个团队都对此感到惊讶。好在修复这个问题其实非常简单,只需登录 AWS 控制台,更改 WAF 规则以允许更大的请求负载,然后再次测试:终于一切正常了!
不过等我清理了一些代码后重新部署,再次进行了测试……结果又失败了。
什么鬼?!我心想。哦对了,因为整个基础设施是由 Terraform 管理的,所以它重置了 WAF 规则的设置。好吧,暂时先手动更改它,应该就没问题了!至于自动化的问题,就留给未来的我来解决吧。
总结
这一切解决之后,我花了大约一个小时左右的时间,详细记录了整个事件的过程,希望这篇文章可以帮助未来在 REKT 项目中遇到类似问题的任何人。
这些年小编给你分享过的干货
转发在看就是最大的支持❤️