news 2026/10/17 11:14:14

磁盘满、inode耗尽与临时目录:No space left on device报错排查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
磁盘满、inode耗尽与临时目录:No space left on device报错排查指南

磁盘满了怎么办:一次"No space left on device"报错的完整复盘

遇到Could not install packages due to an EnvironmentError: [Errno 28] No space left on device这个报错时,第一反应通常是"磁盘满了,清理一下就完事"。但实际处理过几次后发现,这个问题远没有表面看起来那么简单——它可能是磁盘真的满了、可能是某个分区满了、可能是inode耗尽、甚至可能是临时目录挂载在内存盘上导致容量受限。我踩过几次坑之后,把整个排查和解决过程完整梳理了一遍,希望能帮你在下次遇到这个报错时少走弯路。

先说说这个报错的典型场景:正准备pip install一个包,结果终端里直接抛出了上面这行红字,安装过程瞬间中断。如果你用conda或者mamba,也会遇到类似的提示。报错信息本身已经明确告诉你是"设备上没有剩余空间",但这只是表象,真正要搞清楚的是"哪里满了"以及"为什么满"。

这篇文章适合谁看?刚入门的开发者也好,有几年经验的运维和数据处理人员也好,只要你的工作环境里需要装Python包、跑机器学习训练或者构建项目依赖,这篇内容都能给你一个完整的排查思路。我不仅会讲排查步骤,还会把我在实践中遇到的各种边缘情况和对应解法一并分享出来。

1. 为什么一个小小的安装动作会碰上磁盘空间问题

1.1 问题表象:不是所有"空间不足"都是同一个原因

许多人在第一次遇到[Errno 28]时,会习惯性地执行df -h查看一下磁盘使用率。如果发现根分区确实满了,那自然是清理。但问题是,在某些情况下磁盘明明显示还有几十G的空余,安装依然报错——这时候就要往更深层次的方向去查了。

我自己的经历里,有一个非常典型的案例:某次我在一台云服务器上给Python环境装运算库,磁盘使用率只有40%,但安装始终失败,报的错就是这个。折腾了半天,最后发现是因为/tmp目录被单独挂载,而且挂载的是一个只有1G的小分区。pip在解压和编译包的过程中会把临时文件写到/tmp,小分区迅速被占满,于是安装就中断了。这个案例给我最大的启发是:处理这类问题,第一步不是急着清理文件,而是要先把磁盘的整体布局看清楚。

另一个容易被忽略的情况是inode耗尽。如果磁盘上存在大量的小文件(比如某些项目里的缓存文件、日志碎片),inode会被消耗完。inode是文件系统用来记录文件元信息的存储结构,可以理解为"文件的索引号"。当inode耗尽时,即使磁盘还有空闲容量,系统也没有办法再创建新文件和目录了。这种场景下df -h显示的空间是充裕的,但df -i会告诉你inode使用率已经100%。

1.2 安装过程中空间消耗的流转路径

要彻底理解这个报错,就得清楚安装包时数据是怎么流动的。以pip为例,整个安装过程大致是:先下载源包或者二进制包到缓存目录,然后解压到临时目录,接着执行编译或构建流程(如果是在本机构建),最后再复制到site-packages目录。

这个流程里的每一站都涉及磁盘写入:

  • 缓存目录:默认在用户目录下的.cache/pip, Windows 下是C:\\Users\\xxx\\AppData\\Local\\pip\\Cache, Linux 下是~/.cache/pip。如果你用conda,那对应的就是pkgs目录。
  • 临时目录:主要是/tmp,有些系统上由TMPDIR环境变量指定。pip下载完包之后会在这里解压,编译过程也会产生中间文件。
  • 目标目录:也就是site-packages或conda的envs目录。大包(比如PyTorch这种几个G的安装包)复制进去会需要较大空间。

所以排查时不能只盯着一处,得把这几条路径的空间都看一下。

2. 系统排查:从准确诊断到定位占用源头

2.1 完整排查命令清单与解读

排查思路要按由宏到微的顺序推进,我这里列一套我实际常用的命令组合:

df -h

这条命令查看文件系统总容量、已用容量、可用容量、挂载点。重点要确认的是报错进程的工作目录所在分区使用了多少,同时观察是否存在多个挂载点各自为政的情况。比如根目录挂在/上,而/tmp或/home可能单独挂在另外的设备上。

接着看inode情况:

df -i

df -i与df -h的输出结构类似,只是把空间换成了inode的用量。这一步是为了确认是否存在"文件数爆了但空间没爆"的异常情况。

查完总量之后,要找具体的占用大头。先用下面这条命令看当前目录下的空间占用:

du -sh * | sort -rh | head -20

du -sh *把当前目录下每个子目录/文件的大小汇总成人类可读的格式,再经过sort -rh按大小倒序排列,head -20取出最大的前20个。这是找出目录里"大户"最直接的方式。

如果想看全盘的目录大小分布,比如根目录下哪些一级目录占用了空间,可以这样:

du -x -h --max-depth=1 / 2>/dev/null | sort -rh | head -30

-x参数表示不跨越文件系统边界,也就是说只统计根分区本身,不会跑到其他挂载点里去。这个参数在排查跨分区问题时特别好用。

对于容器场景,还要额外检查overlay2目录:

docker system df

如果容器运行时产生了大量镜像、容器层、卷、build cache,用这条命令能一目了然地看到空间消耗分布。

2.2 存活进程占用与隐藏目录的检查

有时候空间确实被某个被删除了但还被进程占用的文件耗着。这种情况常见于日志文件:你删了一个正在被服务写入的大日志文件,但服务进程还持有该文件的文件描述符,空间并不会真正释放。找这类进程可以用:

lsof +L1

这个命令可以列出已被删除但仍被打开的文件。+L1参数的含义是只显示link count小于1的文件——也就是说文件已从目录结构中移除但仍有进程在使用。如果发现大量空间被这类文件占用,重启对应的进程或者把进程kill掉,空间就释放了。

另外别忘了检查用户目录下的隐藏目录。Python开发者的用户目录下,.cache这个目录常常会被忽视。在设备上跑东西跑得久了,.cache里可能存了远超预期的数据。处理方式在后面第3节会详细展开。

2.3 空间已够但装不上:inode和挂载边界问题

先看inode的情况。你可以在项目目录里快速创建一个测试文件验证:

touch testfile

如果提示No space left on device,而df -h显示有空间,那基本可以判定是inode耗尽。解决方式也直接,找出大量小文件所在位置并清理。

至于挂载边界的问题,这里要特别提醒一下:用df -h时要留意"Filesystem"这列,如果根目录和/tmp对应的Filesystem不一样,说明/tmp是独立的挂载点。独立挂载的/tmp分区如果容量很小,就需要重点检查它的占用。有些系统在云平台上默认只给/tmp分配很小的空间,而pip的构建流程恰恰重度依赖这个目录。

3. 快速回收空间:一套顺手可用的清理手段

3.1 pip和conda的缓存清理,但别一刀切

清理pip缓存是最直接的一个方向,命令很简单:

pip cache purge

pip cache purge会清空所有pip缓存轮子文件。这个操作本身是安全的,只是清掉后下次安装相同的包需要重新下载而已。如果系统中存在多个Python环境,或者你是用pip --user方式装的包,缓存位置可能在各个用户目录下,都需要分别清理。

conda的清理命令略有不同:

conda clean --all

conda clean --all会清掉索引缓存、锁文件、未使用的包压缩包和临时文件。和pip的purge一样,清理后不会影响已安装环境的功能。需要注意的一点是,conda在安装新包时会用pkgs目录里的缓存来加速环境创建,清掉缓存之后,新建环境时的速度会变慢,但不会出错。

还有一个细节:不要在这种时期急着升级一个大版本的环境。如果conda环境里的包版本和当前Python版本不太匹配,conda会尝试重新解压多个包来构建新环境,这个过程的临时文件体积可能是环境本身的好几倍。空间紧张时做这种操作,极容易再次撞上同一个报错。

3.2 临时文件目录的定向清理

