☰
ChatGPT、Codex排查实录:磁盘明明还有40%空间,为什么服务却突然报“No space left on device”?
2026/10/5 10:18:23 网站建设 项目流程

线上服务突然开始报错。

不是CPU打满。

不是内存爆了。

也不是数据库挂了。

日志里反复出现一句:

No space left on device

第一反应当然是:

磁盘满了?

结果你马上执行:

df -h

却发现最主要的磁盘分区只用了60%左右。

明明还剩40%空间,甚至还有几十GB可用。

这时候就很容易怀疑:

监控是不是错了?

系统是不是抽风了?

容器是不是误报?

如果这时直接让 ChatGPT、Codex:

“帮我清理一下磁盘。”

很可能一上来就查错方向。

因为Linux里的:

“No space left on device”并不一定只代表磁盘容量真的100%用满。

真正需要先搞清楚的是:

系统到底缺的是磁盘Block、inode、可写挂载空间,还是被某个进程占着没有真正释放。


一、先给核心判断:磁盘空间还有,不代表“还能创建文件”

Linux文件系统真正能不能继续写,不只取决于:

还有多少GB。

还取决于:

  • inode是否耗尽;
  • 当前目录所在挂载点是否已满;
  • 删除文件是否仍被进程占用;
  • 容器自己的Overlay空间是否已满;
  • 临时目录或日志目录是不是单独分区;
  • 文件系统是否进入异常状态。

所以看到:

No space left on device

第一件事不是继续盯着df -h。

而是要回答:

到底是“容量没了”,还是“文件系统资源没了”?


二、第一种高频情况:inode已经用完了

这是最经典的一类。

文件系统除了存储数据块,还要为每一个文件维护inode。

inode可以理解成:

文件的元数据入口。

比如:

权限。

所有者。

时间。

数据块位置。

如果一个磁盘里出现海量小文件,就可能出现一种很反直觉的情况:

磁盘容量还剩很多,但inode已经100%用完。

比如:

日志目录里每天生成几十万个小文件。

缓存目录不断创建临时文件。

某个程序每个请求都写一个独立文件。

结果磁盘用了60%。

但inode已经:

100%。

这时候系统再创建新文件,就可能直接报:

No space left on device

哪怕还有几十GB容量。


三、最快先看df -i

遇到这种问题,我通常会在df -h之后马上执行:

df -i

重点看:

IUse%

如果某个挂载点已经:

100%

那基本就已经找对方向了。

例如:

Filesystem Inodes IUsed IFree IUse% /dev/sda1 6553600 6553598 2 100%

这时候真正缺的不是:

GB。

而是:

inode。

所以清理策略也不能只找“大文件”。

你真正应该找的是:

哪个目录里有海量小文件。


四、为什么“找大文件”有时候完全没用?

很多人的清理习惯是:

找几个10GB日志 删掉

这种方法解决的是:

Block空间。

但inode耗尽时,真正的问题可能是:

某个目录下面有:

300万个2KB文件。

总容量不大。

但每个文件都要占一个inode。

于是你可能删掉一个10GB文件,磁盘多出10GB。

结果:

No space left on device

仍然存在。

因为:

inode一个都没释放多少。

所以如果df -i已经接近100%,排查重点应该从:

“谁占空间最大”

切到:

“谁的文件数量最多”。


五、第二种高频情况:文件已经删了,但空间没有真正释放

这个场景也特别容易让人懵。

你发现一个日志文件占了20GB。

直接:

rm app.log

然后再看:

df -h

结果空间几乎没变。

很多人会以为:

Linux没删成功。

其实很可能是:

文件目录项删了,但进程仍然持有这个文件描述符。

比如Java进程还在持续写:

app.log

你把文件名删了。

文件在目录里已经看不到。

但只要进程还握着fd,

对应的数据块就不会真正释放。

于是会出现:

文件没了。

du也找不到。

但:

df空间还是被占着。


六、这种deleted-but-open文件怎么查?

一个非常实用的办法就是:

lsof +L1

或者查包含:

(deleted)

的文件。

