ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AutoDL存储用量高报错:系统盘清理、缓存迁移与落盘治理

AutoDL存储用量高报错:系统盘清理、缓存迁移与落盘治理

AutoDL 存储用量高这个报错,我在带新人的时候见过太多次了。现象往往很一致:昨天还能正常跑的实例,今天一开机就弹出提示说存储空间不足,或者点了保存镜像之后卡在某个百分比直接失败,更有甚者连实例都创建不起来,控制台一直提示磁盘配额超限。这个时候绝大多数人的第一反应是“我明明没存什么东西”,然后开始怀疑平台计费有问题。实际情况大概率是系统盘被 conda 环境、pip 缓存、模型权重下载缓存、训练日志这几样东西悄悄吃光了,而数据盘还空着一大半。这篇内容就是把这套问题的来龙去脉讲清楚:AutoDL 的存储是怎么划分的、报错到底在卡哪一步、怎么用几条命令快速定位占用大户、怎么从根上把该搬的东西搬到数据盘、训练场景下数据集和 checkpoint 该怎么放、以及我在实际操作中踩过的那些坑。不管你是刚接触云 GPU 的新手,还是已经跑了几个月实验的老手,这套排查和治理思路都能直接照着用。

1. 先摸清存储账本:报错到底卡在哪一层

很多人处理这个问题的姿势是看到报错就去删文件,删完发现空间没变多少,或者过两天又满了。原因很简单,没搞清楚平台的空间是怎么分层的,也不知道报错具体校验的是哪一块。AutoDL 这类算力租赁平台的存储不是一块铁板,而是分成了几个职责完全不同的区域,每个区域的容量、是否随镜像保存、清理方式都不一样。搞清楚这张账本,后面所有的操作才有依据,不然就是在黑箱里瞎试。

1.1 系统盘、数据盘、文件存储的职责边界

以常见的套餐规格为例,系统盘挂载在/root下,容量通常只有三十个 G 左右;数据盘挂载在/root/autodl-tmp,容量一般是五十个 G 起步,可以按需扩容;再往上是文件存储,挂载点类似/root/autodl-fs,属于跨实例共享的网络存储,需要单独开通;另外还有公共数据集目录/root/autodl-pub,只读,不占你的配额。这些具体数值以你控制台显示的为准,不同套餐会有差异,但分层逻辑是一样的。

真正的关键差异在于是否随镜像保存。你点“保存镜像”的时候,平台打包的是系统盘的内容,数据盘和文件存储不会被打进去。这就决定了一个非常反直觉的结论:系统盘既是容量最小的,又是唯一需要长期保存的,而数据盘容量大却不随镜像走。所以系统盘必须保持精简,只放代码、环境和你确实需要固化下来的配置;数据集、缓存、日志、checkpoint 这些体量大又不需要跨实例保留的东西,全都应该扔到数据盘去。我见过太多人把 HuggingFace 下载的几个 G 权重、几十个 G 的解压数据集全堆在/root下,系统盘不爆才怪。

还有一个容易被忽略的点:/root下的隐藏目录才是真正的空间黑洞。~/.cache里躺着 pip 的 wheel 缓存、torch 的预训练权重、transformers 的模型文件,这些目录用ls根本看不见,但动辄十几 G。新装的 conda 环境一个就是两三个 G,装三四个环境系统盘就告急了。这些内容在报错之前完全静默,等你发现的时候已经晚了。

1.2 报错触发时机与提示语的对应关系

平台不是随时都在检查空间,而是在几个特定动作上做校验,所以同一份占用在不同操作下表现完全不同。第一种是实例创建或开机阶段,平台需要把镜像解压到系统盘,如果镜像本身加上预留空间超过了配额,就会直接拒绝启动,提示大意是存储空间不足、无法创建容器。第二种是保存镜像阶段,打包过程需要额外的临时空间,系统盘占用超过某个阈值(经验值在百分之八十五到九十之间)就会中途失败,提示镜像保存失败。第三种是运行过程中写入失败,这时候平台未必拦你,但你的训练脚本会抛No space left on device,日志写不进去,checkpoint 存不下来,进程直接崩掉。

这三种场景的处理优先级是不一样的。如果卡在创建阶段,说明镜像本身或者系统盘的固化内容已经超标,必须瘦身镜像;如果卡在保存阶段,说明当前系统盘临时文件太多,清理缓存往往就能过;如果是运行中写入失败,那多半是你在往系统盘写大数据,属于路径选择错误,得改代码里的落盘位置。分清楚是哪一种,比盲目删文件有效得多。

