核心目标是最大化构建缓存命中率、最小化镜像层数、控制最终体积、提升复现性与安全性;需将稳定指令(如固定版本FROM、依赖文件COPY与安装)前置,易变指令(如源码COPY)后置,并合并RUN操作、善用多阶段构建和.dockerignore。
合理规划 dockerfile 指令顺序,核心目标是**最大化构建缓存命中率、最小化镜像层数、控制最终体积、提升复现性与安全性**。这不是“把命令排个序”那么简单,而是围绕“每条指令生成一层、层不可变、缓存按顺序逐层比对”这个底层机制来设计的。
把变动少的内容往前放,变动多的往后放
构建缓存从上到下逐层校验,一旦某层哈希不匹配(比如源码变了),它和它之后的所有层都会重新构建。所以要把最稳定、最不容易变的部分放在前面:
-
基础镜像(FROM):固定版本标签,如
python:3.11-slim-bookworm,避免latest导致意外更新 -
依赖安装(RUN pip install / apt-get):先复制
requirements.txt或package.json,再执行安装——因为依赖文件变更频率远低于代码,这样只要依赖没改,后续构建就能直接复用已安装好依赖的层 - 应用代码(COPY . .):放在依赖安装之后——绝大多数开发迭代只改代码,不改依赖,这样能跳过前面所有耗时步骤
合并相关操作到同一层,避免“装了又删”
Docker 层是追加式、不可删除的。如果在不同 RUN 中执行 apt-get update && apt-get install 和 apt-get clean,清理操作不会真正删掉前一层的缓存,只会额外增加一层“标记已删”,最终镜像仍含冗余数据。
正确做法是:
- 用
&&把安装、清理、临时文件删除写在同一 RUN 中,例如:RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/* - Python 场景同理:复制
requirements.txt后,用单条 RUN 完成 pip 安装 + wheel 缓存清理 + venv 创建
善用 WORKDIR 和分阶段构建,减少隐式层和冗余内容
WORKDIR 不仅是设置路径,它还能替代多条 RUN mkdir && cd,避免无意义的层;更重要的是,配合多阶段构建,可彻底剥离构建工具链:
- 第一阶段(builder)用完整镜像(如
golang:1.20或node:20)编译代码或安装依赖 - 第二阶段(runtime)用极简镜像(如
alpine:3.18或distroless),只COPY --from=builder编译产物或虚拟环境 - 这样最终镜像里没有编译器、头文件、包管理器,体积小、攻击面小、启动快
用 .dockerignore 精简上下文,加速传输与解析
docker build 会把当前目录(构建上下文)打包发送给 Docker daemon。如果里面包含 node_modules、.git、logs/、.env 等,不仅拖慢上传,还可能污染缓存或泄露敏感信息。
务必创建 .dockerignore 文件,明确排除:
-
.git、.gitignore -
node_modules、__pycache__、venv -
.env、*.log、docs/、tests/ - 大型二进制文件或本地测试数据


















