Docker 空间清理:先分清镜像、构建缓存和业务数据

Docker 空间清理:先分清镜像、构建缓存和业务数据原创封面

从磁盘告警开始建立对象清单

开发机磁盘变满时,Docker 的占用列表很容易让人产生一个直觉:既然界面写着可以回收,那就把这一栏全部清掉。但可以回收描述的是对象之间的引用关系,并不说明这些对象对业务是否重要。一个暂时没有容器挂载的数据卷,也可能保存着唯一一份数据库。清理之前,需要先回答三个具体问题:当前命令连接哪台引擎,哪些对象可以重新生成,哪些数据必须在容器消失后继续存在。本文以单机开发环境为例说明判断顺序,不提供可以直接套到生产数据库上的批量删除清单。

先运行 docker context show 和 docker context ls,确认当前上下文。电脑上同时安装 Docker Desktop、远程构建器和服务器上下文时,命令提示符所在目录并不能决定清理目标。接着记录 docker system df -v 的输出、容器列表和构建器列表,保存检查时间。这里的记录不是为了追求漂亮的空间数字,而是让每一次删除都能对应到清理前的对象。若引擎无法连接,应先解决连接问题;不能把命令失败或返回空列表解释成没有业务数据,更不能转而手工删除引擎的数据目录。

镜像是启动材料,容器还有自己的写入层

镜像通常由仓库下载,或者根据项目中的构建文件生成。对仍能获取基础镜像、源码和依赖的项目,它具有较强的可重建性;但离线机器、已经删除的私有仓库版本和临时手工构建的镜像并不一定满足这个前提。检查镜像时,应把标签、镜像标识、关联容器和恢复来源一起记录。不要仅根据镜像名称陌生就删除,也不要认为同一个标签永远对应同一份内容。清理旧版本前,保留当前运行版本以及明确需要的回滚版本,并确认它们能从可信来源重新取得。

容器停下以后,其可写层仍可能存在。数据库若没有正确挂载持久化目录,数据甚至可能写在这一层里;开发者在容器内手动编辑的文件也可能只有这一份。因此,应先查看 docker inspect 中的 Mounts,再结合应用配置判断写入位置。停止状态只说明进程没有运行,并不说明可以删除。遇到用途不清楚的容器,可以先整理其创建来源和挂载信息,找到对应的项目负责人或配置文件。把停止容器整体清空,会使后续的卷关联分析失去线索,清理顺序应考虑这种信息损失。

构建缓存需要核对所属构建器

构建缓存保存的是构建过程中可以复用的结果,包括中间层、来源上下文以及缓存挂载中的内容。它通常可以重新生成,但重新生成会消耗下载流量和构建时间,也可能重新访问已经不可用的依赖服务。检查时使用 docker buildx ls 列出构建器,再通过 docker buildx du 查看选定构建器的记录。不同构建器可能使用不同驱动,缓存也可能保存到不同的位置。只查看默认引擎的汇总,并不能证明另一套构建器已经没有缓存;反过来,远程构建器占用很大,也未必影响本机系统盘。

缓存清理可以从明确的构建器和保留时间开始,例如对开发用途的构建器执行带有 until 条件的 prune。这里应保留交互确认,先核对命令帮助和版本,再决定是否执行。清理完成后重新检查同一构建器的用量,并运行一个可以接受重新构建成本的项目。构建变慢是可能的结果,不能提前承诺空间回收以后速度也会提升。如果环境依赖本地缓存才能完成离线构建,则应先准备依赖镜像和制品;可重建性必须通过实际依赖条件判断,不能仅因为对象叫缓存就推定成立。

数据卷的价值不能由引用数决定

数据卷独立于容器生命周期。先用 docker volume ls 获取名称,再用 docker volume inspect 检查标签、驱动和挂载位置,并通过容器的挂载信息找出使用者。Compose 创建的卷通常带有项目标签,可以帮助追溯来源,但项目更名、容器重建或配置变化会让旧卷暂时无人引用。数据库卷、用户上传卷和只能人工重新收集的媒体卷应优先列入保留清单。卷名看起来像随机字符串也不等于内容无价值,匿名卷同样可能承载需要恢复的状态。

对于准备删除的卷,要记录它原先属于哪个服务、保存什么数据、是否有备份以及备份是否能恢复。数据库的备份方法应符合数据库本身的一致性要求;直接复制运行中的数据目录可能得到不完整的快照。可以先在隔离环境恢复备份,检查必要的数据和应用读写,再确认旧卷是否仍承担恢复职责。这个过程不要求每一个开发卷都建立复杂的审批流程,但必须让删除判断有依据。没有用途信息、没有恢复来源的卷,应暂缓处理,继续追查挂载和项目配置。

分项操作比一条总清理命令更可核对

