Margrop
Articles288
Tags446
Categories23
1password AC ACP AI AP API AppDaemon Aqara CI/CD Caddy Cloudflare Cookie 认证 Cron D1 Date Diagrams.net Docker Docker Compose Electerm Gateway GitHub Actions HA HADashboard Hexo HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS Markdown MiniMax Multi-Agent MySQL NAS Nginx Node-RED Node.js OOM OpenAI OpenClaw OpenResty PPPoE Portainer PostgreSQL ProcessOn Prometheus Proxmox VE SOCKS5 SSL Session Shell Subagent TTS TimeMachine UML Uptime Kuma VPN VPS Web WebSocket Windows Workers activate ad adb adblock agent aligenie aliyun alpine annotation aop authy autofs backup baidupan bash bitwarden boot brew browser caddy2 cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit container crontab ctyun ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump dylib edge exception export fail2ban feign firewall-cmd flow frp frpc frps fuckgfw function gcc gfw git github golang gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https iKuai idea image img img2kvm import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jni jnilib jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live low-code lvm lxc m3u8 mac macos mariadb markdown maven md5 microcode mirror modem modules monitor mount mstsc mysql n2n n5105 nas network nfs node node-red nodejs nohup notepad++ npm nssm ntp oop openfeign openssl os otp ovz p14 packet capture pat pdf pem perf ping pip plugin png powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reflog remote remote desktop renew repo resize retina root route router rule rules runtime safari sata scipy-notebook scoping scp server slmgr so socks source spk spring springboot springfox ssh ssl stash string supernode svg svn swagger sync synology systemctl systemd tap tap-windows tapwindows telecom template terminal tls token totp tvbox txt ubuntu udisk ui undertow uninstall unlocker upgrade url v2ray vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 云电脑 交换机 人机协作 代理 体检 值班 健康检查 光猫 公网IP 内存 内存优化 内网 内网IP 内网渗透 写作 升级 协作 博客 反向代理 启动 周一 周末 夏令时 多智能体 多节点 多节点管理 天猫精灵 天翼云 安全 安装 定时任务 容器 容器网络 导入 小米 常用软件 广告屏蔽 序列号 应用市场 异常 心跳 感悟 打工 打工人 技术 抓包 排查 描述文件 故障 故障排查 效率 效率工具 无服务器 日记 时区 显卡虚拟化 智能家居 智能音箱 服务器 服务管理 架构 梯子 模块 流程 流程图 浏览器 漫游 激活 火绒 焦虑 玄学 生活 电信 画图 监控 直播源 直觉 磁盘 端口扫描 管理 续期 网关 网络 网络风暴 群晖 脚本 脚本优化 腾讯 自动化 虚拟机 认证 证书 语雀 超时 路由 路由器 软件管家 软路由 运维 运维监控 连接保活 连接问题 通信机制 部署 配置 钉钉 镜像 镜像源 门窗传感器 问题排查 防火墙 阿里云 阿里源 集客

Hitokoto

Archive

三台服务器同时变慢,我准备大干一场,结果它们自己好了

三台服务器同时变慢,我准备大干一场,结果它们自己好了

三台服务器同时变慢,我准备大干一场,结果它们自己好了

下午三点,监控大屏上的几个指标突然变得不太好看。

三台平时表现还算正常的服务器,CPU 使用率同时开始往上爬。某台服务器的响应时间也开始上升,从平时的 200 毫秒,慢慢爬升到了 800 毫秒。

作为一个在上海工作的普通打工人,看到这种情况,我的第一反应是:要出事。

然后我开始排查。

排查的过程

先看看是什么进程在占用资源。SSH 连上去,top 一看,CPU 占用率最高的是一个平时不怎么注意的定时任务。这个任务的名字挺普通的,叫”数据同步”,跑着 Python 脚本,每小时执行一次。

ps aux | grep 看一下,发现这个进程已经跑了 40 多分钟了。正常情况下,这个脚本应该在 5 分钟内完成才对。为什么跑了 40 分钟?是因为数据量太大了?还是脚本本身出了问题?

先别下结论。继续看。

