一位读者要求我跟进我很久以前提到的事情。早在 2018 年,我就曾写道,也许我应该写一下“作战室”,以及为什么它们不利于进行深思熟虑的分析工作。
这是我对此事的最佳看法,以反馈的方式,事情有点即兴,我没有提前准备好的声明。准备好?开始了。
我对地狱般的作战室的典型例子是 2014 年 8 月 1 日星期五发生的事情。对于知情人士(就像任何参加过我的新技术员工课程的人)来说,那天是“呼叫警察”的日期 – 一场史诗般的中断,导致整个 Facebook 瘫痪了几个小时。它在内部如此命名是因为洛杉矶县治安官在推特上发布了类似“我们知道它已关闭,这不是执法紧急情况,所以不要再打电话给我们”之类的话。
我在演讲和帖子中多次提到过这一点,其他人也提到过。这不是什么秘密。那天该网站严重崩溃。
实际上,当我等待公交车到达时,它就坏了,所以我最终使用手机的网络共享功能从人行道上上网,然后在公交车冲向门洛帕克的 101 路公交车上,一边弹跳一边尽我所能地骑着车。
然后,由于那天是星期五,我们将像往常一样每周进行一次“大型表演”,回顾最近的停电 (SEV),因此用于这些活动的以布偶命名的房间被重新用作“作战室”。这是一群工程师类型的人,他们在一个相对较小的房间里思考得非常努力,流了很多汗,通风不是很好,门开着或没有。
变得臭了。好的?我不会粉饰它。
不过,还有更多。我已经说过几次,当我使用 Mac UI 时,我确实没有“完全发挥功能”。因此,我在公司笔记本电脑上运行 VMWare Fusion,并在虚拟机中运行 Ubuntu,并将其用于我的所有工作。
尽管如此,实际上在笔记本电脑(13 英寸 Macbook Air)上进行工作还是很糟糕,因为按键布局不太正确(想要在 Linux 中使用 alt-tab?最好使用 option-tab!),而且屏幕很小!它是一个用于浏览网页和阅读电子邮件的很棒的小机器,但它不是尝试破解一堆 xterm 并进行实际工作的地方。
我可以在那里运行我的终端吗?是的。我有吗?是的,有一段时间。我的做法有效吗?并不真地。我想念我的桌子、我的普通椅子、我的大 Thunderbolt 显示器、我的全尺寸(但完全无聊)键盘,以及一个相对无气味的环境。
幸运的是,到了我人生的那个阶段,我已经足够大了,我愿意采取措施来增强自己的理智和效率,并说我会从办公桌上回来。我回到我们的大楼,坐在办公桌前,开始处理事情。
发生了很多事情,我四处寻找有用的东西。我花了一段时间才最终解决了一件事:为什么这些机器实际上从网络上掉下来了?为什么 sshd 死了?那里发生了什么事?
我的全部目标是找出为什么机器在那天早上的“推送”过程中内存不足时似乎摧毁了一切。这是某种病态的内核“OOM 杀手”吗?是否还有其他原因导致错误的工作被关闭?
我们“必须”知道,否则我们无法确定这种情况不会再次发生。许多其他人都在努力研究如何重现那天早上发生的事情,而其他人则同时努力减少网络服务器上的内存膨胀。
但是,如果不知道到底发生了什么,导致机器被简化为 init 和这个“fbagent”的东西,我们就陷入了达摩克利斯之剑的境地,它可能随时落在我们身上,并“再次”击倒我们。
人们发现,是的,他们运行机器时内存不足,特别是在推送时——将新字节码分发到网络服务器。其他人开始采取措施来消除那个夏天一直在蔓延的一些肿胀,这样记忆情况就不会那么糟糕了。我怀疑其他一些人也减少了小型网络服务器上的线程(同时请求)数量,以防止它们运行得“过热”。
我的任务仍然是找出“用核武器攻击世界”这件事的根本原因。如果没有我平时精心调整的环境,这不可能发生在那个小会议室里,这就是我放弃的原因。
我花了几周时间才真正理解它的意义。我的意思是,真的,我并没有夸大其词。停电发生在 8 月 1 日,我终于在 8 月 19 日下午弄清楚了破坏盒子上所有进程所需的顺序。我知道这一点是因为有一张屏幕截图,我在 IRC 上通过它进行了讨论,因为它已经摆脱了……这使它成为我的公开演讲之一。
超过 18 天的时间里,我一直不知道为什么,而且几乎一直担心会发生什么。
在那些日子里,机器正在运行 Upstart for init (pid 1),所以唯一会恢复的东西是在 inittab 中设置为“respawn”的东西(还记得吗?),所以我们会在控制台上得到一个 getty 等等。这就是我如何能够使用带外访问来跳入并进入“是的,机器已启动,但其他所有东西(包括 sshd)都已关闭”。
[旁注:如果您今天这样做,我认为 systemd 最终会重新启动盒子上的大部分内容,并且您实际上可能会从中恢复。不过,我不打算仅仅为了发表一篇文章而尝试! ]
为什么fork失败?很简单:盒子内存不足。但是,我必须重现这一点才能确定。我是怎么做到的?这花费了更长的时间,并且是在基于“内核 OOM 杀手”之类的谣言而追寻许多死胡同之后。我们在 OOM 杀戮期间陷入僵局吗?发生了一些可怕的事情,当消息涌入 printk 环形缓冲区时,主机会变得非常奇怪。这浪费了很多时间,而且也不是真正造成这种情况的原因。
最后,重现它需要将我的测试系统的交换大小从几 GB 缩小到仅 64 MB。然后我还运行了一些我编写的“memeater”东西:它们会 malloc() 一些空间并通过写入来弄脏页面,这样它们实际上就获得了物理内存。然后他们就坐下来等待。在给盒子施加了足够的内存压力后,它终于崩溃了。
即便如此,我仍然认为这是公司为自己的产品环境编写的任务调度程序,因为每个人都认为它有罪,这就是暗流。但不,事实并非如此。几分钟后,我找到了确凿的证据:fbagent 在所有东西都死掉的时候记录了一些关于“开始杀死子-1”的信息。
多年研究 Linux 机器的经历告诉我杀死“pid -1”实际上会做什么,这最终解释了为什么 fbagent 这个东西没有在机器上每个进程的屠杀中消失。这就是杀人的凶手!
这个 fbagent 进程以 root 身份运行,运行了一堆子进程,称为 fork(),不处理 -1 返回代码,然后去杀死那个“任性的孩子”。在 Linux 上向“pid -1”发送信号(在本例中为 SIGKILL)会将其发送给除 init 和您自己之外的所有内容。如果您是 root(是的)并且没有在某种 PID 命名空间中运行(也是的),那么这几乎就是整个世界。
当然,我正在查看签入的源代码,但无法弄清楚这个 -1 到底来自哪里。对 fork() 的调用*确实*检查了 -1 并将其作为错误处理并退出。那么它是如何一直存活到调用kill() 的地方的呢?
这是另一个老鼠洞,答案也是值得注意的:我在签入的源代码中看不到它,因为它已经被修复了。其他一些工程师在一个完全不相关的项目上被绊倒了,解决了这个问题,并向拥有该程序的团队发送了修复程序。他们已经提交了它,所以源代码看起来很好。
[另一个旁注:这个人修复了一些代码中的错误,而这不是他们真正的“工作”,这是一种过去被推崇的优秀行为——“FB 里的任何事情都不是别人的问题”。这个信条很久以前就死了。 ]
不幸的是,生产(许多机器)正在运行最后一个版本,该版本在此之前已被削减。它有一个错误:运行机器内存不足,使 fork 失败,杀死世界,全部死亡,哦,尴尬。
我无法想象在一个小小的笔记本电脑屏幕上做那种多窗口并行调查的事情,两边都有人在我旁边,而整个房间都在为倒闭或如果他们不解决问题而担心什么。
我想如果你有一堆必须发生的事情来处理可能的“危机”,那么“作战室”可能会起作用,然后只需协调它即可。在这样的情况下,你不会有人做那么多“低头黑客”的事情。
我实际上已经看到这样的聚会进行得很顺利,我将把这个故事留到下次再讲。