如果看到类似:

java 12345 app 6w REG ... 20G /logs/app.log (deleted)

那就很明显了。

文件虽然被删了,

但Java进程还在占。

真正释放空间通常需要:

正确让程序重新打开日志文件。

重载日志。

或者在可控情况下重启对应进程。

这也是为什么:

“删文件”不等于“空间已经释放”。


七、第三种情况:你看的不是出问题的那个挂载点

这也是线上特别常见的误判。

你执行:

df -h

看到:

/

还有40%。

于是觉得:

磁盘肯定没满。

但真正报错的目录可能是:

/var/log

/tmp

/data

/var/lib/docker

这些路径可能挂载在完全不同的文件系统上。

比如:

根分区还有40%。

但/var/log单独挂载的分区已经100%。

应用写日志时一样会报:

No space left on device

所以不能只看:

“机器总磁盘还剩多少”。

要看:

报错目录实际属于哪个Filesystem。


八、最快的方法:先确定报错路径属于哪个挂载点

比如报错发生在:

/data/upload/tmp

那就直接围绕这个路径检查。

不要泛泛地看整个机器。

你真正要确认的是:

这个目录所在Filesystem的:

容量。

inode。

挂载参数。

状态。

否则很容易出现:

你一直清理/

但真正满的是/data

这种低效排查。


九、第四种情况:容器宿主机有空间,但容器自己的写层满了

Docker、Kubernetes场景里,这个问题更容易出现。

你看宿主机磁盘:

还有很多空间。

但容器内部却已经写不动。

原因可能是:

Overlay文件系统。

容器可写层。

emptyDir。

临时卷。

日志层。

某个特定PVC

已经触碰自己的限制。

特别是:

应用大量写/tmp

或者不断产生日志、临时文件、缓存。

从宿主机总容量看:

还有空间。

但容器当前真正能用的那一层已经满了。

所以容器环境里一定要区分:

Host Disk

和:

Container Writable Layer / Volume

这两个不是一回事。


十、Kubernetes里还要注意emptyDir和Ephemeral Storage

很多服务会把临时文件写到:

/tmp

或者挂一个:

emptyDir

看起来简单方便。

但如果:

文件不断增长。

程序不清理。

或者一个请求会生成大量临时内容,

就可能消耗Pod的Ephemeral Storage。

最后你看到:

宿主机磁盘没满。

Persistent Volume也正常。

但Pod照样因为临时存储问题异常。

所以K8s排查时不能只看:

PVC。

还要看:

Ephemeral Storage使用情况。


十一、还有一种很容易被忽略的情况:日志轮转“看起来有,实际上没生效”

很多服务都配置了日志轮转。

比如:

每天一个文件。

保留7天。

超过大小自动切分。

理论上应该很安全。

但线上经常出现:

轮转规则没匹配到。

程序自己又持有旧文件。

压缩逻辑失败。

旧日志根本没删。

最后目录里堆了:

几千个文件。

甚至几十万个文件。

所以如果一个日志目录突然把inode吃满,不能只问:

“有没有logrotate?”

而要继续确认:

它到底有没有真正执行成功。


十二、最快排查顺序,我建议直接按这7步走

以后遇到:

“磁盘还有空间,却报No space left on device”

可以直接按这个顺序。

第一步:锁定报错目录

先确认到底哪个路径写失败。

第二步:看这个路径对应的Filesystem

不要只看根分区。

第三步:df -h

看Block空间。

第四步:df -i

看inode。

第五步:lsof +L1

检查deleted-but-open文件。

第六步:统计大目录和小文件数量

确认是大文件吃容量,还是小文件吃inode。

第七步:如果在容器里,再看Overlay、emptyDir、Ephemeral Storage和PVC

这套顺序比:

“一上来找大文件删”

有效得多。


十三、具体怎么修,要看是哪一类问题

如果是:

inode耗尽

清理海量小文件。

优化临时文件和日志生成策略。

必要时调整文件系统设计。

如果是:

deleted-but-open

找到持有文件的进程。

正确触发日志重开、reload,或者在可控情况下重启进程。