Docker 提供镜像、容器、卷和构建缓存的分别清理入口。执行前,应理解所用版本中各项参数的范围,尤其是镜像清理的 all 参数、卷清理是否包含命名卷,以及 system prune 是否同时处理停止容器。本文选择先盘点、再按对象处理的步骤,是为了让命令的影响与记录中的目标对应。若只是构建缓存过大,就从构建器开始;若问题来自无用镜像,就核对镜像关联。不要为了减少输入次数,将尚未确认的数据卷也加入同一次清理。

业务数据应采用明确对象名称的操作方式,而不是根据一个宽泛标签或陌生名称模式批量删除。筛选条件也需要校验:创建时间不等于最近使用时间,项目标签可能来自旧配置,未使用状态还会随容器删除而变化。操作之间重新读取列表,可以发现这些变化。保留清理命令的退出码和汇总即可,避免把环境变量、数据库口令等敏感内容一起写入报告。若命令中途失败,应核对实际留下的对象,然后决定是否重试;不能假定所有步骤均已执行。

空间验证要区分引擎和宿主机

清理后应使用相同上下文再次运行用量检查,并比较对象数量、共享空间和可回收空间。镜像层可能被多个镜像或缓存记录共享,因此删除一个镜像以后,物理空间不一定减少同样大小。Docker Desktop 还存在外层虚拟磁盘:内部文件系统释放的空间,可能暂时仍被虚拟磁盘文件占据。此时先确认引擎内部已经释放,再讨论虚拟磁盘整理;反复清理卷不会解决外层文件收缩问题,反而可能误删原本准备保留的数据。

除了空间,还要检查恢复能力。启动保留的数据库服务,确认应用能连接;核对上传文件是否仍可读取;检查计划保留的镜像能否启动。对没有实际执行过的恢复步骤,只能标记为待验证,不能写成已经恢复成功。清理前后的报告应包含时间、上下文、对象类型和判断理由,不必编造回收了多少容量的结果。这样,即使磁盘数字没有明显变化,也能知道下一步应调查日志、宿主目录、其他构建器还是虚拟磁盘,而不是继续扩大删除范围。

让后续清理成为可维护的日常动作

项目应在配置中明确持久化目录,给重要卷添加容易追溯的标签,并在文档中说明备份位置。构建器可以设置缓存预算,镜像保留策略应考虑回滚周期,日志也应设置轮转。每种对象有不同的增长原因,分别管理能减少临时磁盘告警时的猜测。对于多项目开发机,可以定期整理已经结束的实验项目,但要先把实验中形成的有效数据移出临时环境。项目停止开发,并不自动意味着所有状态都可以抛弃。

上述方法适用于能够识别对象归属的单机 Docker 环境。集群存储、远程卷驱动和托管构建服务还需要遵循各自的平台规则;它们的挂载位置与清理效果不能由本机命令直接推断。真正可以执行的删除清单,应来自这台引擎当前的检查结果。把镜像、缓存、容器写入层和持久化卷分开以后,清理就从一次不可解释的批量操作,变成了能够说明目标、成本和恢复条件的维护过程。先完成这种分类,再追求释放空间,通常更容易定位真正的占用来源。

别忽略引擎之外的宿主目录

绑定挂载的文件实际保存在宿主机指定目录中,Docker 的卷列表不能完整代表它们。对每个挂载,应记录源路径、容器内目标和读写方式,再由应用配置确认用途。开发项目的上传目录、导出的备份和本地测试素材经常位于这一类位置。容器删除以后,这些目录可能继续存在,但是否应该删除,需要按文件本身的来源判断。统计它们时,应检查路径中是否存在符号链接,避免把另一个项目的文件误计为当前目录中的可清理内容。

还应核对应用是否向标准输出记录大量日志,以及引擎采用什么日志驱动。日志占用与镜像和卷的统计口径不同,发现增长以后应配置对应的轮转规则,保留必要的诊断窗口。不要直接在运行中截断不熟悉的日志文件,也不要手动搬动引擎内部文件来绕过对象管理。先保留能够说明问题的时间段,再调整后续增长方式。只有把宿主目录和日志纳入清单,空间盘点才不会因为遗漏这些来源而反复清理同一批镜像。

实现片段

# 只读盘点:先确认上下文,再检查各类对象
docker context show
docker context ls
docker system df -v
docker ps -a --size
docker buildx ls
docker buildx du --builder default
docker inspect demo-db --format '{{json .Mounts}}'
docker volume inspect demo_database
# 以下是有删除影响的单项操作,确认目标和版本后再执行
# docker buildx prune --builder default --filter 'until=168h'
# docker image prune

一手参考资料

评论 · 0

还没有评论留下第一句经过思考的话。

游客评论需审核。注册后可直接公开,无需审核。

SHARE / 分享

分享这篇文章

WECHAT / 微信

用微信扫一扫

在手机微信中打开文章后,再从微信右上角分享给朋友或朋友圈。