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-depth1 /root 2/dev/null | sort -hr | head -20这条是核心。它把/root下第一层目录按大小倒序排列一眼就能看出是哪个目录在吃空间。2/dev/null是必须加的否则遍历到没有权限的目录会刷一屏报错把有用的输出淹掉。跑完通常会发现几个熟悉的面孔miniconda3或者anaconda3占几个 G.cache占几个 G 到十几个 G项目目录占几个 G。第三条针对具体目录再下一层比如du -h --max-depth1 /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 的用法与取舍命令行党可以装个ncduapt 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 到数 Gconda 包缓存conda clean -a -y一到两 Gapt 缓存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管道命令。这个习惯看起来很小但确实能省下不少重复劳动也能让团队里的人少踩一遍你踩过的坑。