提示:报错文案在不同版本的控制台上措辞会有差异,但核心语义都是“系统盘配额被突破”。看到“存储”“磁盘”“空间”“配额”这类字眼,先默认是系统盘问题去排查,命中率在九成以上。

2. 五分钟定位:把占用大户揪出来

排查这件事最忌讳的就是凭感觉删。正确的做法是先看总量、再看分布、最后看异常。整套动作熟练之后五分钟以内能完成,而且不需要装任何额外软件。下面这几条命令我基本上是肌肉记忆,每次新开实例第一件事就是跑一遍,心里有个底。

2.1 三条命令锁定空间分布

第一条df -h,看的是挂载点和总体使用率。输出里重点看/和/root/autodl-tmp两行,前者对应系统盘,后者对应数据盘。如果/的使用率已经到九十以上,那基本可以确诊了。这条命令还有个作用是确认数据盘到底有没有挂上、容量是多少,有时候你以为数据盘有五十个 G,实际套餐给的是二十个 G,认知偏差会误导后面的判断。

第二条du -h --max-depth=1 /root 2>/dev/null | sort -hr | head -20,这条是核心。它把/root下第一层目录按大小倒序排列,一眼就能看出是哪个目录在吃空间。2>/dev/null是必须加的,否则遍历到没有权限的目录会刷一屏报错,把有用的输出淹掉。跑完通常会发现几个熟悉的面孔:miniconda3或者anaconda3占几个 G,.cache占几个 G 到十几个 G,项目目录占几个 G。

第三条针对具体目录再下一层,比如du -h --max-depth=1 /root/.cache | sort -hr,看看是 huggingface 大还是 pip 大。pip 的缓存是最没用的,删了完全不影响任何东西;huggingface 的缓存删了下次还得重新下,所以要权衡,更好的做法是把它整体迁到数据盘去。

这里有个实操细节值得说:du在几十万个文件上跑会比较慢,如果你赶时间,可以用du -sh只统计总量先看个大概,再对可疑目录细查。另外du统计的是文件实际占用,和df的块级统计会有细微出入,有几百兆的差异属于正常,不用纠结。

2.2 那些“看不见”的占用:被删除却仍被持有的文件

有一种情况特别搞人心态:df显示系统盘百分之百,但du把/root翻了个底朝天加起来也只有十几个 G。这中间的差额去哪了?答案是已经被删除、但仍然被某个进程持有句柄的文件。

原理很简单,Linux 下删除一个文件,只是把目录项摘掉了,如果还有进程开着这个文件的句柄,磁盘块不会真正释放。典型场景是你跑了一个训练脚本,往日志文件里写了几十个 G,然后你rm掉了这个日志,但训练进程还在跑,空间就一直占着。Jupyter 里删大文件、后台 nohup 进程写日志、TensorBoard 的事件文件,都是高发区。

定位方法是lsof +L1或者lsof | grep deleted,输出里会列出那些link count为 0 但仍有进程持有的文件,后面跟着 PID 和大小。找到之后两条路:杀掉对应进程,或者重启实例。重启是最干脆的,所有句柄释放,空间立刻回来。我个人的习惯是遇到这种“账对不上”的情况,先把正在跑的任务 checkpoint 存好,然后直接重启实例,比一个个找进程快得多。

2.3 交互式排查工具 ncdu 的用法与取舍

命令行党可以装个ncdu,apt install ncdu或者用 conda 装都行,几十兆的安装包,换来的是一个可以用方向键上下翻、回车进目录的交互界面,找大文件比du直观太多。进入之后按c看文件数量,按s按大小排序,按g切换显示百分比。清理的时候直接在界面里按d就能删,省得来回敲路径。

不过ncdu有个前提,得先能装上去。系统盘已经满了的时候apt可能装不动,这时候可以先清一点/var/cache/apt或者/tmp腾出几十兆。另外ncdu全盘扫描也要时间,只在系统盘范围内扫就行,别去扫数据盘和公共数据集目录,那是在浪费生命。

注意:不管用哪种方式排查,都不要去动/root/autodl-pub和平台自己维护的隐藏目录,那些是只读挂载或者系统组件,删了可能影响实例启动,而且删了也不释放你的配额。

3. 从根上治理:该搬的搬,该删的删

