记录一下那天晚上的排查过程。一个基于 ubuntu 22.04 的 Python 服务镜像做出来 1.2G,docker history 一看,装编译依赖那一层单独占了 800M,删了 apt 缓存体积也没变。
刚开始盯着「多阶段构建没用,编译工具链全留在最终镜像里」,折腾了半个多小时,排掉了。
排查方向其实有限,先把可能性排一排。同一条 RUN 里装的包和删缓存被拆成两层,删除只是加了一个白洞,空间照样计进去。少数情况下是把源码和测试数据一起 COPY 进镜像,后面的层删掉文件上层体积仍在。
不容易想到的是,基础镜像选的是带完整工具链的版本,本身就有一 G 多。
上来先逐层看哪一层最占体积
docker history --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' web:1.4 | head -10
随后改用多阶段构建只留运行产物
docker build --target runtime -t web:1.5 .
同层安装与清理写进一条 RUN
docker build -t web:1.5 --build-arg PIP_NO_CACHE_DIR=1 .
剩下的就是对比新旧镜像的层大小
docker image inspect web:1.5 -f '{{.Size}}'
怎么算真的好了,看的是同一批现象。docker history 中没有超过 200M 的单层,镜像总大小降到 400M 以内,容器启动和拉取时间同步缩短。
容易忽略的是,分层机制决定了删文件不减体积,必须把安装和清理写在同一条 RUN;瘦身优先做多阶段,压缩工具反而会拖慢启动。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
