核心是合并run指令、按变更频率倒序组织指令、采用多阶段构建、选用轻量基础镜像并配合.dockerignore。例如将apt update与install及清理缓存合并为单层,依赖指令前置、源码copy后置,go项目用golang:1.21构建后复制二进制至alpine运行镜像,最终体积可从数百mb降至几十mb。

优化 Docker 镜像分层结构,核心是减少冗余层、提升缓存命中率、精简最终镜像体积。这直接决定构建快慢、推送耗时和磁盘占用。
合并 RUN 指令,减少层数
Dockerfile 中每条 RUN、COPY、ADD 都会生成一层。层数越多,构建越慢,镜像越臃肿。关键不是“少写指令”,而是“把逻辑相关的操作合并到同一层”。
- 把 apt update + install 合并在一行,避免中间层残留 apt 缓存目录
- 安装完立即清理:比如
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* - 避免连续多个
RUN echo ... && echo ...,用管道或 heredoc 整合
按变更频率倒序组织指令
底层应放最稳定的内容(基础系统、运行时),上层放最易变的内容(源码、配置)。Docker 构建时从上往下逐层比对缓存,一旦某层失效,其后所有层都需重建。
- 把
FROM、RUN安装依赖放在前面 - 把
COPY . .或COPY package.json .放在靠后位置 - 如果只改了代码,只有最后一两层会重建;如果改了依赖文件,就从依赖安装层开始重建
用多阶段构建剥离构建环境
编译型语言(Go、Rust、Java)或带构建步骤的项目(Node.js 的 build 输出、Python 的 wheel 打包),不应把编译器、测试工具、临时文件打包进最终镜像。
- 第一阶段用完整 SDK 镜像(如
golang:1.22)编译程序 - 第二阶段用极简运行时镜像(如
alpine:latest或debian:slim)只复制二进制文件 - 最终镜像不包含
gcc、npm、python-dev等任何构建依赖
选用合适的基础镜像并善用 .dockerignore
基础镜像越小,起始层越轻;无关文件越少,构建上下文越干净,传输和解压越快。
- 优先选
:slim(Debian)、:alpine(musl)、:distroless(仅含运行时)等精简标签 - 避开
:latest,固定版本号(如node:20.15-slim)保证可复现性和缓存稳定性 -
.dockerignore中排除node_modules/、.git/、__pycache__/、*.log等非必需内容
不复杂但容易忽略。真正见效的不是堆技巧,而是让每一层都有明确目的、最小改动范围、最高复用概率。











