
很多“磁盘还有几十 GB,却提示 No space left on device”的故障,最后并不是容量问题。Linux 文件系统同时受 block 和 inode 两套配额约束;日志文件被删除但进程仍保持打开,也会继续占用空间;应用写入的目录还可能位于另一个已经满掉的挂载点。下面按现象把这三类情况拆开,命令都可以直接在服务器上执行。
先确认到底是哪一层满了
先看整体容量和 inode 使用率:
df -hT
df -ih
df -hT 查看 block 使用情况,df -ih 查看 inode。两个输出都要关注 Use%,并记录报错目录所在的文件系统。例如 /var 单独挂载在 /dev/vdb1 上,那么根目录 / 还有空间,并不能说明 /var 能继续写入。
再把目标路径解析到实际挂载点:
df -hT /var/lib/docker
findmnt -T /var/lib/docker
如果应用写入 /data/uploads,就把同一个路径传给 df 和 findmnt。不要只看 df -h / 的结果。
block 满了:找大目录和大文件
当 df -hT 接近 100%,先从目录层面定位增长点。下面的命令只统计当前文件系统,避免跨入 /proc、其他数据盘或网络挂载:
sudo du -xhd1 /var 2>/dev/null | sort -h
sudo du -xhd1 /var/log 2>/dev/null | sort -h
sudo find /var -xdev -type f -size +1G -printf '%s %p\n' 2>/dev/null | sort -n
常见来源包括未轮转的应用日志、Docker json-file 日志、临时导出文件、数据库 binlog 和备份压缩包。确认文件用途后再处理。直接删除正在使用的数据库文件或业务日志,可能造成数据损坏或丢失审计记录;应优先使用应用自己的清理机制、日志轮转或备份策略。
如果是 systemd journal 占用过大,可以先查看:
journalctl --disk-usage
sudo journalctl --vacuum-time=14d
--vacuum-time 只会清理达到保留时间条件的归档日志,执行前要确认保留周期符合排障和合规要求。
inode 满了:容量没满也无法创建文件
一个目录里的大量小文件会先耗尽 inode。典型场景是缓存目录、PHP session、邮件队列、缩略图和失败任务留下的临时文件。判断方式是:df -hT 还有余量,但 df -ih 的 IUse% 接近 100%。
查找 inode 数量异常的目录:
sudo du --inodes -x -d 1 /var 2>/dev/null | sort -n
sudo du --inodes -x -d 1 /var/lib 2>/dev/null | sort -n
继续向最大的目录下钻,直到找到具体的缓存或临时目录。清理前先确认文件的创建者和生命周期;如果目录由服务管理,停用对应写入任务或调用服务提供的清理命令,再删除已经确认无用的文件。
ext4 可以在创建文件系统时设定 inode 数量,后续通常不能像扩容 block 那样直接补充 inode。因此长期方案是减少小文件数量、调整缓存策略,或把数据迁移到按需规划过 inode 的文件系统。XFS 的 inode 分配机制不同,但“大量小文件导致元数据压力”的排查思路仍然适用。
deleted 文件仍然占用空间
进程打开文件后,即使文件名已经被删除,文件内容仍会保留到最后一个文件描述符关闭。于是 du 看不到它,df 却显示空间没有释放。
用 lsof 找出这类文件:
sudo lsof +L1
sudo lsof -nP +L1 | sort -k7 -n | tail -n 20
重点看 SIZE/OFF、进程名、PID 和文件路径。确认是可以重新打开日志的服务后,优先使用服务支持的 reopen 或 reload 机制。例如部分程序可以通过发送 HUP 重新打开日志;systemd 管理的服务则应查看该服务的日志配置和重载方式。不要因为看到 (deleted) 就直接 kill -9,先判断进程是否承载在线请求、数据库连接或队列任务。
挂载点和容器场景容易误判
宿主机的 / 有空间,不能推出 Docker 数据目录所在文件系统有空间。先检查 Docker 根目录:
docker info --format '{{.DockerRootDir}}'
df -hT "$(docker info --format '{{.DockerRootDir}}')"
sudo du -xhd1 /var/lib/docker 2>/dev/null | sort -h
容器内的 df -h 反映的是容器看到的挂载和存储层;bind mount、Docker volume 和容器可写层可能对应不同位置。应用报错路径如果来自 bind mount,应在宿主机上检查实际源目录;如果是容器可写层,则要结合镜像、日志驱动和容器生命周期处理。
一个可复用的判断顺序
先对报错路径执行 df -hT PATH 和 df -ih PATH,分别确认 block 与 inode;再用 findmnt -T PATH 验证挂载点。block 满了就用 du -x 找目录,inode 满了就用 du --inodes 找小文件,df 与 du 差距很大时再查 lsof +L1。这个顺序能把“空间够但写不进去”拆成可验证的几条路径,避免反复删除无关文件。