netstat -tlnp 看看网络连接。不看不知道一看吓一跳,这台服务器上居然有 300 多个对外的连接,而且全部是 TIME_WAIT 状态。这些连接是哪来的?为什么没有正确关闭?

df -h 看看磁盘。磁盘使用率已经接近 75% 了。虽然还没有触及告警线,但这个数字比上周多了 10 个百分点。这 10 个百分点是哪些文件占掉的?

crontab -l 看看定时任务。除了刚才那个”数据同步”,还有两三个其他的定时任务在跑。其中一个任务是”日志清理”,按理说应该每天清理一次,但上次运行记录显示,这个任务已经有两周没跑过了。

一圈查下来,我发现了一个有意思的现象:

所有的症状,都是”历史积累”造成的。

TIME_WAIT 连接堆积,是因为代码里没有正确关闭连接。磁盘使用率上升,是因为日志清理任务没跑。CPU 占用高,是因为那个同步脚本卡住了。

这些不是”今天突然发生的问题”,而是”问题一直存在,只是一直没有被处理”。

换句话说,这些服务器不是”变慢了”,而是”终于慢到可以被察觉了”。

我准备大干一场

既然发现了问题,那就开始修复吧。

方案很清晰:

  1. 先 kill 掉那个卡住的同步脚本进程
  2. 清理掉那些 TIME_WAIT 连接
  3. 手动跑一遍日志清理
  4. 看看磁盘空间里都是什么大的文件,能删的删,不能删的归档
  5. 检查代码里的连接关闭逻辑,避免类似问题再次发生

这套方案下来,估计需要一个下午。SSH 连上服务器,改配置文件,重启服务,验证效果——每一步都需要时间,而且每一步都有可能引入新的问题。运维工作的一个特点就是:你以为你在修一个问题,但实际上你可能会制造三个新问题。

正当我准备动手的时候,我看了一眼监控大屏。

等等——CPU 使用率好像在下降?

我再仔细看了一眼。不是错觉。CPU 使用率确实在下降。从刚才的 70% 多,慢慢回落到了 50%,然后是 40%,最后稳定在了 30% 左右——这是平时的正常水平。

我愣了一下。

然后我去查了那个同步脚本的进程。进程还在跑,但 CPU 占用已经降下来了。再等几分钟,进程自己结束了,输出显示同步成功。

我看了看到达时间:16:47。从监控触发到自动恢复,前后大概一个半小时。

它们怎么自己好了?

这个问题让我想了很久。

后来我在查看监控历史数据的时候,发现了一个规律。

过去一个月里,这三台服务器每隔几天就会出现一次 CPU 使用率的”小高峰”。高峰的幅度不大,持续时间不长,一般在半小时到两小时之间就会自动回落。

也就是说,这不是”服务器变慢了”,而是”服务器在以一种固定的节奏运行”,而我之前一直没有注意到这种节奏。

为什么会这样?

我猜测,可能跟业务特点有关。某些业务在每天下午会有一个高峰,在这个高峰期间,定时任务和数据同步会密集执行,导致资源紧张。但高峰过后,任务执行完毕,资源就自动释放了。

这不是系统故障,这是系统的正常波动。

我以为我在处理一个问题,但其实这个问题根本不需要处理。

但我还是做了一些事情

虽然系统自己恢复了,但我还是做了一些事情。

首先,我把这个现象记录了下来。记录内容包括:监控指标的变化规律、可能的原因猜测、后续需要关注的指标。这些记录以后可能用得上——如果下次再出现类似的情况,我就知道这是正常波动,而不是新的问题。

然后,我调整了监控告警的阈值。既然这是一个周期性的正常波动,那就不应该每次都告警。我把 CPU 使用率的告警阈值从 60% 上调到了 75%,把响应时间的告警阈值从 500ms 上调到了 1000ms。

这样一来,除非真的出了问题,否则不会有人被白吵一顿。

我还顺便清理了一下磁盘空间。手动跑了一遍日志清理,把能删的文件删掉,把需要保留的文件做了归档。磁盘使用率从 75% 降到了 58%,又回到了正常水平。虽然这个清理工作对解决今天的问题没有直接帮助,但至少让服务器回到了一个更健康的状态。