定位到问题只是第一步,真正省心的是把结构调对,让空间不再反复告急。这里要区分两类东西:一类是可以直接删的,比如 pip 缓存、apt 缓存、临时文件;另一类是必须保留但放错位置的,比如模型权重缓存、conda 环境。前者一删了之,后者得迁移。迁移这件事看起来简单,但里面有几个坑足够让人折腾一下午。

3.1 缓存目录统一迁移的完整操作

这是收益最高、风险最低的一步。pip、HuggingFace、torch、matplotlib 这些库默认都往~/.cache或者用户目录里写,全部指向系统盘。把这些路径统一改到数据盘,能一次性释放好几个 G 到十几个 G,而且后续下载再也不会撑爆系统盘。

具体做法是在~/.bashrc里追加一组环境变量。HuggingFace 系的用HF_HOME和HF_DATASETS_CACHE,老版本 transformers 还认TRANSFORMERS_CACHE,两个都写上不冲突。torch 的预训练权重走TORCH_HOME。pip 的缓存走PIP_CACHE_DIR。还有一个兜底的XDG_CACHE_HOME,很多第三方库会读这个,把它指到数据盘能覆盖一大片漏网之鱼。matplotlib 的字体缓存比较特殊,它不认 XDG,得单独用MPLCONFIGDIR指定。

# 追加到 ~/.bashrc,路径按自己的数据盘目录调整 export CACHE_ROOT=/root/autodl-tmp/cache mkdir -p $CACHE_ROOT/{hf,pip,torch,mpl,triton} export HF_HOME=$CACHE_ROOT/hf export HF_DATASETS_CACHE=$CACHE_ROOT/hf/datasets export TRANSFORMERS_CACHE=$CACHE_ROOT/hf/transformers export TORCH_HOME=$CACHE_ROOT/torch export PIP_CACHE_DIR=$CACHE_ROOT/pip export MPLCONFIGDIR=$CACHE_ROOT/mpl export TRITON_CACHE_DIR=$CACHE_ROOT/triton export XDG_CACHE_HOME=$CACHE_ROOT

写完记得source ~/.bashrc,或者在新的 shell 里验证一下echo $HF_HOME有没有生效。有一点要强调:这些变量必须在启动任何 Python 进程之前生效,如果你是在 Jupyter 里改的,得重启 kernel 才管用。我在第一次搞这个的时候就是改完变量直接跑脚本,结果模型还是往老地方下,白白多占了几个 G,后来才发现是 kernel 没重启。

迁移完成之后,老的~/.cache目录可以直接删掉释放空间。但删之前确认一下没有正在跑的任务依赖里面的文件,稳妥点先mv到数据盘做个备份,跑通一个流程之后再彻底删。

3.2 conda 环境该不该搬到数据盘

这个问题是整套方案里最容易做错的一步,必须说清楚。理论上把 conda 的环境目录搬到数据盘能省下系统盘好几个 G,但这里有个致命前提:保存镜像只打包系统盘。如果你的环境在数据盘上,保存出来的镜像在新实例里是没有这个环境的,你开机之后会发现 conda 环境消失了,得重新装一遍。

所以我的建议是分两种情况。如果你的用法是用完即弃、不保存镜像,那环境放数据盘完全没问题,甚至更好,因为每次开机都能复用同一个环境。如果你的用法是保存镜像、后续反复基于这个镜像开实例,那环境必须留在系统盘,只能靠精简来省空间:只保留一个主环境,别装三四个平行的 conda 环境,依赖冲突用pip install在同一个环境里解决;装完包立即conda clean -a和pip cache purge,把安装过程产生的临时包全清掉;能用--no-cache-dir的就加上,别留缓存。

如果确实环境太多、系统盘扛不住,可以走“系统盘放一份最小环境 + 数据盘放完整环境”的双轨方案,镜像里只保留最基础的依赖,重活儿开机后从数据盘的环境跑。这种方案适合固定流程的长期项目,前期配置麻烦,但后续每次开机省事。

3.3 软链接方案的适用边界与死链风险

网上流传很广的做法是用ln -s把~/.cache软链到数据盘,配置量比环境变量少,看起来更省事。这个方案本身可行,但有两个边界必须知道。

第一,软链接的目标目录必须存在。保存镜像时,软链接本身会被打进去,但它指向的数据盘目录不会。新实例开机后,如果那个目录不存在,所有依赖这个链接的程序都会报错,你会看到一堆莫名其妙的“文件不存在”。解决办法是在启动脚本里加一行mkdir -p重建目标目录,或者干脆把这个软链接的创建写进开机初始化逻辑。

