适用场景:K8s GPU 集群节点、Docker 容器运行时
问题特征:overlay2 存储层暴增、开发容器吃满磁盘、/var/lib/docker 所在分区 100%
核心手段:du 精准定位 + 软链迁移 + 在线扩容 + journald 限日志 + Registry API 删镜像
📑 目录
- 背景与问题全景
- overlay2 爆盘排查
- 长驻开发容器占盘定位与清理
- 磁盘目录迁移
- 在线扩容
- 日志治理
- 私有 Registry 镜像清理
- 踩坑清单
- 小结
一、背景与问题全景
在 AI/GPU 集群的日常运维中,磁盘问题比 CPU/内存问题更隐蔽、更致命——服务不会立刻崩溃,但会在某个深夜悄悄把/var/lib/docker撑到 100%,然后 kubelet 开始驱逐 Pod、容器无法启动、节点直接变为 NotReady。
本次治理覆盖了生产环境中最典型的五类磁盘问题:
- overlay2 单容器层占 3.1T(
.vscode-server的 pylance 扩展缓存爆炸) - 长驻开发容器单实例吃 130G+
/var/lib/docker和/var/lib/kubelet挂错分区- 云盘需要在线扩容
- systemd journal 日志无限制增长
- 私有 registry 镜像堆积,需要 API 级清理
二、overlay2 爆盘排查:精准定位元凶
2.1 快速定位哪个目录最大
先看整体磁盘使用:
df-h进入 overlay2 目录,逐层排查:
cd/var/lib/docker/overlay2du-h-x--max-depth=1|sort-hr|head-202.2 找到具体容器和文件
本次排查中发现单个容器层占3.1T / 3.3T,罪魁祸首是.vscode-server下的Pylance 扩展缓存:
根据 overlay2 的 long ID 反查容器
dockerps-a--no-trunc|grep<overlay2-id-prefix>或者直接进容器层看
cd/var/lib/docker/overlay2/<id>/diffdu-h-x--max-depth=2|sort-hr|head-10定位到:/root/.vscode-server/extensions/ms-python.vscode-pylance-*/
💡 经验:AI 开发场景中,Pylance 索引大型 Python 项目时会疯狂写缓存,单个扩展目录轻松突破 TB 级。解决方案是给
.vscode-server配置settings.json排除目录,或在镜像构建时预装 Pylance 并限制其缓存路径。。
三、长驻开发容器占盘定位与清理算法同学的长驻开发容器(Jupyter / SSH 开发容器)经常在不知不觉中吃掉上百 GB,因为数据集、conda 环境、pip cache 全都写入容器可写层。。
3.1 找出占盘大户
实时查看容器磁盘占用。用
dockerstats --no-stream--format"table {{.Name}}\t{{.Size}}\t{{.MemUsage}}"按容器查具体目录大小。小
dockerinspect<container-id>|grep-imerged 输出类似:/var/lib/docker/overlay2/<id>/mergeddu-sh/var/lib/docker/overlay2/<id>/merged本次查出的典型案例:本次排查出的典型案例:
| 容器名 | 占盘大小 | 原因 |
|---|---|---|
dev-container-7f3a | 134 GB | 数据集直接下载到容器层 |
notebook-8821 | 131 GB | Conda 环境 + pip cache 未清理 |
3.2 清理策略
方案 1:进入容器内部清理(不停服)
dockerexec<container-id>bash-c'conda clean -a -y && pip cache purge && rm -rf ~/.cache'方案 2:直接删容器(确认数据已持久化到 PVC 后)
dockerrm-f<container-id>方案 3:限制容器可写层大小(docker run 时)
dockerrun --storage-optsize=50G...四、磁盘目录迁移:软链方案与批量执行
当/var/lib/docker所在分区空间不足,而数据盘有空闲时,最快捷的方案是停服务 → 迁移数据 → 建软链。
4.1 单节点迁移
- 停 Docker 和 kubelett
systemctl stopdockersystemctl stop kubelet- 迁移数据
mv/var/lib/docker /home/data/dockermv/var/lib/kubelet /home/data/kubelet- 建软链
ln-s/home/data/docker /var/lib/dockerln-s/home/data/kubelet /var/lib/kubelet- 启动服务
systemctl startdockersystemctl start kubelet4.2 Ansible 批量迁移移
批量移动(适用于多节点统一调整)。)
ansible gpu-nodes-mshell-a'mv /data1/data/docker /data'批量创建软链。链
ansible gpu-nodes-mfile-a'src=/data/docker dest=/var/lib/docker state=link force=yes'⚠️ 迁移前务必确认目标分区有足够空间,
mv跨文件系统实际是 copy+delete,大目录可能耗时很久。
五、在线扩容:growpart + resize2fs
云环境新增云盘容量后,不需要重启,直接在线扩容分区和文件系统。
- 查看磁盘和分区
lsblkdf-hT- 扩展分区(以 /dev/vdb1 为例)
growpart /dev/vdb1- 扩展文件系统
ext4 resize2fs /dev/vdb1 xfs xfs_growfs /data| 步骤 | 命令 | 说明 |
|---|---|---|
| 查看 | lsblk/df -hT | 确认分区和文件系统类型 |
| 扩分区 | growpart /dev/vdb 1 | 将分区 1 扩展到整个磁盘 |
| 扩文件系统 | resize2fs/xfs_growfs | 让文件系统识别新空间 |
六、日志治理:journald + docker log
6.1 systemd journal 日志清理
临时清理:只保留最近 3 天或 500M
journalctl --vacuum-time=3d journalctl --vacuum-size=500M永久限制:修改配置文件
cat>>/etc/systemd/journald.conf<<EOF SystemMaxUse=1G SystemKeepFree=2G MaxRetentionSec=1week EOF重启 journald 生效
systemctl restart systemd-journald6.2 Docker 容器日志限制
在/etc/docker/daemon.json中配置:
{"log-driver":"json-file","log-opts":{"max-size":"100m","max-file":"3"}}systemctl reloaddocker七、私有 Registry 镜像清理:用 API 删远程镜像私有 Registry(master0:5000/images/*)镜像堆积时,不能只删除本地 tag,必须从 Registry 存储中真正删除。。
7.1 开启 Registry 删除功能
确保 Registry 启动时设置了环境变量::
REGISTRY_STORAGE_DELETE_ENABLED=true7.2 通过 API 删除镜像
- 获取镜像的 config digest
TOKEN=$(curl-s-u<user>:<pass>\"https://master0:10000/v2/eflops/<image>/manifests/<tag>"\-H"Accept: application/vnd.docker.distribution.manifest.v2+json"\|python-c'import sys,json;print(json.load(sys.stdin)["config"]["digest"])')- 删除 manifest(这才是真正删镜像)
curl-XDELETE\-u<user>:<pass>\"https://master0:10000/v2/eflops/<image>/manifests/$TOKEN"- 触发 Registry 垃圾回收收
dockerexec<registry-container>registry garbage-collect /etc/docker/registry/config.yml⚠️ 注意:直接 DELETE manifest 是不可逆操作,删除前确认没有人在用这个版本。生产环境建议先用
skopeo list-tags列出所有 tag,再逐个确认。
八、踩坑清单
| 现象 | 排查点 |
|---|---|
| overlay2 突然 100% | 查.vscode-server/pylance/conda/pip cache |
| growpart 报 “unexpected output” | 分区表类型不对(GPT vs MBR),或分区正在被使用 |
| journald 日志删了又满 | 只 vacuum 没改journald.conf,重启后又涨回来 |
| 开发容器删了盘没释放 | overlay2 的deleted文件仍被进程占用,用lsof | grep deleted排查 |
九、小结
磁盘治理的核心经验:
- 定位要快:
du -h -x --max-depth=1是排查 overlay2 爆盘的最快手段。段2.迁移要稳:停服务 → mv → 软链 → 验启动,顺序不能乱。乱3.限制要早:journald 的SystemMaxUse和 Docker 的log-opts部署时就该配好。好4.清理要彻底:Registry 镜像不能只删 tag,要用 API 删 manifest + garbage-collect。t有问题欢迎在评论区贴出报错信息,我看到后会回复。。