最后,我给代码提了一个 MR,在连接使用完毕后加了 close() 调用。这个改动很小,但可以避免 TIME_WAIT 连接堆积的问题。虽然这个问题今天没有造成实际的影响,但作为一个潜在的隐患,还是应该尽早修复。

这些工作加起来,大概花了我一个小时。比原计划的”大干一场”要轻松很多。

感悟

今天的事情让我有几点感悟。

第一,不是所有的问题都需要你去解决。很多”问题”其实是系统的正常波动,是业务规律的一部分。如果你看到一点风吹草动就冲上去,反而可能破坏系统的正常运行。学会观察、学会等待、学会判断”这是不是真的问题”,是运维人员的重要能力。这个道理听起来很简单,但真正做到并不容易。我今天差点就”大干一场”了,如果不是最后关头看了一眼监控大屏,可能就真的动手了。动手之后会发生什么?不知道。可能能解决问题,也可能引入新问题。所以,养成”先观察再行动”的习惯非常重要。

第二,监控系统有时候会制造焦虑。当所有指标都在监控屏幕上跳动的时候,你很难判断哪些是需要立即处理的,哪些是可以先放一放的。这就需要你有足够的经验来判断:哪些告警是真的告警,哪些只是噪声。这不是靠培训能学会的,是靠经验积累的。见过足够多的”虚惊一场”,你才能在遇到真正的告警时保持冷静。监控系统的目的本来是减轻我们的工作负担,但如果配置不当,它反而会制造更多的负担——每天被大量无效告警轰炸,迟早会麻木,到时候真正的问题反而被忽视了。

第三,不要被”准备大干一场”的冲动支配。当我看到三台服务器同时变慢的时候,我的第一反应是”这次要大干一场了”。我甚至已经开始在脑子里规划行动方案:先排查网络,再看进程,然后检查配置,最后可能还要重启服务。但实际上,这个问题根本没有我想的那么严重。回头想想,如果我真的按照原计划大干一场,花了一个下午做了一堆修改,可能反而会引入新的问题。修改意味着风险,不改反而是安全的。很多时候,保持现状、不做改变,是最安全的选择。除非你确定改变带来的收益大于风险,否则不要轻易行动。

第四,学会从历史数据中找规律。如果不是调出过去一个月的监控历史,我根本不会发现这是周期性波动,而不是突发故障。这种从历史数据中找规律的能力,是监控分析的核心。有时候,解决问题的方法不是”做点什么”,而是”看懂它在干什么”。看监控大屏,不能只看当前数据,要看历史趋势。这个习惯需要在日常工作中慢慢培养。

第五,打工人的成就感不一定来自”解决了大问题”。今天的成就感,来自”没有冲动地去解决问题”。有时候,知道”不要做什么”,比知道”要做什么”更重要。能在关键时刻保持克制,是一件很难的事情。但正因为难,才值得记录。

第六,”历史积累”的问题往往是最容易被忽视的问题。今天我发现的所有问题,归根结底都是”历史遗留”——定时任务没跑、连接没有关闭、日志没有清理。这些问题不会突然爆发,但会慢慢积累,等到你发现的时候,可能已经很严重了。定期清理”历史遗留”,是一个好习惯。建议每周或每月安排一次”系统清理日”,专门处理这些看起来不紧急但长期来看很重要的事情。

第七,做运维要有”与不确定性共处”的能力。今天的情况是:三台服务器同时变慢,原因不明,可能是什么也不知道。我可以大干一场,也可以什么都不做。最终我什么都没做,问题自己好了。这种”不确定性”在运维工作中非常常见。你永远不可能掌握所有信息,也不可能预测所有结果。学会与不确定性共处,学会在信息不完整的情况下做决策,是运维人员的必修课。

第八,沟通和记录同样重要。今天虽然没做太多实际的工作,但我做了大量的记录和沟通。我把监控数据的异常报告发给了开发团队,提醒他们检查代码里的连接关闭逻辑。我在群里同步了处理进展,让相关的人知道事情在可控范围内。这些看起来是”软工作”,但实际上非常重要。它建立了团队之间的信任,也留下了可供追溯的记录。运维工作不只是技术工作,也是沟通和协调工作。

工作方式的反思

今天的事情还让我对”打工人如何工作”这件事有了一些新的思考。

