磁盘使用率飙到88%?运维告警排查与清理全流程 运维值班最怕半夜手机连环响。前一阵OpenClaw线上业务节点的磁盘使用率飙到88%告警整整持续了11个小时因为是业务高峰期不敢随便动生产环境只能盯着监控面板干着急。这类磁盘水位告警在运维实操里实在太常见但每个人的处理链路完全不同——有人直接du -sh一层层翻目录有人先用lsof找没释放的句柄也有人干脆重启了事。这篇就完整记录我当时从收到告警到恢复水位、再到补齐预防策略的全过程希望能给同样被磁盘告警困扰的同学一条可复用的排查路径。1. 告警之初不慌先把现场证据固定下来告警消息弹出来的时候第一反应别是冲去服务器上删文件。先花两分钟把现场证据固定下来这能帮你省下后面大量的复盘时间。1.1 确认告警信息与基础状态我收到的那条告警内容是OpenClaw线上业务节点磁盘使用率88%已连续11小时高位运行。这个“连续11小时”很重要说明磁盘空间不是突然被写满的而是一个缓慢爬升的过程——这种状态通常指向日志、缓存或临时文件的持续堆积而不是单次大文件写入。登录服务器第一件事跑一遍df -h看挂载点的真实水位同时跑df -i看inode使用率。很多人只盯空间忽略了inode但某些情况下inode满了也会让进程报No space left on device而且排查起来非常隐蔽。确认一下具体是哪个分区告警、空间还剩多少、inode有没有同步告警df -h df -i输出非常直观。比如我的环境里/dev/vdb1挂载在/data80G容量已经用了70G只剩10G可用。而/根分区还算健康说明压力集中在业务数据盘上。这一步还要顺手记录一下时间点和当前值因为后面清理完要对比清理前后至少要有两次数据才能确认问题真正解决。1.2 看一下负载与进程状态排除干扰项磁盘告警虽然主要矛盾在空间但也值得花半分钟瞄一眼其他指标防止判断被干扰。跑一下uptime看负载、free -h看内存再用top扫一眼有没有异常进程在大量写磁盘。这一步不是必须的但有意义如果负载和内存都正常那基本可以判断是文件系统本身的堆积问题如果负载很高、大量进程处于D状态说明磁盘IO可能也有压力清理动作就要更谨慎避免在高峰期先触发IO风暴。当时现场情况是负载正常内存也充裕进程状态里没有明显的D状态卡死所以我可以放心地往下做文件层面排查。1.3 记录基线为后续核对留下依据把df -h的输出保存一份顺手记录时间和当前使用率df -h /tmp/disk_before_$(date %Y%m%d_%H%M%S).log这个操作只要一秒但后面你清理完再跑一次df -h做对比时就会感谢自己。很多人在排查过程中凭印象说“我删了好几个G”结果一查剩余空间根本对不上——不是没删掉就是还有进程占着句柄没释放。有了基线文件所有的前后对比都清清楚楚一点都不需要打嘴仗。2. 从上到下定位空间消耗大户目录与文件的“层层下钻”现场固定完之后真正的排查才开始。磁盘空间的排查核心就一句话逐层缩小范围直到锁死具体文件和目录。2.1 用du从最上层开始扫别跳级很多人习惯直接在业务目录里翻文件这是低效的。正确姿势是从挂载点根目录开始一层一层下钻。比如/data是告警分区那就先扫/data下的第一层目录du -sh /data/* | sort -hrdu -sh会把每个子目录的总大小统计出来sort -hr按数值从大到小排。命令执行可能需要一点时间尤其是目录很多时但这是最可靠的起点。我当时看到输出里最大的三个目录35G /data/openclaw_data 27G /data/logs 12G /data/backups三个合计已经74G基本对得上总量。接下来不需要三个都查先从最大的/data/openclaw_data开始下钻因为它的增长速率通常是最快的。2.2 用--max-depth控制下钻深度继续下钻时别再一层层du -sh *了效率太低。用--max-depth一次多包两层du -h --max-depth2 /data/openclaw_data | sort -hr | head -20输出会直接显示目录树两层的空间占用排名让你快速看到哪个子分支冒尖。当时结果里有一项非常刺眼18G /data/openclaw_data/task_runtime/tmp 12G /data/openclaw_data/model_cache一个任务运行临时目录占18G模型缓存占12G。看到这种结构基本就能猜到问题的大方向了。2.3 顺着时间线检索近期大文件除了按目录统计体积还得主动找“最近被写入的大文件”。因为磁盘是11小时内慢慢爬升的那么11小时内写入的大文件或者快速膨胀的文件就是重点怀疑对象。用find按时间和大小两个维度过滤find /data -type f -size 500M -mtime -1 -exec ls -lh {} \; | sort -k5 -hr这个命令找出/data下所有大于500MB、且在24小时内修改过的文件按大小排序。输出会直接列出候选文件路径。如果有超大日志文件这里一定跑不掉。我当时扫出来几个GB级别的日志文件位于/data/logs/openclaw/下文件名带了日期后缀明显是滚动切割后还没来得及清理的旧日志。2.4 常见大文件窝点逐个清点结合多台服务器的经验磁盘空间积压通常集中在几个固定位置每个位置都有自己的脾性常见位置典型特征危险程度应用日志目录日志不轮转或轮转后不清理单文件狂涨极高任务临时目录任务结束后tmp残留没删干净高模型/离线数据缓存版本更新后旧缓存未清理中容器镜像与容器日志overlay2目录膨胀、json.log无限增长高打包产物或备份文件一次性写入但忘了清理中core dump文件进程崩溃后写入的core文件中包管理器缓存npm/pip/yum缓存堆积低这份清单基本覆盖了90%以上的磁盘告警场景。每次排查时按这份清单挨个看一遍就不会漏掉大块头。我这次遇到的正好是其中三类日志、任务临时目录、缓存。3. 藏在阴影里的空间杀手被删未释放与临时文件目录统计看到的是“文件系统认为占用的空间”但实际占用磁盘的还有一类特殊存在——已被删除但仍在被进程占用的文件。这类文件你在任何目录列表里都看不到却真实地占着磁盘空间是排查中最容易忽略的盲区。3.1 lsof找出deleted状态文件检查这一类空间用lsof命令lsof L1 | grep deletedL1是lsof的一个专有选项表示列出link count小于1的文件——也就是已经被unlink但仍有进程打开的文件。每个文件都会显示对应的进程PID和进程名。这里要重点解释原理。Linux下文件删除分为两步目录项解除链接unlink和实际释放磁盘块。当一个文件被进程打开时即使外部已经把它从目录里删掉只要该进程还持有文件句柄文件的存储空间就不会归还给文件系统。只有进程关闭文件或进程退出空间才会真正释放。这就像你借出去的书还书人嘴上说不要了但只要他手里还攥着不放书就还在他包里占地方。当时我的服务器上确实找到了一个典型的java 12345 1w REG 8,1 2107637760 /data/logs/openclaw/error.log (deleted)什么意思error.log已经被人手动删过了但OpenClaw的服务进程还在往这个句柄里写数据所以这个文件一直占着约2GB的空间而且还在持续增长。目录里看不到它但空间被它实打实地吃掉。这种是最隐蔽的坑——数据量往往不小还带动态增长属性。3.2 处理方案重载或重启进程让句柄真正释放一旦确认了“deleted但被占用”的文件处理方法不是再去删一次目录——因为目录里已经删了而是要处理持有文件句柄的进程。最优雅的方式是让服务优雅地重载或重启。对支持日志重载的服务用kill -HUP PID触发日志文件重新打开然后旧句柄自然关闭如果服务不支持reload就需要重启进程重启前务必确认业务影响面尤其是线上节点在高峰期时更要评估。这次的情况是OpenClaw支持平滑重启我直接systemctl reload openclaw或者对对应PID发HUP信号处理完再看lsofdeleted文件就消失干净了。3.3 临时目录里的漏网之鱼除了删除未释放的句柄/tmp和任务运行目录下的临时文件也值得专门扫一遍。这类临时文件有两个特点一是文件名随机性强你很难从业务逻辑推断它是什么二是生命周期短很多是任务跑完就该删但没删的。我的环境里/data/openclaw_data/task_runtime/tmp占了18G里面是一堆任务运行中产生的中间文件。因为OpenClaw的任务调度模块在异常退出时没有走清理逻辑导致tmp目录持续累积。对于这类文件如果业务高峰期没有正在运行的关键任务可以放一个较长的时间窗口来清理。当时我确认了一下没有正在运行的任务后直接以超过7天为阈值清理了目录下所有旧文件。4. 清理行动与效果验证从88%逐步回落到安全水位定位到问题接下来就是动手清理阶段。这里必须说一下优先级和顺序先处理“已删除未释放”的空间再清理应用日志和临时文件最后再考虑删业务数据或扩容。因为每次清理动作都有风险先做零风险的动作把安全的回退空间先拿回来。4.1 稳妥有效的日志清理策略应用日志的清理我不建议直接rm了事。日志往往有合规要求或排查需求删了就找不回来所以最好的方式是设置“保留最近N天其余清空”。比如/data/logs/openclaw/下保留了近60天的业务日志但实际排障需求一周内基本就足够我按七天保留策略做了清理find /data/logs/openclaw/ -type f -name *.log.* -mtime 7 -exec truncate -s 0 {} \;注意这里用的命令是truncate -s 0而不是rm。这两种处理有本质区别truncate把文件清空但保留路径不影响任何正在写入该文件的进程句柄rm则是把文件移除很容易再次制造“deleted但被占用”的局面。大量线上磁盘事故都源于某个运维同学直接rm了正在被写入的日志文件。如果你确定日志文件已经没有进程占用也可以直接rm但更稳妥的通用做法是保留文件、清空内容。清了这波/data/logs从27G降到约8G立刻拿回19G空间。4.2 清理任务临时目录与缓存对于任务临时目录执行按修改时间清理find /data/openclaw_data/task_runtime/tmp -type f -mtime 1 -exec rm -f {} \;这里我用的是-mtime 1只删除1天前生成的临时文件。原因很明确接近当前时间点的文件可能是正在运行任务或刚结束任务写下的误删会导致任务数据丢失。宁可保守一点多留一天也别为了空间冒险删掉运行中的任务文件。模型缓存目录model_cache的处理要更谨慎因为同样的模型文件如果以后再被使用删掉后重拉既费时间又可能影响线上推理。我当时的处理是只删除已明确不再使用的旧版本缓存保留当前版本。判断依据是与当前模型的MD5对比。这类操作建议逐个确认再删不要写个一条命令全部清掉。4.3 清理后的验证与观察期清完之后别急着宣布“搞定”。整个验证过程要按“命令验证—监控观察—告警确认”三步走。命令验证很简单df -h输出里/dev/vdb1从上一步的88%降到了45%左右。这个数字符合预期日志、临时文件、缓存和句柄释放加起来一共释放了30多G空间。但空间下降不代表问题终结。真正的检验是接下来一段时间内水位是否反弹。一定要在清理完的30分钟到2小时内多刷几次df -h观察使用率有没有快速回涨。如果回涨明显说明还有一个持续的写盘点没找到如果稳定不动才算真正落地。同时把告警平台里的当前磁盘监控项确认一下告警是否自动恢复。4.4 复盘记录这次告警为什么能持续11小时处理完故障还需要做一个快速复盘为什么磁盘水位88%能连续告警11小时才被处理主要原因有三条告警虽发出但夜班同学没有第一时间响应等到早上才发现——这暴露了告警响应的SOP缺失磁盘增长是慢性的不是突发的容易让人觉得“还能撑一会儿就没管”——这暴露了对阈值和前置告警的认知不足缺少自动化的日志清理机制——旧日志、临时文件都在靠人工定期清理人工一旦忘就爆。这些复盘点非常值钱。故障是表象机制才是根源。后面我就是围绕着这三点去补齐防御体系的。5. 磁盘水位的日常防御监控、告警与滚动清理机制处理完这一次故障能保证下次不再发生、或者发生了能第一时间解决才是最关键的。这里分享几个我落地过的日常防御手段覆盖三个层面告警分级、自动轮转、容量前瞻。5.1 将磁盘告警从“单点告警”升级为“分级响应”很多团队只设置一个磁盘告警阈值比如80%结果到了80%才收到消息留给响应的回旋余地非常小。我建议做分级级别阈值动作预警70%通知相关运维记录趋势不打断业务严重85%进入排查流程触发自动清理脚本或人工介入紧急92%立即处理必要时先停掉非核心写入任务再排查分级告警的好处是你在70%时就开始关注而不是到88%甚至92%才傻眼。这次告警如果把预警阈值设为70%提前十几个小时就能发现问题——不用等它连续11小时高位运行才触发严重告警。5.2 用logrotate做日志轮转让滚动清理自动化人工清理再勤快也赶不上日志增长速度日志轮转是必须的基础设施。以OpenClaw的业务日志为例一份最低配置/data/logs/openclaw/*.log { daily rotate 7 compress delaycompress missingok copytruncate }解释几个关键参数daily按天轮转rotate 7保留7份旧的轮转日志compress对旧日志做压缩missingok某天没有日志不报错copytruncate先复制后清空原文件——这个参数配合运行中的进程非常有用不需要重启服务。配置好后用logrotate -f强制执行一轮测试是否正常下次就交给系统自己跑。5.3 写一个带保护逻辑的清理脚本用cron定时跑对于任务临时目录这类历史遗留问题光靠人肉清不够必须脚本化。我写了一份简单的定时清理脚本逻辑不复杂但一定要加好保护#!/bin/bash BASE_DIR/data/openclaw_data/task_runtime/tmp TARGET_AGE2 # 删除2天前的临时文件 DISK_THRESHOLD70 CURRENT$(df -h /data | awk NR2 {print $5} | tr -d %) if [ $CURRENT -lt $DISK_THRESHOLD ]; then echo Disk usage under ${DISK_THRESHOLD}%, skip clean. exit 0 fi find $BASE_DIR -type f -mtime ${TARGET_AGE} -exec rm -f {} \;脚本做了两个保护一是只清理特定目录绝不触碰业务数据二是只有当磁盘使用率超过阈值才执行清理选择的是“容忍型清理”而不是“激进型清理”避免为了清理而去清理。放在cron里每小时跑一次* */1 * * * /usr/local/bin/cleanup_disk.sh /var/log/cleanup_disk.log 215.4 监控侧采集实时磁盘趋势别只看瞬时值在监控平台里把磁盘使用率做成时间序列曲线并配置增长速率检测。这点很多人会忽略——瞬时水位只是结果增长趋势才是原因。如果磁盘使用率每小时匀速上升1%那么即使当前是50%你也大概能推算出还有多久会到85%。把这部分做成一个“磁盘增长估算”的展示项运维心里就有了提前量。另外一个极易被忽略的点监控对象要覆盖到每个节点而不只是主业务节点。边缘业务节点磁盘满了会影响整个集群的健康而很多团队只看着最核心的那台机器。5.5 最后一条经验永远保留稳定冗余经过这次故障我最深的体会是不管怎么清理和监控总会有日志暴涨、缓存异常、任务残留等意料之外的情况。与其每次都靠清理“救火”不如给自己留好保底空间——对关键路径做容量预估时按“当前使用量的1.5倍”规划为宜针对核心数据盘要么预留足够冗余要么配置好扩容流程。对这次OpenClaw节点处理完整理之后我顺手把监控阈值、logrotate和定时清理脚本都落地了。大概过了两周左右有一次磁盘使用率又出现小波动但触发预警后脚本自动执行清理还没等我打开告警面板水位就已经自己回落了。这种“告警没变成故障”的感觉比修好一次故障踏实得多。