清理/tmp目录要用一种"聪明"的方式。直接rm -rf /tmp/*在某些场景下会有风险,因为可能有其他正在运行的程序正在使用/tmp里的文件。更稳妥的做法是只清理超过一定时间的临时文件:

find /tmp -type f -atime +3 -delete find /tmp -type d -empty -delete

第一条命令找到/tmp下最后访问时间在3天前的文件并删除,第二条清理空目录。这个做法兼顾了安全性和效果——正在使用的文件通常访问时间新鲜,不会被误伤。如果/tmp的容量还是不够,可以重启一下机器,Linux系统重启会默认清空/tmp下的文件。

3.3 日志文件与容器空间的回收技巧

日志往往是磁盘空间的隐形杀手。在排查时,我会先看两个地方:

du -sh /var/log/* | sort -rh | head -10

日志文件如果确认是历史遗留,可以清理。对于正在被进程写入的日志,直接删除文件不会释放空间(进程还持有句柄),建议用truncate清空而不是rm:

truncate -s 0 /var/log/某个服务.log

truncate -s 0会把指定文件的大小改成0字节,但保留文件本身的inode和文件描述符,正在写入的进程不会受到影响。

容器环境下的空间回收按下面的顺序来:

docker image prune -a docker container prune docker volume prune

docker image prune -a会删除所有未被任何容器使用的镜像,docker container prune清掉所有已停止的容器,docker volume prune删除没有容器使用的卷。这一套组合拳下来,一般能回收可观的空间。做完之后别忘了用docker system df确认空间回收效果。

3.4 用户目录常见占用大头一瞥

在我大量排查这类故障的经验里,用户目录下常见的空间占用大户主要有三类:

  • ~/.cache:pip、conda、Hugging Face等工具都会往这里写缓存。特别是Hugging Face的模型下载缓存,动辄几个G甚至几十G。
  • ~/.local:很多以--user方式安装的Python包会放在这里,还有一些编辑器插件的扩展数据。
  • ~/miniconda3或~/anaconda3:conda本体以及它的所有环境都在这里,环境多了之后空间消耗十分可观。

自查时,可以直接从这个目录开始看占用:

du -sh ~/.cache ~/.local ~/miniconda3 2>/dev/null

如果发现~/.cache特别大,可以先看看里面占用最多的是哪一项,再针对性地删。比如huggingface的缓存可以按数据集或模型删除,而不是整个目录清空。

4. 拆解临时目录困局,并解决多环境下载异常

4.1 给临时目录单独扩容:一边挂verified风险,一边保构建速度

在某些生产环境里,/tmp挂在独立小分区上,而且分区类型可能是tmpfs——也就是把内存当磁盘用。tmpfs的好处是速度快,坏处是容量受内存限制。举个例子,如果用一个小内存实例跑构建任务,tmpfs默认大小可能是物理内存的一半,在装PyTorch这种大包时,临时文件很可能直接把tmpfs给撑爆。

解决思路有几种:

一是将TMPDIR环境变量指向一个空间充足的目录。在运行安装命令前设置环境变量,让pip把临时文件写到别处:

mkdir -p /data/tmp export TMPDIR=/data/tmp pip install some-large-package

注意设置TMPDIR时要确保目标目录所在分区有足够空间,并且在目标目录里留足写入权限。bash或zsh用户还可以把export TMPDIR=...写进~/.bashrc或~/.zshrc,长期生效。但如果是为了排查临时问题,不建议改全局配置,排查完恢复默认更稳妥。

二是对/tmp分区本身做扩容。如果/tmp是一个逻辑卷(LVM),可以用lvextend扩展容量;如果它是一个独立磁盘分区,可以调整分区表。但扩容操作涉及系统底层,操作前务必确认自己有系统权限和备份,否则在没把握的情况下,优先使用改TMPDIR的方式更安全。

4.2 缓存目录设到项目内:换一种增量管理方式

除了解决临时目录不够的问题,缓存目录的迁移也是一种管理策略。如果你偏向更精细地控制缓存,可以给pip指定缓存到项目内部目录:

pip install --cache-dir=/project/.pip-cache some-package

这样缓存放到了项目目录下,不占系统分区空间。项目结束后可以随目录一起归档或者清理,不会遗留一堆缓存垃圾在系统目录里。

不过要注意:如果项目目录本身在空间紧缺的分区上,那这个方法会把问题转移到项目分区上。选择前先确认目标路径所在分区的剩余空间。

4.3 处理多环境场景下的空间配置问题

多环境场景下的空间管理,比单环境要复杂一个维度。可能同时存在python venv、conda环境、还有pipenv或poetry的虚拟环境。这里分享几个我实际用过的策略:

先做总量确认,用一条命令列出所有环境的体积:

du -sh ~/venv/* ~/miniconda3/envs/* ~/.venv/* 2>/dev/null | sort -rh

再针对不再使用的环境做删除。conda环境的删除命令是:

conda env remove -n 环境名

如果只是临时不用某个环境,并不想彻底删除,可以把它打包成tar.gz存到对象存储或外部磁盘上,需要时再解压还原。我一直在用的一种方式是给每个项目环境做一个"干净需求清单":环境里只装项目真正依赖的包,不随手装各种调试工具。这样环境体积能小不少,也有利于复现一致性。

4.4 磁盘配额:一个经常被忽略的隐藏约束

在共享服务器上,空间不足有时不是因为全盘满了,而是账户配额被用完。用下面的命令检查当前用户是否设置了配额限制:

quota -s

输出里如果显示No quotas running或者没有提示,说明没有配额限制。但如果显示了usage和limit,说明确实有配额。这种场景下,无论怎么清理第三方缓存都没用,必须删掉自己账户下的数据或者找管理员提高配额。我个人经验里,在高校、实验室的共享集群上,配额问题占比相当大,而且排查起来容易走弯路。

5. 根分区和其他分区的极限情形与系统视角

5.1 根分区在上层目录被占满时的定向定位思路

根分区被占满是最常见的一种"死法"。问题是,根分区里半数的空间可能被那些你不一定知道的一级目录占用着。我找占用大头的思路是:

du -h --max-depth=1 -x / 2>/dev/null | sort -rh | head -20

这条命令的作用是扫描根目录下的一级子目录,按空间占用倒序排出前20名,-x防止跨越挂载点。扫描结果出来后,顺着最大的目录逐级往下追查。有些目录一眼能看出是干嘛的(比如/var/lib/docker、/usr),有些则需要进一步确认。

在这个环节,如果大量空间消耗源于某个服务的数据库或者日志,可以针对性清理。我遇到过几次根分区占满,是因为某个统计服务积累了海量日志,没人处理。找到之后用truncate -s 0清空日志文件,同时配置好日志轮转策略,空间问题就迎刃而解了。

5.2 文件系统层面的扩展与修复实操

如果空间清理后仍然不能满足需求,那就需要从文件系统层面做扩展了。这里分几种情况看:

  • LVM管理的根分区:扩展逻辑卷的大小,接着扩展文件系统。
  • 普通分区:用分区工具调整分区大小,需要先卸载或者使用支持在线扩展的方式。
  • 云主机:在云控制台里先对磁盘扩容,然后进入系统内部扩展分区和文件系统。

以常见的ext4文件系统为例,如果根分区是LVM逻辑卷,扩展逻辑卷和文件系统的过程是:

lvresize -L +10G /dev/系统卷组名/根逻辑卷名 resize2fs /dev/系统卷组名/根逻辑卷名

注意resize2fs对应ext4文件系统。如果你用的是XFS,扩展命令要用xfs_growfs:

xfs_growfs /

在还没有完全确认文件系统类型时,先执行命令blkid或df -T查一下类型,避免用错工具。

对于云服务器场景,控制台扩容磁盘后,云厂商一般会有配套的扩展文档。核心步骤就是:先在控制台把云盘扩容,然后在系统内部重新扫描磁盘大小,最后调整分区表和文件系统。

5.3 系统盘目录瘦身建议:哪些东西可以安全删除

很多系统盘空间不足的机器上,往往会躺着一些"食之无味,弃之可惜"的组件。如果你也处在这样的状况里,可以考虑优先清理下面这些内容:

  • 旧版本内核:内核更新后,旧内核其实不会自动删除。可以执行apt autoremove或yum autoremove清理不再需要的内核包。但保留最近两三个版本的内核作为回退保证,不建议全部清掉。
  • apt/yum缓存:apt-get clean和yum clean all清理的是软件包缓存,不会影响已安装软件。
  • /var/cache:里面如果有构建缓存或下载缓存,也可以删。
  • systemd日志:journalctl默认持久化存储在/var/log/journal下,长期不清理会非常大。可以使用journalctl --vacuum-time=30d把30天前的日志清掉,或者--vacuum-size限制日志总大小,这个命令按日志时间线清,风险小。

再做一层保护,建议在清理前对关键配置做一份备份。比如/etc/fstab,如果扩容后重启出现挂载异常,这个文件可是救命的关键。

5.4 特殊挂载导致的"假满"现象

有一种情况特别迷惑:df -h看着空间还剩很多,安装包就是报空间不足。除了前面提到的inode问题外,还有一种可能就是挂载选项里带了size限制。例如tmpfs挂载时可以指定大小:

mount -t tmpfs -o size=1G tmpfs /some/path

这样即使系统内存和磁盘都有大量空余,/some/path分区却只能使用1G。遇到这种情况,要把对应的挂载项在/etc/fstab里查找并调整其大小参数,然后重新挂载。注意fstab的修改需要格外小心,错误配置可能导致开机异常,改之前先做好备份。

6. 实用技巧与总结经验:从应急到长远

6.1 把大包依赖分层安装,稳字当头

在处理空间不足问题的过程中,我发现一个实用的安装策略:大依赖分层装比一把梭更稳。比如需要安装一个机器学习包加上一堆辅助库,可以把最占空间、最可能出问题的包先单独安装,确认通过后再安装其他小包。这样如果中途出现空间不足,报错点会更明确,不会因为所有包同时下载解压把临时空间瞬间吃光。同时,单独安装大包能让缓存分得更清,以后需要针对性清理时也更方便。

6.2 给磁盘空间做监控和预警

空间问题的处理是应急,更重要的是预防。我通常会在生产环境配置一个简单的磁盘监控脚本,每10分钟检查一次使用率,超过阈值就告警:

#!/bin/bash CURRENT=$(df / | awk 'NR==2{print $5}' | sed 's/%//g') if [ "$CURRENT" -gt 85 ]; then echo "磁盘使用率超过85%:$CURRENT%" fi

把这个脚本加入crontab,让它每10分钟执行一次。阈值可以根据自己的情况调,但对于生产环境,我个人习惯在80%到85%的时候就开始处理,不要真等到打满再抢救。同时把日志轮转配置好,无论用logrotate还是其他方案,确保日志不会无限增长。

6.3 安全交叉自检:真实复盘

这个报错看似只有一句话,但背后涉及的问题分层非常多。

  • 第一层:磁盘空间真的不够了。直接用df -h就能判断。
  • 第二层:存在小分区或独立挂载点限制了某个目录的使用。查df -h的输出要逐个挂载点看。
  • 第三层:inode耗尽。用df -i验证。
  • 第四层:挂载参数或配额限制了写入。检查挂载选项和配额设置。
  • 第五层:容器层、镜像层堆积导致的写入困难。用docker system df检查。

如果按这个顺序排查,基本能把问题定位到具体环节,不会绕弯路。

6.4 一个小习惯:装包前先看空间

最后分享一个值得培养的习惯:在安装任何大型软件包之前,先看一下空间和临时目录的情况,几十秒的事情,却能省下后面白折腾的大把时间。习惯成自然之后,这套流程就像肌肉记忆一样顺滑。

df -h /tmp /加上df -i,两条命令在同一屏输出,把关键信息一次看全。如果是容器环境,再顺手加一条docker system df。养成这个习惯后,再遇到[Errno 28]的概率会低很多,就算遇到也知道从哪个方向排查。

这套方法在数据分析环境、Web服务环境、深度学习训练环境上都验证过,适用场景覆盖得挺广。你可以在自己的机器上对照着做一次全面检查,把隐患提前解决掉。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 11:10:56

原生HTML+CSS+JS实现高性能滑动导航栏

1. 项目概述:为什么一个“滑动导航栏”值得花时间深挖?“滑动导航栏效果 htmlcssjs”——这十个字看似简单,但背后藏着前端交互设计中一个极其高频、极易被低估、又特别容易翻车的核心模块。我带过不少刚入行的前端新人,也帮某高校…

作者头像 李华
网站建设 2026/10/17 11:10:38

YOLACT++实时实例分割:原型mask与系数组合的工程实践

1. 从Mask R-CNN的痛点说起:为什么实时实例分割这么难做计算机视觉的同行大概都有体会,实例分割这个任务在2019年之前一直处在一个很尴尬的位置。Mask R-CNN精度确实能打,COCO榜单上常年霸榜,但那个速度实在让人爱不起来——一张1…

作者头像 李华
网站建设 2026/10/17 11:10:34

反向传播原理与梯度流:从链式法则到梯度消失实战

反向传播这四个字,几乎每个人在训练神经网络的第一天就会碰到。无论是敲下 loss.backward() ,还是手动算梯度,本质上都在做同一件事:把损失函数关于模型参数的变化率算出来。但你真的跟它“熟”吗?我在社区里看到太多…

作者头像 李华
网站建设 2026/10/17 11:09:37

Unity跨平台自定义光标与NPC交互状态机实现

1. 这个需求到底在解决什么实际问题?“Unity 设置游戏内鼠标形状和触碰NPC改变形状”——乍看是个小功能,但背后藏着的是玩家沉浸感的第一道门槛。我带过好几个学生项目,也参与过某高校实验室的跨平台教育类模拟项目X,几乎每回测试…

作者头像 李华
网站建设 2026/10/17 11:08:37

Web登录接口暴力破解防御验证实战指南

1. 项目概述:这不是“黑客教程”,而是一次面向安全从业者的防御能力验证实验“暴力破解实验步骤”这个标题,在当前网络环境中极易引发误解。我必须开宗明义地强调:本文所描述的,是在完全合法授权、封闭可控环境内&…

作者头像 李华
网站建设 2026/10/17 11:07:26

深入理解 PyTorch CrossEntropyLoss:原理、参数详解与工程实践

1. 从公式到直觉:CrossEntropyLoss到底在算什么 做分类任务的人,十有八九第一行损失函数写的就是 torch.nn.CrossEntropyLoss() 。用起来确实简单,把模型输出的logits和真实标签扔进去,调用一次 .backward() 就行了。但说句实…

作者头像 李华