如果是:

某个单独挂载点满了

清理对应挂载点。

不要去清别的分区。

如果是:

容器写层满了

检查写路径是否应该迁移到Volume。

减少容器层写入。

设置合理清理机制。

如果是:

日志轮转失效

修轮转规则。

确认程序和logrotate的协作方式。


十四、为什么不建议直接“rm -rf 一堆目录”?

因为线上空间问题最容易引发第二次事故。

比如误删:

正在使用的数据。

缓存索引。

运行时文件。

数据库临时目录。

应用依赖文件。

所以真正修之前应该先回答:

这些文件是谁创建的?

现在谁还在使用?

删除以后能不能自动重建?

是否影响正在运行的服务?

如果只是为了快速腾空间,最好也先明确:

哪一类文件是安全可删除的。

而不是见目录大就删。


十五、修完以后怎么验证?

不能只看:

df -h降下来了。

至少还要一起看:

  • df -h是否恢复;
  • df -i是否恢复;
  • deleted-but-open是否清零;
  • 报错目录是否重新可写;
  • 日志是否继续正常轮转;
  • 文件数量是否还在异常增长;
  • 容器临时存储是否稳定;
  • 重启后问题会不会再次出现。

真正的目标不是:

“现在能写了。”

而是:

这个资源耗尽路径已经被切断。


十六、一个很典型的现场

比如你看到:

根分区:

60%。

第一反应:

磁盘还有40%。

但继续查:

df -i

发现:

/var/log

inode已经100%。

再统计文件数:

某个服务每秒生成几十个独立小日志。

一天就是几百万个文件。

这时候根因就很清楚:

不是磁盘容量不够。

也不是Kubernetes有问题。

而是:

文件数量把inode提前耗尽了。

这类问题如果不先看inode,

再怎么删几个大文件都解决不了。


十七、这种Linux磁盘排查,Plus和Pro怎么判断?

如果你平时只是偶尔让 ChatGPT、Codex:

分析一次磁盘告警。

看看df -h、df -i。

帮你读一下lsof。

辅助判断Docker、K8s写层问题。

这种任务比较集中,Plus通常已经够用。

关键还是把Evidence给完整:

报错路径。

Filesystem。

Block使用率。

inode使用率。

容器信息。

日志目录。

文件数量。

这些信息比只给一句:

“服务器磁盘报错了。”

有用得多。


如果你的日常已经变成:

持续排查Linux、Docker、Kubernetes、JVM、数据库、网络问题。

一次线上事故需要同时分析:

系统日志。

容器日志。

磁盘。

inode。

文件句柄。

Pod事件。

应用代码。

还要让Codex继续改清理逻辑、日志策略、容器配置并做回归,

这种高频、多轮、长时间工程排障已经成为日常,

那Pro会更适合。

所以Plus还是Pro,不看:

“这个磁盘问题难不难。”

而是看:

ChatGPT、Codex每天是不是已经持续参与完整的线上排障链路。

偶发排查、短任务:

Plus通常够用。

持续系统治理、多轮分析和验证:

再考虑Pro。


最后

磁盘明明还有40%空间,

为什么服务却突然报:

No space left on device?

因为Linux里的:

“没空间”不一定只代表GB用完了。

还可能是:

inode没了。

错误挂载点满了。

文件删了但进程还占着。

容器写层满了。

临时存储耗尽。

日志轮转失效。

所以以后遇到这类问题,不要只盯着:

df -h

更完整的排查应该是:

Block → inode → Mount → Open File → Container Layer → Log Rotation

一旦把这几个层级拆开,

很多看起来特别矛盾的:

“磁盘明明还有空间,为什么却写不进去?”

其实很快就能定位。

真正的问题往往不是:

磁盘到底还剩多少GB。

而是:

当前这个写入路径,到底还剩多少真正可用的文件系统资源。

持续分享 Codex、大模型开发与 AI 编程实战内容。
长期深度使用各类代码大模型,也整理了
稳定的Plus/Pro会员订阅渠道,有需要可自取。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询