第二,软链接对某些程序不透明。绝大多数 Python 库读写文件走的是普通系统调用,软链接没问题;但有些程序会做路径规范化,或者在容器边界上出问题,导致软链接失效。我的经验是,能用环境变量解决的优先用环境变量,那是官方支持的机制,兼容性最好;环境变量覆盖不到的场景,再考虑软链接,而且一定要在镜像保存后实际开一个新实例验证一遍,别想当然。

4. 训练场景专项:数据集、日志与 checkpoint 的落盘策略

前面两节讲的是通用治理,这一节专门说训练场景。因为跑深度学习的人遇到的存储问题,八成以上出在数据集、日志和 checkpoint 这三样东西上,而且它们的特点是单个文件大、增长速度不可控,一旦放错位置,问题会在训练中途爆发,最难处理。

4.1 数据集解压位置的选择与压缩包清理

解压数据集是最常见的踩坑点。一个压缩包传上去,解压出来通常是原体积的两三倍,如果解压到/root下,系统盘当场就满。正确做法是把压缩包和减压后的目录都放在/root/autodl-tmp下面,解压命令里显式指定目标路径,别用默认的当前目录。

# 上传到数据盘后,在数据盘内解压,避免跨盘占用 cd /root/autodl-tmp/datasets unzip -q your_dataset.zip -d ./your_dataset # 解压完成后立刻删压缩包,双份占用是最常见的浪费 rm -f your_dataset.zip

还有一类数据是公共数据集,挂载在/root/autodl-pub下,只读,不占你的配额。如果你要用的数据刚好在里面,千万别复制到自己的目录,直接在代码里指向那个路径读就行。确实需要改动的,复制过去之后记得及时清理原始副本。

对于需要反复使用的数据集,更好的方案是放到文件存储里。文件存储的容量独立于实例,多个实例可以同时挂载,实例释放了数据还在,省去了每次重新上传的时间。代价是网络存储的读取速度比本地盘慢,如果训练任务是 IO 密集型的,还得先拷到数据盘再跑。这个取舍要看你的数据量和训练时长,数据几个 G、训练几小时,拷到本地跑更划算;数据上百 G、只做推理,直接读文件存储更省事。

4.2 checkpoint 与日志的落盘路径控制

训练脚本里写 checkpoint 的路径必须显式指定到数据盘。很多开源代码默认存在./checkpoints或者./runs,也就是你启动脚本的当前目录,如果那个目录在/root下,十几个 epoch 之后系统盘就满了。改代码的时候把这几个路径统一抽成一个配置项,指向/root/autodl-tmp/xxx,能省掉后面所有的麻烦。

TensorBoard 或者 wandb 的本地日志目录同理。事件文件单个不大,但训练几万步之后能堆到几个 G,而且它是持续写入的,不会自动清理。我的习惯是把logdir设到数据盘,并且写一个小的清理逻辑,只保留最近 N 轮的日志,老的直接删。这个逻辑二十行代码就能写完,但能避免很多半夜被报警叫醒的情况。

还有一种情况是多进程写日志。DataLoader 开了多 worker,每个 worker 都往同一个文件里写,除了内容错乱,还会产生大量的临时文件碎片。正确做法是用主进程写日志,worker 里只做数据加载。这个问题表面上是存储问题,根子上是代码结构问题,我在排查的时候遇到过好几次,日志目录莫名其妙多出几百个小文件,查了半天才发现是 worker 各写各的。

4.3 保存镜像前的瘦身清单

如果你打算把当前环境保存成镜像,保存之前必须走一遍清理流程,否则大概率会失败在百分之七十左右。这份清单我用了很久,基本能保证一次过。

清理项命令或路径预期释放
pip 缓存pip cache purge或删~/.cache/pip几百 M 到数 G
conda 包缓存conda clean -a -y一到两 G
apt 缓存apt clean或删/var/cache/apt几十到几百 M
临时文件清/tmp和/var/tmp不确定,可能很大
已删除未释放lsof +L1后重启实例可能几十 G
项目目录里的输出手动确认后删outputs、runs、*.pth视情况
Jupyter 检查点删.ipynb_checkpoints通常很小

清理完之后用df -h确认系统盘使用率降到百分之七十以下,再点保存。留出百分之三十的余量不是浪费,是因为打包过程本身需要临时空间,压缩、分层、写临时文件都要占地方。我最早不懂这个,卡在百分之八十五去保存,十次有八次失败,后来留足余量就再没出过问题。

