劳动节前夕的服务器保卫战:脚本永不眠

服务器又挂了,监控告警短信在凌晨两点十七分准时轰炸进来。我掐灭第三根烟,盯着屏幕上一片红的 CPU 图表,胃里一阵翻腾。这他妈是劳动节礼物?攻击流量像潮水一样涌进来,峰值冲到了平时正常访问的五十倍。

不是那种花里胡巧的渗透,就是最原始、最粗暴的 CC 攻击。成千上万个 IP 轮番请求首页和几个核心 API 接口,请求头伪装得跟正常用户一模一样,User-Agent 随机生成,连 Referer 都给你填上。它们的目的很简单:用垃圾流量灌满你的带宽,拖垮你的应用进程,让你的数据库连接池彻底枯竭。我切到 Nginx 的 access 日志,tail -f 命令滚出来的全是 200 状态码,但后面跟着的响应时间从正常的几十毫秒飙升到了十几秒。每一个成功的请求都在消耗一个宝贵的 PHP-FPM 子进程,直到所有进程都被这些“合法”的请求占据,真正的用户再也挤不进来。

先上紧急止血方案。iptables 规则一条条敲进去,封禁那些请求频率异常的 IP 段。但对方明显用了代理池,封掉一批,立刻换上一批新的。这种猫鼠游戏不能一直玩下去,体力跟不上。得写脚本,让机器来干这个脏活。我打开 Vim,开始写一个 Python 监控脚本。核心逻辑很简单:实时分析 Nginx 日志,按 IP 统计单位时间内的请求数,一旦超过阈值,就自动调用 iptables 封禁,同时把 IP 记录到黑名单文件里。这里面的坑在于性能,日志文件增长太快,直接用文件读写会卡死。得用 subprocess 模块 tail -f 管道输出,用字典在内存里做计数,每五秒清空一次重新统计,避免内存泄漏。封禁操作也要加锁,防止多个进程同时执行 iptables 命令导致冲突。

脚本跑起来了,控制台开始刷刷地输出被封禁的 IP。但服务器的负载并没有立刻降下来,那些已经建立的恶意连接还在持续消耗资源。得重启 PHP-FPM 和 Nginx 吗?风险太大,可能会误伤正在进行的正常交易订单。我切换到数据库监控,发现大量的 Sleep 进程,都是这些恶意请求建立的数据库连接,查询执行到一半就被卡住,不释放。这时候,之前为了应对促销而调整的 MySQL 参数成了负担——为了容纳更多连接,我把 wait_timeout 设得太长了。现在,这些僵尸连接成了压死骆驼的最后一根稻草。我咬着牙,用 mysqladmin 命令手动 kill 掉那些来自攻击 IP 的连接,手指在键盘上敲得生疼。

窗外的天开始泛灰,不是天亮,是城市永远不灭的霓虹光污染。烟灰缸满了。团队里负责运维的小伙子下午请假回老家了,电话打过去,能听到背景音里车站的广播。我没说服务器的事,只让他好好过节。说了有什么用呢?他手上没有终端,心里还惦记着刚抢到的火车票。这种时候,唯一能依靠的,就是自己这双手和屏幕上这几行还能生效的代码。管理?团队?温情?在服务器告警的红光面前,都是奢侈品。你花了半年时间培养人,建立流程,搞什么弹性工作制,真到了刀刀见血的实战环节,能顶上去的,还是当年那个单枪匹马、对着一堆日志文件死磕的自己。脚本在后台不知疲倦地封禁着第 347 个 IP,我靠在椅子上,脑子里想的不是怎么加强防火墙,而是突然理解了为什么那些黑客骨子里都那么偏执。因为在这个由信号和代码构成的世界里,信任的边界非常清晰——你亲手验证过的规则会执行,你写的脚本会工作,除此之外,一切皆不可控。

攻击流量在凌晨四点左右渐渐退去,像潮水一样,来得猛,去得也干脆。可能是对方的代理IP池用尽了,也可能是觉得这块骨头太难啃。服务器负载缓慢地降回绿色区间。我保存好日志,把临时写的脚本整理了一下,加了些注释,扔进专用的应急工具箱目录。劳动节假期就要开始了,街上应该都是出游或者回家的人。我关掉办公室的灯,屏幕的光映在脸上。保卫战结束了,但我知道,没有永远的胜利,只有暂时的休战。而我能做的,就是让这些脚本更聪明一点,让下一次的响应更快一点。在这个行当里,劳动节从来都不是休息的代名词,它是另一个需要高度戒备的时间窗口。脚本永不眠,我也差不多。

© 版权声明
THE END
喜欢就支持一下吧
点赞76 分享