docker history 是定位镜像体积病灶的第一步,可逐层查看每条指令(如 RUN、COPY)及其 SIZE 贡献,重点关注 size 异常大或显示 <missing> 的层,并结合 CREATED BY 字段识别未清理的 apt install 等冗余操作。
镜像体积大,不能只看最终大小,得一层一层“解剖”才能找准病灶。关键不是猜,而是用工具定位哪一层塞了不该有的东西、谁在悄悄占空间。
用 docker history 看清每层贡献
这是最基础也最有效的第一步。它能列出所有构建层,显示每条指令和对应大小:
- 运行 docker history your-image:tag,重点关注 SIZE 列明显偏大的行
- 如果某层显示 <missing> 或 size 超过 100MB,大概率是 apt install、go build 或 COPY 整个 node_modules 这类操作没清理或没隔离
- 注意 CREATED BY 列——若看到 RUN apt-get update && apt-get install 却没带 rm -rf /var/lib/apt/lists/*,基本就是元凶之一
用 dive 深度探测“隐形垃圾”
history 只告诉你哪层大,dive 才能告诉你这层里到底塞了什么:
- 执行 docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive:latest your-image:tag
- 进入后按 Tab 切换视图,重点看右侧面板:标红的文件(如 /tmp/*.deb、/usr/src/、/root/.cache)说明被写入又删除,但依然占用空间
- 左侧面板逐层展开,可直接看到某层新增了哪些目录,比如 /app/node_modules 或 /usr/local/go——这些对运行时毫无价值
检查 .dockerignore 是否失效
很多臃肿其实源于源码目录里的“杂物”被悄悄 COPY 进来了:
- 确认项目根目录存在 .dockerignore,且包含常见干扰项:node_modules/, logs/, *.log, .git, README.md, Dockerfile, .dockerignore
- 特别警惕 .gitignore 和 .dockerignore 内容不一致的情况——有人复制 .gitignore 就完事,但 .dockerignore 还需额外排除 build/、dist/、target/ 等构建产物目录
- 一个简单验证法:临时把 COPY . . 改成 COPY package.json . && COPY yarn.lock .,再构建,如果体积骤降,说明原 COPY 带进了大量冗余文件
反查基础镜像与多阶段是否被绕过
有些团队写了多阶段构建,却在最后阶段又 FROM ubuntu:22.04,等于白做:
- 用 docker inspect your-image:tag | jq '.[0].Config.Image' 查看最终镜像继承自哪个父镜像
- 如果结果是 golang:1.21 或 node:18,说明编译环境直接成了运行环境——必须改成 alpine 或 distroless
- 检查 Dockerfile 是否真有多个 FROM;有没有误把 builder 阶段的 COPY --from=0 /app/myapp /usr/local/bin/ 写成 COPY --from=0 / /,把整个构建系统都拖进来了

