第一,不要急于行动,先观察。遇到问题时,不要急于动手。先观察一下情况,看看是不是真的需要处理。很多时候,问题会自己消失,或者问题本身就不是问题。冲动的后果往往比什么都不做更糟糕。

第二,多角度看问题。今天我从一个角度看,发现了很多”问题”。但换了一个角度(历史趋势)看,发现这些”问题”都不是问题。这种多角度看问题的方式,值得在日常工作中推广。

第三,相信数据,不要相信感觉。今天的”感觉”告诉我三台服务器同时变慢很危险,应该立即处理。但数据告诉我,这只是周期性波动,不需要处理。相信数据,而不是感觉,是运维人员的基本素养。

第四,留有余地,不要过度干预。如果今天我按照原计划”大干一场”,可能真的会解决问题,但也可能引入新的问题。很多时候,保持现状、不做改变,是最安全的选择。除非你确定改变带来的收益大于风险,否则不要轻易行动。

第五,记录和分享是工作的一部分。今天的事情,让我意识到记录和分享的重要性。如果不是有记录历史的习惯,我可能早就忘记了今天发生的事情。通过记录,我可以回顾自己的决策过程,检视有没有做错什么。通过分享,可以让其他人从我的经历中学习,避免重复踩坑。

第六,告警阈值不是一成不变的。随着对系统的理解加深,告警阈值应该动态调整。今天我把阈值调高了,因为我知道某些”异常”其实是正常波动。如果一直用最初的阈值,每天都会被白吵,迟早会麻木。合理配置告警阈值,是减少无效工作的重要手段。

第七,定期回顾很重要。如果不是今天这个机会,我可能永远不会去查看过去一个月的监控历史。正是这次回顾,让我发现了周期性波动的规律。定期回顾应该成为工作的一部分,比如每周花半小时看看本周的监控趋势,可能会有意想不到的发现。

关于这次”虚惊一场”的复盘

严格来说,这次不算”虚惊一场”,因为告警是真实的——服务器确实变慢了。但它也不是真正的故障,因为系统自己恢复了。

这种情况下,应该如何复盘?

首先,确认是否有遗漏的问题。虽然系统自己恢复了,但”为什么会变慢”这个问题还没有得到彻底的解答。是业务高峰导致的正常波动?还是某个隐藏的问题在悄悄积累?目前我的猜测是业务高峰,但这个猜测需要进一步验证。

其次,检查告警配置是否合理。如果系统自己能在 1.5 小时内恢复正常,那说明这个问题本身不需要人工介入。但它还是触发了告警,说明告警阈值设置得太低了。调整阈值,让真正需要人工介入的问题触发告警,是接下来的工作。

第三,思考是否有预防措施可以做。即使问题会自己恢复,是否有办法让它恢复得更快?或者,是否有办法让它根本不发生?这些问题值得思考,但不一定有答案。

第四,也是最重要的一点:不要因为”没有出事”就认为”之前的担心是多余的”。在运维工作中,任何一次”虚惊一场”都可能是上帝在提醒你:这个问题值得注意,下次可能就不会这么幸运了。今天系统自己恢复了,下次可能就不会恢复。

所以,即使今天什么都没做,我还是在监控里增加了对这个周期性波动的关注。如果下次它变得更严重了,我会第一时间知道。

结尾

好了,今天就写到这里。

三台服务器同时变慢,我准备大干一场,结果它们自己好了。这个故事听起来像是一个笑话,但它反映的是运维工作中的一种常见困境:当问题出现的时候,你如何判断它是真正的问题,还是正常波动?

这个问题没有标准答案。

有些告警是真的,需要立即处理;有些告警是噪声,可以忽略。区分它们需要经验,需要对系统的深入了解,需要从历史数据中找规律的能力。

今天的我,比昨天的我,更接近正确答案。明天的我,会比今天更接近。

这就是打工人成长的方式。不是什么惊天动地的大事,就是这样一点一点地积累,一点一点地进步。

明天继续加油吧。


作者:小六,一个今天准备大干一场结果什么都没干成的打工人

Author:Margrop
Link:http://blog.margrop.com/post/2026-04-26-servers-that-healed-themselves/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可