磁盘满了,服务却一声不吭
那天下午,运营突然在群里说:后台上传图片失败了,报"保存失败,请稍后重试"。
我上去看了一眼服务:进程在,systemctl status 显示 active (running),CPU 和内存都很平稳,健康检查接口秒回 200,日志里干干净净,连个 ERROR 都没有。
一切正常。除了图片存不进去。
我自信满满地开始排查。
- 是不是上传接口的代码逻辑有问题?本地跑了一遍同样的上传,成功了。
- 是不是权限问题?服务进程对上传目录有写权限,
ls -ld确认了,没错。
- 是不是磁盘配额限制?查了 quota,没设。 | | | | --- | --- |
- 是不是目录被挂载成只读了?
mount | grep看了,rw,可写。
- 是不是对象存储/外部依赖挂了?这次上传走的是本地磁盘,不涉及外部服务。
- 是不是 Nginx 的
client_max_body_size拦了?换个特别小的图片试,还是失败。
- 是不是代码里有个隐藏的异常被吞了?翻了一圈日志,真的没有 ERROR。
到这里我已经开始怀疑人生了:进程在、状态正常、健康检查通过、权限对、目录可写、无报错——按理说没有任何理由写不进去。
一个下午就在这种"每一项都健康、但功能就是坏的"里过去了。中间我还重启了服务两次,每次重启后前几秒能成功上传一两张,然后又失败——这个细节我当时注意到了,但没往深想。
五点多,我实在没招了,随手敲了条平时根本想不起来的命令:
然后看到了一行:
磁盘满了。Use% 是 100%,可用空间是 0。
熟悉运维的人这时候大概要笑出声了。服务写文件时,系统调用返回的是 ENOSPC(No space left on device),也就是"没地方写了"。但我们的代码在写文件失败时,只打了一行 debug 级别的日志,生产环境日志级别是 info,这行被直接过滤掉了;对外则统一包装成一句友好的"保存失败,请稍后重试"。
于是整个服务看起来岁月静好:进程活着、健康检查通过、监控面板一片绿、日志零报错——唯独它最该干的"写文件"这件事,悄悄失败了。
那"重启后能成功一两张"也解释得通了:重启时清了点临时文件和缓存,腾出几 KB 空间,够写一两张图,然后又被日志填满了。
至于磁盘是被什么吃光的——是某个调试日志,忘了关,以每秒几十行的速度连续写了两周。du -sh /var/log/* 一看,单个日志文件 30 多 G。
清掉那个日志、logrotate 配上、服务重启,秒好。
送给每一个"看监控觉得服务很健康"的人:
- "写不进去"的第一步永远是
df -h,顺便df -i。 磁盘空间满(100% Use)和 inode 耗尽(文件数太多但空间还有)都会导致ENOSPC,表现一模一样。这两个命令两秒钟出结果,比排查代码逻辑快一百倍。
- 健康检查不等于功能健康。 大多数健康检查只探测"进程活着吗、端口通吗",根本不验证"能不能写文件、能不能连数据库"。一个磁盘满的服务,能完美通过所有健康检查。给关键写操作加一个真实的端到端探测(真的写一个小文件再删掉),比十个 /health 接口都管用。
- 日志要轮转,这是纪律不是建议。
logrotate配好保留策略和大小上限,别让任何一个日志文件有无限增长的机会。调试日志用完就关——"忘记关调试日志"是磁盘被撑爆的头号元凶。
- 磁盘满这种错误,必须大声报错。 应用遇到
ENOSPC时,请打 ERROR、触发告警、让监控变红,千万别包装成一句用户友好的"请稍后重试"就完事。静默的资源耗尽,坑的不只是用户,还有那个对着全绿面板发呆一下午的倒霉蛋(很可能就是你自己)。
- 点赞
- 收藏
- 关注作者
评论(0)