跳到主要内容

技术教程

Linux No space left on device:数据块满、inode 满,还是文件仍被占用

验证与风险信息

适用环境
Alpine Linux 3.20 / Docker 24.0.7 / capped tmpfs
验证耗时
约 12 分钟
风险与回滚
df、findmnt、lsof 为只读取证;删除不可直接回滚,必须先确认对象归属、保留策略与备份
最后复核
2026-08-04
验证证据
4 MiB tmpfs 数据块写满后 ENOSPC 且 df -h=100%;64 inode 用尽时数据仍 0% 而 df -i=100%;分别释放资源后写入恢复。

Linux 报 No space left on device 时,“找最大的文件删掉”并不总是正确。文件系统可能是数据块用尽,也可能是 inode 用尽;还可能存在文件已经从目录删除、但仍被进程打开,导致 du 看不到而 df 仍显示空间被占用。正确诊断必须先确认发生错误的挂载点,再分别检查块、inode 和打开文件。

适用范围与先给结论

本文适用于常见 Linux 文件系统和容器宿主机上的 ENOSPC 故障。V2CE 复现实验使用 Alpine 3.20 与两个容器内 tmpfs:一个限制为 4 MiB 数据空间,另一个限制为 64 个 inode。两项限制都不会消耗完宿主机文件系统。

第一轮只读检查应包含:

findmnt -T /发生错误的路径
df -hT /发生错误的路径
df -i  /发生错误的路径
sudo lsof +L1

四条命令回答的是不同问题:路径落在哪个挂载点、数据块是否满、inode 是否满、是否存在已删除但仍打开的文件。只看根分区的 df -h / 可能错过单独挂载的 /var、容器卷或临时文件系统。

第一步:锁定真正失败的路径和挂载点

先从应用错误日志中找到具体路径,例如日志文件、数据库目录、上传目录或临时目录。然后运行:

findmnt -T /var/lib/my-app/data.db
stat -f /var/lib/my-app/data.db
df -hT /var/lib/my-app/data.db
df -i  /var/lib/my-app/data.db

如果目标文件尚不存在,对它的父目录执行检查。记录文件系统类型、设备、挂载点、总量、可用量和采样时间。容器内看到的路径可能是 overlay 层、bind mount、named volume 或 tmpfs,应同时从容器配置确认真实存储位置。

第二步:区分数据块满与 inode 满

数据块耗尽

df -h 的 Use% 接近 100%、Available 为 0,通常表示可分配数据块不足。先在同一文件系统内按一级目录统计,不跨越其他挂载点:

sudo du -xhd1 /var 2>/dev/null | sort -h
sudo du -xhd1 /var/lib 2>/dev/null | sort -h
sudo find /var -xdev -type f -size +500M -printf '%s %p\n' 2>/dev/null | sort -n

不要一开始就在整台机器运行无边界的递归扫描;繁忙磁盘上 dufind 本身会增加 I/O。先从错误路径所在挂载点和最可能增长的日志、缓存、上传、备份、数据库目录开始。

inode 耗尽

df -h 可能仍有大量空间,但 df -i 的 IUse% 已是 100%。这通常来自海量小文件,例如失控的 session、缓存分片、邮件队列或临时文件。此时删除一个大文件可能释放很多数据块,却只释放一个 inode。

在受控范围内统计文件数量,逐层缩小:

sudo find /var/lib/my-app -xdev -type f -printf '%h\n' 2>/dev/null \
  | sort | uniq -c | sort -n | tail -30

大型目录上该命令可能很慢;生产环境优先使用已有文件系统监控、应用指标或分批采样。确认文件生命周期和业务归属后再清理,不能仅凭数量多就批量删除。

第三步:解释 df 与 du 为什么可能不一致

当进程打开文件后,管理员从目录中删除该文件,目录项会消失,du 无法再遍历到它;但只要文件描述符仍打开,文件系统数据块就不会释放。检查链接数为 0 的打开文件:

sudo lsof +L1

重点记录进程、PID、文件描述符、文件大小和路径。优先让应用按正常方式重新打开日志,或在变更窗口滚动重启对应实例。不要不加判断地终止数据库等关键进程;它可能触发恢复、复制中断或更长停机。

此外,快照、文件系统保留块、用户配额、容器 overlay 层和存储驱动也会影响可用空间。若普通用户写入失败而 root 尚有少量空间,应检查文件系统保留与配额,不能通过长期以 root 运行应用解决。

第四步:先释放可控的应急空间

先暂停会继续快速写入的任务或摘除流量,再选择已确认可再生成、可归档或已过保留期的数据。常见候选包括应用缓存、已轮转且确认上传成功的旧日志、失败的临时制品和重复备份。每项删除都要有明确路径、时间范围和恢复来源。

对于 systemd journal,先查看实际占用和配置:

journalctl --disk-usage
systemd-analyze cat-config systemd/journald.conf

不要把直接清空当前日志、数据库文件或容器存储目录当成通用方案。Docker 宿主机也不应手工删除 /var/lib/docker 内部文件;应通过 Docker 自身的对象清单确认镜像、构建缓存、容器和卷的所有权。

第五步:恢复后找出增长来源

释放少量空间只能恢复写入能力。继续检查故障窗口内哪个目录、日志或指标增长最快,并处理根因:

  • 修复无限日志、异常重试或未设置上限的缓存;
  • 为日志配置轮转、压缩、保留天数和失败监控;
  • 为临时文件和 session 配置生命周期;
  • 把备份上传与本地清理做成可验证的两阶段流程;
  • 对块使用率、inode 使用率和增长速度分别设置告警。

容量规划要看增长速度和清理失败后的缓冲时间,而不是只在 95% 设置一个静态阈值。大量小文件系统必须单独监控 inode。

复核与回滚

处置后重复 df -hTdf -i 和应用真实写入检查,确认可用块/inode 已恢复、错误日志不再增长、数据库或队列没有遗留损坏。若执行了服务重启,还要验证健康检查、数据恢复和复制状态。

删除操作通常不可直接回滚,因此操作前必须确认备份或再生成方式;配置变更则保留旧配置并在语法检查后应用。若清理对象不确定,先移动到同一文件系统的隔离目录不会释放空间,只适合阻止应用继续读取,不能作为容量恢复手段。

V2CE 复现实验记录

数据块实验把 /lab 限制为 4 MiB。写入 8 MiB 文件时,dd 在写入 4 MiB 后返回 No space left on devicedf -h 显示 4.0 MiB 已用、0 可用、使用率 100%。截断隔离填充文件后,同一挂载点恢复为约 4.0 MiB 可用,并成功创建恢复标记。

inode 实验把另一个 16 MiB tmpfs 限制为 64 个 inode。创建第 64 个小文件时失败;此时 df -h 仍显示 16 MiB 可用、数据使用率 0%,而 df -i 显示 64/64、使用率 100%。释放 10 个实验文件后成功新建恢复标记,最终还有 9 个 inode 可用。这证明两种 ENOSPC 必须使用不同证据和处置方式。

参考资料

退出移动版