注意:保存镜像前一定要确认所有你需要保留的东西都在系统盘里。数据盘上的内容不会被打包,镜像存好之后如果你把实例释放了,数据盘的东西也就没了。这个坑真的有人踩过,训练了两周的模型权重放在数据盘,镜像保存成功,实例一释放全没了。

5. 常见问题速查与踩坑实录

前面讲的是方法论,这一节把高频问题和实际踩过的坑整理成速查形式,遇到对应症状直接查表,能省掉不少试错时间。表里的排查顺序建议从下往上做,先看最省事的原因,再逐步深入。

5.1 高频报错与对应处置

现象高概率原因处置方式
开机提示存储不足,实例起不来镜像体积加上系统盘预留超配额基于旧镜像重开实例,清理后重新保存
保存镜像卡在某个百分比失败系统盘占用过高,无打包余量清理缓存,使用率降到七成以下再存
df满但du加起来没那么多已删除文件仍被进程持有lsof +L1定位,重启实例
跑着跑着报No space left on device日志或 checkpoint 写入系统盘改落盘路径到数据盘,重启任务
环境变量改了但缓存还在老地方shell 未重新加载或 kernel 未重启source ~/.bashrc,重启 Jupyter kernel
保存的镜像里没有 conda 环境环境装在数据盘,未随镜像打包环境迁回系统盘,或改用开机脚本重建
软链接在镜像中失效链接目标目录未随镜像保存开机脚本里mkdir -p重建目标目录
数据盘空间也不够了checkpoint 累积过多加保留策略,只留最优和最近若干轮

5.2 几条不太容易想到的经验

只在需要的时候开实例。云上按小时计费,很多人习惯开着一台机器挂着不关机,觉得方便。但存储问题和时间强相关,机器开得越久,日志、缓存、临时文件积累得越多,系统盘满的概率越高。跑完实验就关机,下次要用再开,环境在镜像里不会丢,数据在数据盘里也还在,反而更安全。

把清理写成开机脚本。与其每次手动排查,不如在~/.bashrc或者开机初始化里挂一个清理函数,开机自动清 pip 缓存、删过期日志、重建软链接目录。我现在的做法是维护一个小脚本,放在数据盘的固定位置,每次开机第一件事跑一下,五秒钟完成,之后一整天都不用操心空间问题。

警惕隐蔽的写入点。有些库会把中间结果写到当前工作目录,比如某些图像处理库的可视化输出、某些框架的编译缓存。这类写入不会报错,就是默默占空间,等你发现的时候已经几个 G 了。排查的时候在项目根目录跑一次du,看看有没有意料之外的目录冒出来,往往能找到惊喜。

别迷信“清理了就一定降下来”。前面提过,删除和释放是两回事。养成习惯:清理之后一定用df -h复核,数字没降说明有句柄没释放,别继续在删除上使劲,那是在做无用功。

容量规划要留缓冲。数据集大小、模型权重、缓存、日志加起来,最好控制在数据盘容量的六成以内,系统盘控制在五成以内。听起来很奢侈,但实际跑起来你会发现,各种临时文件和不预期的输出会迅速吃掉剩下的空间。留缓冲不是保守,是给意外留余地。

6. 收个尾:这套流程我实际跑下来的感受

这套东西我用了挺长时间,从最开始看到报错就手忙脚乱,到现在基本能做到开机五分钟内把空间状态摸清楚、遇到报错直接定位到具体目录。最大的转变其实不是学会了哪条命令,而是把“路径选择”这件事前置了:数据、缓存、日志、环境,每一类东西该放哪,在动手之前就想清楚,后面九成的问题根本不会发生。真正需要临时排查的,往往是自己新装的什么东西把路径写死了,或者某个第三方库偷偷往系统盘写东西。

还有一点体会比较深:不要等到报错才去治理。每次新开实例,先花两分钟df -h看一眼、配置好缓存路径,比事后花两小时排查划算太多。存储这件事属于典型的“平时不管、出事要命”,治理成本前置之后几乎为零,滞后的代价却可能是训练到一半崩掉、镜像保存失败重来。

最后分享一个小技巧:在你的项目目录下建一个scripts/文件夹,把常用的排查命令、清理脚本、环境变量配置都放进去,跟着代码一起进版本库。换实例、换机器、交接给别人的时候,直接跑脚本就行,不用再凭记忆去敲那几条复杂的du管道命令。这个习惯看起来很小,但确实能省下不少重复劳动,也能让团队里的人少踩一遍你踩过的坑。

返回列表