1. 场景与整体思路一次磁盘告警引发的标准操作先说清楚这篇博文是干什么的。标题里四个动作——swap扩容与删除、Hugging Face换目录、ffmpeg视频处理、清理空间——看起来互不相干但它们背后是同一个痛点开发机磁盘不够用了而且你还不知道东西都藏在哪。我这次处理的机器是一台 1TB 的 Linux 工作站日常跑深度学习模型推理、偶尔用 ffmpeg 做视频抽帧和裁切。某天收到磁盘告警df -h一看根分区已经用了 92%再往下查才发现三只“吃盘巨兽”系统 swap 分区分配太小且位置尴尬、Hugging Face 的模型缓存默认塞在~/.cache/huggingface里野蛮生长、以及一堆 ffmpeg 处理视频时留下的中间产物。这三个问题其实很有代表性。很多人以为磁盘空间不够就是删文件但真正专业的做法是先搞清楚“什么东西占空间、为什么占、能不能把目录挪走、能不能用工具链减少临时文件”然后再决定是扩容还是清理。这篇文章就是把我这次完整的处理流程、每一条命令背后的原因、以及踩过的坑都拆开来讲适合正在被磁盘告警困扰、或者想系统梳理一遍 Linux 开发环境空间管理的读者。整个处理流程我按“先扩容后清理、先挪目录后删文件”的顺序来做。逻辑很简单扩容是给系统更多余量换目录是把大文件挪到有余量的磁盘清理空间是把没用的东西干掉。顺序乱了容易出问题——比如你还没扩容就急着删删错了又没备份那就真回不来了。下面按四个部分逐个展开每个部分都包含可复现的命令和实操中的坑。2. swap交换空间先搞懂扩容和删除的正确姿势2.1 为什么需要动 swap它和 swapfile 文件方式的关系Linux 的 swap 本质上就是磁盘上划出来的一块区域当成内存用。当物理内存不够时内核会把不活跃的内存页换到 swap 里腾出物理内存给活跃进程。很多开发机在装系统时只给了很小的 swap 甚至没给跑大模型推理、编译大型工程、或者开多个 Docker 容器时内存一紧张就会直接 OOMOut Of Memory进程被内核杀掉非常崩溃。扩容 swap 有两种常见方式一种是动分区fdisk 调整分区表或 LVM 扩容另一种是用swapfile 文件。我强烈建议用 swapfile 而不是分区——原因有三个。第一swapfile 不需要动分区表风险低随时可以删除重建第二位置灵活可以放在任意有空间的挂载点上第三现代内核2.6 及以上对 swapfile 的性能已经优化得很好了对于开发机来说性能差异根本感知不到。我这里机器原来的 swap 是安装系统时自动分的只有 4GB而且是在根分区上。问题是根分区本身已经很满了再在原位置扩容等于雪上加霜。所以我的做法是在数据盘/data上创建 swapfile然后把旧 swap 删掉。这样既扩容了又把 swap 从紧张的根分区挪到了宽裕的数据盘一举两得。2.2 扩容实操创建 swapfile 的完整步骤与参数选择先说一个核心原则不要在旧 swap 还在用的时候就删它。你至少得有新的可用 swap 或者足够的内存余量才能删旧的否则系统可能直接卡死。下面是完整步骤# 第一步查看当前 swap 情况 free -h swapon --show # 第二步在 /data 下创建 32GB 的 swapfile # 用 fallocate 快速创建但注意某些文件系统如 ext4上 fallocate 创建的文件 # 存在空洞可能导致 swapon 失败稳妥起见可以用 dd sudo dd if/dev/zero of/data/swapfile bs1M count32768 statusprogress # 第三步设置正确的权限 sudo chmod 600 /data/swapfile # 第四步格式化为 swap 格式 sudo mkswap /data/swapfile # 第五步启用新的 swap sudo swapon /data/swapfile # 第六步确认结果 swapon --show这里有个细节值得展开为什么用dd而不是fallocatefallocate是瞬间完成的但它分配的是稀疏文件sparse file文件系统上记录的块可能没有真正写数据。swapon对这种情况有时会报 swapfile has holes 的错误。dd虽然慢32GB 在 SSD 上大约半分钟到一分钟但它真的把所有块都写了一遍绝对稳妥。我自己第一次用 fallocate 就踩过这个坑后来老老实实用 dd。如果你需要更大的 swap比如 64GB把count改成 65536 就行。但是有一个问题要提前想清楚swapfile 本身也占磁盘空间它只是把内存压力转移到磁盘上并不能解决磁盘不够的根本问题。所以我的建议是 swap 大小设置为物理内存的 1 到 2 倍即可不要贪大。2.3 旧 swap 的安全删除与开机自动挂载配置新 swap 启用之后就可以处理旧的了。删除前先确认当前没有进程在大量使用旧 swap用swapon --show可以看每个 swap 的使用情况。实际操作# 先关闭旧 swap假设旧的是 /dev/mapper/xxx-swap sudo swapoff /dev/mapper/xxx-swap # 如果关闭时报 Cannot allocate memory说明内存紧张先把新 swap 优先级调高再试 # 编辑 /etc/fstab给新 swapfile 加上 pri100 # 确认关闭后删除旧 swap 相关配置 # 编辑 /etc/fstab删除或注释旧 swap 那行 sudo vim /etc/fstab # 最后更新 swap 配置并验证 sudo swapon -a sudo swapon --show这里要注意/etc/fstab里新旧两行 swap 的配置不能冲突。新 swapfile 那行应该写成/data/swapfile none swap pri100 0 0pri100的作用是告诉内核优先使用这个 swap。如果有多个 swap 设备内核优先用优先级高的这样可以确保新扩容的 swap 被充分使用而不是还扎堆挤在旧 swap 上。swapon -a是读取/etc/fstab并启用所有标记为 swap 的设备加完配置后用这个命令验证是对的。有个排查经验分享如果你swapoff的时候报内存不足说明物理内存确实很紧张这时候不要硬删。可以先把新的 swapfile 用swapon启用并设置更高优先级让内核优先把不活跃页换到新 swap 上过几分钟等旧 swap 的用量降下来了再执行swapoff就顺畅多了。2.4 调优补充swappiness 参数怎么设置swap 扩容之后还有一个容易被忽视的参数是vm.swappiness。这个参数控制内核“多倾向于”使用 swap取值范围 0 到 100默认通常是 60。数值越大内核越积极地把内存页换到 swap数值越小越倾向于保留在物理内存。对于开发机来说我一般建议设成 10 左右。原因很简单开发机上的 IDE、浏览器、编译进程都希望数据留在物理内存里以保流畅如果 swappiness 太高刚切到后台的 IDE 窗口再切回来就会感觉卡一下因为页面被换到 swap 了。但如果你跑的是内存密集型任务比如同时跑多个大模型可以适当调高到 30 左右避免物理内存被耗尽直接 OOM。# 临时生效 sudo sysctl vm.swappiness10 # 永久生效写入 /etc/sysctl.conf echo vm.swappiness10 | sudo tee -a /etc/sysctl.conf # 验证 sysctl vm.swappiness3. Hugging Face 更换默认目录缓存和模型到底怎么搬3.1 为什么 Hugging Face 默认目录是磁盘杀手Hugging Face 在transformers、datasets等库加载模型时默认会把模型文件缓存到~/.cache/huggingface下。这个目录的问题在于它有两个特性能让你的磁盘迅速告急第一默认在用户 home 目录下而 home 目录通常就在根分区上第二它会保留所有版本的模型文件即使你只是调用from_pretrained加载了某个模型下载的权重文件、tokenizer、配置文件全部躺在缓存里。如果代码里多次加载不同版本或者/root/.cache和普通用户的缓存目录不统一占用的空间更是翻倍。以我这次的情况为例光~/.cache/huggingface/hub/models--xxx就占了 280GB——因为我一边研究不同模型的推理效果一边跑批量任务每个模型动辄几十 GB再加上 datasets 缓存加起来轻松超过 300GB。更麻烦的是这个目录如果直接在根分区上你就算删了它下次加载模型又会自动重新下载治标不治本。所以正解是把整个 Hugging Face 缓存目录迁移到数据盘上然后通过环境变量让它“指哪打哪”。3.2 通过环境变量更换默认目录HF_HOME 与 HF_HUB_CACHEHugging Face 生态涉及的目录变量有好几个但核心就两个HF_HOME总目录默认~/.cache/huggingface和HF_HUB_CACHE具体缓存子目录默认$HF_HOME/hub。设置HF_HOME是最省事的它会自动影响下面的 datasets、hub、metrics 等子目录。HF_HUB_CACHE则是更精细的控制如果你只想挪模型缓存而保留其他配置在同目录就用它。我的做法是在/data下创建专门的目录然后在~/.bashrc或~/.zshrc里写入环境变量# 创建新目录 mkdir -p /data/huggingface mkdir -p /data/huggingface/cache # 写入环境变量 cat ~/.bashrc EOF export HF_HOME/data/huggingface export HF_HUB_CACHE/data/huggingface/cache export TRANSFORMERS_CACHE/data/huggingface/cache EOF # 让配置生效 source ~/.bashrc # 验证环境变量 echo $HF_HOME echo $HF_HUB_CACHETRANSFORMERS_CACHE是 transformers 库自己的环境变量虽然新版 transformers 会默认跟随HF_HOME但写上它主要是兼容老代码、paddlenlp等库以及公司内部封装的一些工具脚本。这三个一起设基本覆盖 99% 的情况。但如果你的模型没有自动走这些变量比如某些库硬编码了路径最简单粗暴有效的方案是软链接。把旧目录移动到数据盘然后在原位置做一个符号链接这样所有按默认路径查找的程序都能正常工作# 方法移动旧的默认目录到新位置 mv ~/.cache/huggingface /data/huggingface_old ln -s /data/huggingface_old ~/.cache/huggingface # 更推荐的方式是让缓存最终落地到 /data 下的独立目录 mv ~/.cache/huggingface/hub /data/hf_hub ln -s /data/hf_hub ~/.cache/huggingface/hub这个方法尤其适合那种“我不确定这个库认不认环境变量”的情况。软链接对应用程序完全透明但要注意权限——ln -s出来的软链接如果跨用户使用权限可能会出问题建议用chown -R统一归属。3.3 已下载模型的迁移与校验别删先搬家如果你已经下载了大量模型那么设完环境变量之后还有一步关键操作把旧缓存目录里的大头搬到新位置而不是直接删掉重新下载。几百 GB 的模型重新下载不仅浪费时间还可能因为网络波动中断。迁移用rsync最稳# 推荐用 rsync支持断点续传、保留权限和时间戳 rsync -av --progress ~/.cache/huggingface/ /data/huggingface/ # 搬完之后确认没有遗漏 du -sh /data/huggingface du -sh ~/.cache/huggingface # 确认无误后旧的根分区缓存目录就可以随缘删除了 # 但建议先保留等运行一天确认新路径正常再删rsync的-a参数是归档模式保留权限、属主、时间戳-v是显示详情--progress显示进度条。为什么不用mv因为跨文件系统移动大目录时mv本质上也是复制然后删除但中途如果断了就停在中间状态没有对比校验。rsync可以重跑重复执行到没有差异为止安全得多。搬迁之后有一件事必须做用一个小模型加载测试确认走的环境变量正确。我习惯顺手跑一段 Pythonfrom transformers import AutoTokenizer, AutoModel # 测试用一个小模型如 bert-base-uncased model_name bert-base-uncased tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModel.from_pretrained(model_name) print(模型加载成功缓存路径:, model_name)正常加载成功后你会发现/data/huggingface/hub/models--bert-base-uncased出现了而旧目录没有新增文件。有些用户会遇到“环境变量设置了但模型还是下到旧路径”大概率是Python 进程启动时没有继承新的 shell 环境变量——比如通过 systemd 服务、cron 任务或者 IDE 内部终端启动的进程它们可能读取的是/etc/environment而非~/.bashrc。这种情况下要么在启动命令前显式export要么把变量写进~/.profile。3.4 关于下载加速的补充说明写这一节一定会提到的还有 Hugging Face 的下载速度。国内网络环境下直接访问官方地址经常慢到让人怀疑人生几十 GB 的模型下到 30% 就断掉。解决思路有两个第一是使用国内镜像站将HF_ENDPOINT环境变量指到镜像地址比如export HF_ENDPOINThttps://hf-mirror.com第二是安装hf_transfer这个 Rust 编写的下载加速器配合HF_HUB_ENABLE_HF_TRANSFER1使用速度提升明显。# 使用国内镜像加速注意这只是把下载源切到国内节点 export HF_ENDPOINThttps://hf-mirror.com # 使用 hf_transfer 加速可选 pip install hf_transfer export HF_HUB_ENABLE_HF_TRANSFER1需要提醒的是hf_transfer虽然快但不支持断点续传时展示完整进度的情况比较少还有个别环境的下载连接数过多会被服务端限速。如果发现开了HF_HUB_ENABLE_HF_TRANSFER1反而下载失败就把它关掉用默认的huggingface_hub下载逻辑也没问题。4. ffmpeg视频处理信息查询、逐帧导出、多视频合并与常见误区4.1 ffmpeg 是什么为什么处理视频它是第一选择ffmpeg 是本地视频处理的瑞士军刀。它不是一个在线工具不依赖图形界面装好之后一条命令就能完成视频转码、剪切、抽帧、合并等操作。对 AI 训练、视频数据预处理、内容剪辑来说ffmpeg 几乎是避不开的依赖。它最核心的定位是“一切皆流”输入文件被分解成音频流、视频流经过各种 filter滤镜处理后重新封装成输出文件。听起来复杂但你日常用的无非就是几个固定套路查信息、抽帧、转格式、裁切、拼接。下面用我这个场景里的真实操作为例把每一条命令的语法点和适用场景拆清楚。4.2 视频信息查询与逐帧导出跑 AI 训练前的常规操作我在清空间之前正好有一批 mp4 视频需要处理成图片数据集。第一步永远是侦察输入文件信息# 查看视频编码、分辨率、帧率、时长简单版本 ffprobe -v error -show_streams -show_format input.mp4 # 查看关键摘要信息更常用 ffprobe -v error -select_streams v:0 -show_entries streamwidth,height,r_frame_rate,codec_name,pix_fmt -of defaultnoprint_wrappers1 input.mp4参数解释-v error只显示错误不显示冗余日志-select_streams v:0选中第一条视频流-show_entries指定显示的字段-of defaultnoprint_wrappers1以纯文本形式输出方便 grep 和脚本解析。命令输出的r_frame_rate是一个分数比如30000/1001换算下来就是 29.97fps30 帧非整数帧率的常见表示。逐帧导出到图片的命令# 逐帧导出全部帧 ffmpeg -i input.mp4 -vsync 0 frames/frame_%06d.png # 每隔 N 帧导出一帧 ffmpeg -i input.mp4 -vf selectnot(mod(n,30)) -vsync 0 frames/frame_%06d.png # 每秒导出一帧按时间间隔 ffmpeg -i input.mp4 -vf fps1 frames/frame_%04d.png逐个拆解%06d表示六位数字序号占位符比如frame_000001.png靠它控制文件名排列顺序fps1是输出帧率设为每秒 1 帧等价于每秒采样一帧select滤镜的not(mod(n,30))含义是“帧序号 n 除以 30 余数为 0 的时候输出”也就是每 30 帧取 1 帧。-vsync 0新版 ffmpeg 建议用-fps_mode vfr告诉 ffmpeg 用可变帧率输出这样抽帧时不会额外复制或丢弃帧来凑固定帧率处理大量图片时速度更快、文件更可控。有个坑要避开导出 PNG 会占用大量磁盘空间。一帧 1080p PNG 大约在 2MB 到 4MB如果视频是 30 分钟 30fps逐帧导出 54000 张就是 100GB 以上。如果你只是要训练数据集几乎肯定不需要全部帧——要么用select滤镜跳帧要么导出 JPG 并把质量适当压缩。我一般首选导出为 JPGffmpeg -i input.mp4 -q:v 2 frames/frame_%06d.jpg-q:v 2表示高质量 JPEG范围是 2 到 31数字越小质量越高文件越大。一张 1080p JPG 大约 200KB比 PNG 小一个数量级。4.3 多视频合并与按时间段裁切处理素材的常规操作多视频合并且每个片段编码参数不一致的情况是 ffmpeg 新手最容易翻车的地方。直接concat滤镜或者concat demuxer有一个前提所有输入的分辨率、帧率、编码格式、音频声道数一致否则输出要么报错要么播放时出现音画不同步、画面花屏。我的处理思路先统一转码为相同的参数再进行无二次编码拼接。具体方案分两步# 第一步把所有输入转成统一格式H.264 AAC 指定分辨率/帧率 for f in part1.mp4 part2.mp4 part3.mp4; do ffmpeg -i $f -c:v libx264 -preset fast -crf 20 -r 30 \ -vf scale1920:1080 -c:a aac -ar 44100 -ac 2 \ temp/${f%.mp4}_normalized.mp4 done # 第二步用 concat demuxer 拼接 cat EOF list.txt file temp/part1_normalized.mp4 file temp/part2_normalized.mp4 file temp/part3_normalized.mp4 EOF ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4第一步里的-c:v libx264指视频编码器用 H.264-preset fast是编码速度和压缩率的折中-crf 20是画质控制参数数值越小画质越高建议 18 到 23 之间-r 30强制输出帧率 30fps-vf scale1920:1080统一分辨率音频部分用 AAC、采样率 44100Hz、双声道。这样处理完第二步的-c copy就是直接复制数据流而不重新编码速度快得不像是视频处理。需要注意-safe 0这个参数concat demuxer 默认拒绝读取相对路径或特殊路径加-safe 0允许 list.txt 里出现相对路径。如果你直接写绝对路径并且文件名不含特殊字符不加也不影响。拼接完用 ffprobe 检查一下输出时长是否等于三段之和即可。4.4 “删除一帧”以及其他常见热词背后的原理热词里有“ffmpeg 删除一帧”。这个操作直观理解就是把视频里的某一帧去掉。但视频帧不是图片文件直接“删一行”会导致视频时长缩短且解码链条异常。真正要删除特定帧需要用 select 滤镜做帧筛选# 删除第 N 帧这里以删除第 100 帧为例 ffmpeg -i input.mp4 -vf selectnot(eq(n,100)) -vsync 0 output.mp4 # 删除某一时间段比如 10 秒到 15 秒的片段 ffmpeg -i input.mp4 -vf selectnot(between(t,10,15)) -vsync 0 output.mp4原理是select滤镜根据表达式决定每帧是否输出eq(n,100)为真表示“帧序号等于 100”取反not()就是不输出这一帧。这里注意这种操作必然要求重新编码因为帧被删掉后时间戳和参考帧都变了不能-c copy。热词里还有关于“ffmpeg 硬解”的问题比如d3d11va和dxva2的区别。这其实是 Windows 下硬件解码加速方案的选择。简单说dxva2是老一代 DirectX Video Acceleration APId3d11va是 Windows 8 之后基于 Direct3D 11 的硬件视频加速接口兼容性更好、支持的格式更多新。如果你只是在 Linux 开发机上用 ffmpeg这两个基本无关。如果你的 ffmpeg 加载不出来硬件解码器优先检查编译时是否带了对应解码器比如 Linux 下常用h264_cuvid、hevc_cuvidNVIDIA 显卡或vaapiIntel/AMD 显卡。4.5 ffmpeg 安装选型GPL 与 LGPL 的取舍热词里有人问“ffmpeg gpl和lgpl有什么区别”这其实是下载和编译 ffmpeg 时必须知道的事。GPL 和 LGPL 是开源许可证。ffmpeg 的核心库采用 LGPL但如果你要使用libx264H.264 编码器、libx265等模块因为 x264 是 GPL 协议的整个 ffmpeg 就会被传染为 GPL。简单记法只要编译或下载时带了 x264/x265 等 GPL 库你的 ffmpeg 就是 GPL 版本商用闭源软件集成时必须开源或购买商业授权。只想要 LGPL 版本编译时禁用--enable-gpl和--enable-libx264等项即可。开发机上自用无所谓的直接下载完整版通常叫ffmpeg-gpl-full。如果是商业项目要分发那就得谨慎选 LGPL 版本否则法律风险不小。顺带一提热词里“ffmpeg 推流到 srs 存在延迟”的问题本质不是 ffmpeg 的锅而是推流参数没调好。用 ffmpeg 推流到 SRS 或 Nginx-RTMP 时延迟大的主要原因是 GOP关键帧间隔太长和编码缓冲。常用优化参数# 推流时降低延迟的关键参数 ffmpeg -re -i input.mp4 -c:v libx264 -preset ultrafast -tune zerolatency \ -g 30 -b:v 2000k -c:a aac -f flv rtmp://your-srs-server/live/stream-tune zerolatency专门为低延迟场景优化编码器-g 30表示每 30 帧一个关键帧推流端延迟能控制在 1 秒以内。推流延迟问题虽然不在本次场景内但既然热词反复出现列出来供需要的人参考。5. 清理空间先找到真凶再动手删除5.1 定位空间占用不要凭感觉用工具说话清空间最大的忌讳就是凭直觉删东西。你以为/tmp很大实际上一查du发现才几百 MB你以为模型库占了 500GB实际上可能是 Docker 的 overlay 目录在疯狂吞噬空间。所以第一步永远是用工具量化。我推荐先用df -h看整体再用ncdu做交互式深度扫描。ncdu是一个基于 ncurses 的磁盘分析工具它比du -sh *舒服在可视化可以上下键导航回车进入目录d键直接删除会二次确认。安装方法很快sudo apt install ncdu # Debian/Ubuntu sudo yum install ncdu # CentOS/RHEL # 或者直接命令行扫描 ncdu /扫描时注意ncdu /会把整个根目录过一遍数据盘、其他挂载点默认不进入必须指定路径或加-x限定文件系统避免跨挂载点。我这次的排查路径是ncdu /data # 先看数据盘因为数据盘的模型库是已知大头 ncdu ~/.cache # 再看用户缓存目录 ncdu /tmp # 然后看临时目录快速定位还有个管道技巧du -h --max-depth2 / | sort -rh | head -20可以列出根目录下占用最大的前 20 个目录但速度比 ncdu 慢很多磁盘 IO 压力也大。生产环境慎用。5.2 常见的空间回收对象日志、缓存、旧内核与无主数据找到大目录后哪些可以安全清理按优先级别举几个常见的第一优先级journald 日志。/var/log/journal一般默认限制为系统盘容量的 10%但长时间运行累计下来非常可观。查看并清理# 查看 journal 日志占用 journalctl --disk-usage # 只保留最近 3 天永久生效 sudo journalctl --vacuum-time3d sudo vim /etc/systemd/journald.conf # 设置 SystemMaxUse500M第二优先级pip 和 npm 缓存。~/.cache/pip和~/.npm经常能清出好几 GB尤其是频繁安装依赖的场景pip cache purge npm cache clean --force第三优先级旧内核和孤儿依赖包。Ubuntu 下清理旧内核sudo apt autoremove --purge # 列出已安装内核 dpkg -l | grep linux-image # 卸载旧内核保留当前使用的 sudo apt purge linux-image-5.x.x-generic第四优先级不再需要的模型权重和中间产物。这是本次清理的大头Hugging Face 旧缓存目录确认新路径正常工作后可以整体删除rm -rf ~/.cache/huggingface第五优先级ffmpeg 中间文件。比如抽帧导出的图片、转码产生的临时文件用完之后立即删不要留在磁盘上“以后可能用”。如果你已经生成了几万张 PNG导出后要快速筛选用不到的批次用find定位批量删除# 删除某个目录下所有 png 文件确认无误后执行 find /data/frames -name *.png -type f -mtime 7 -delete还有一点很容易漏Docker 镜像和容器层。跑深度学习的机器几乎都装了 Docker它的 overlay2 目录会默默吃掉大量空间。清理操作是docker system df # 查看占用情况 docker system prune -a --volumes这个命令会删除所有未被使用容器和镜像关联的数据清理效果有时候比删模型文件还猛。执行前要确认没有正在运行的容器依赖这些镜像。5.3 清理之后的验证与习惯养成清理完了不要直接以为万事大吉。我的习惯是跑一遍全流程验证df -h free -h swapon --show echo $HF_HOME然后重新加载一次常用的模型确保它能从新缓存目录读取。如果加载时间明显比第一次快说明缓存迁移是成功的。最后确认 swap 的启用状态和占用确保swapon --show里只剩/data/swapfile一个设备。从这个场景里延伸出一个很实用的习惯在项目目录里建一个cleanup.sh脚本把常用的清理动作固化下来。这样不用每次空间告警了再回忆从哪儿查起。我的脚本大致长这样#!/bin/bash # 清理基本缓存与临时文件 echo 清理 pip 缓存 pip cache purge || true echo 清理 journal 日志 sudo journalctl --vacuum-time3d || true echo 清理临时目录 find /tmp -type f -atime 7 -delete 2/dev/null || true echo 检查磁盘空间 df -h脚本核心是“安全第一”-mtime 7的删除条件留了很大的安全余地避免误删新文件|| true防止某一步命令失败导致脚本中断。定期跑一次配合cron每周自动执行基本不会再被磁盘告警打乱节奏。6. 高频问题与排查实录这次操作中踩过的坑6.1 问题速查表问题现象原因解决方案swapon提示 swapfile has holes新建 swapfile 无法启用fallocate创建了稀疏文件改用dd重新创建swapoff提示 Cannot allocate memory关闭旧 swap 失败物理内存紧张新 swap 未启用或优先级低先启用新 swap 并设置高优先级稍等再 swapoff环境变量设置了模型仍下载到旧目录通过 IDE 或 systemd 启动的进程不认~/.bashrc进程未继承 shell 环境变量写入~/.profile或/etc/environment或启动命令前显式 exportHugging Face 下载慢/老断模型下载到 20% 就失败网络到官方源不稳定设置HF_ENDPOINT指向国内镜像或启用hf_transferffmpeg concat 报错多视频拼接失败或花屏输入文件分辨率/编码/帧率不一致先统一转码参数再使用-c copy拼接逐帧导出后磁盘爆满导出图片撑爆空间PNG 单帧几 MB帧数多导出 JPG 或按间隔抽帧ncdu /扫描很快但没看到数据盘数据盘是单独挂载点ncdu 默认不跨文件系统指定路径扫描如ncdu /data6.2 独家避坑技巧第一个技巧删除之前先做“反悔预案”。清理空间最容易犯的错误是删完之后才发现某个文件是某个正在跑的脚本的输入。我的习惯是凡是超过 10GB 的目录要么先移动到/data/trash/而不是直接rm要么用du先列出内部明细并发个“将删除清单”到终端预览一遍。确认无误之后才真正删。删除大目录本身也有技巧rm -rf对超大目录树有时会卡住可以先用ionice降低删除进程的 IO 优先级避免删除过程占满磁盘导致其他服务卡顿ionice -c3 rm -rf /data/old_hf_cache第二个技巧swap 与软链接配合使用。我这次空间清理完之后特意把 swapfile 放在数据盘上而数据盘上还有 Hugging Face 的模型缓存。如果后续数据盘也满了swap 就会成为新的瓶颈。所以我写了一个简单的监控脚本每周检查一次free -h和df -h如果可用内存低于 2GB 或磁盘占用超过 85%就自动发告警邮件。空间管理不是一个一次性动作建立习惯才能真正避免下一次告警。第三个技巧用lsof定位被占用的大文件。有时候磁盘显示满了但是du统计却对不上这多半是有进程打开了某个已被删除的文件文件句柄还在但目录项已经没了。用lsof L1可以列出这种“已删除但仍在占用”的文件定位到对应进程后重启或杀掉空间才会真正释放。6.3 本次操作的总体时间线回顾最后把完整流程串一遍方便你对照自己机器的情况做规划第 1 步约 15 分钟df -h整体看磁盘free -h看内存ncdu定位占用大户。第 2 步约 30 分钟在/data上创建 32GB swapfile启用新 swap删除旧 swap配置/etc/fstab开机自动挂载。第 3 步约 20 分钟设置HF_HOME和HF_HUB_CACHE环境变量用 rsync 把旧缓存迁移到数据盘测试模型加载。第 4 步约 40 分钟用 ffmpeg 把视频抽帧、裁切、合并出数据集处理完立即清理中间产物。第 5 步约 20 分钟清理 journald 日志、pip/npm 缓存、旧内核、Docker 残留数据最后写一个cleanup.sh脚本备后续使用。整个过程在磁盘告警之后开始到确认空间恢复、swap 生效、模型加载正常结束前后加起来不到两个小时。其中真正“干活”的时间大约只有一半另一半都是在排查和验证。空间管理就是这样——功夫花在“为什么动”和“动完对不对”上面比你盲目删一堆文件要高效得多。我个人在实际操作中最深刻的体会是系统的空间问题很少是单点原因它往往是 swap 配置不合理、缓存目录位置不佳、工具链产生中间文件这几个因素叠加的结果。所以处理时要站在整体角度做规划而不是“哪满了删哪”。先扩容给系统留出喘息空间再迁移重资源目录到宽裕分区最后清理掉确无价值的缓存和中间产物一步一步来每一步都有明确的验证手段基本不会出大问题。