Docker基础回顾:Dockerfile基本指令及镜像分层原理
在进入多阶段构建实战之前,必须牢固掌握Dockerfile的核心指令与镜像分层机制。这两者是镜像瘦身的理论基础——不理解分层,就无法理解为什么多阶段构建能有效减小镜像体积。
2.1 Dockerfile核心指令速查
Dockerfile是一个文本文件,包含一条条构建指令。每条指令都会在镜像中创建一个新的层(Layer)。以下是生产中最常用的5条指令:
| 指令 | 作用 | 层数影响 | 典型用法 |
|---|---|---|---|
| FROM | 指定基础镜像,每个Dockerfile必须从FROM开始 | 1层(基础镜像本身可能包含多层) | FROM ubuntu:22.04 |
| RUN | 在镜像中执行命令,常用于安装软件包、编译代码 | 每条RUN增加1层 | RUN apt-get update && apt-get install -y curl |
| COPY | 从构建上下文复制文件/目录到镜像中 | 每条COPY增加1层 | COPY ./app /app |
| CMD | 提供容器启动时的默认命令,可被docker run参数覆盖 |
不增加层(仅元数据) | CMD ["python", "app.py"] |
| ENTRYPOINT | 配置容器启动时的可执行文件,不易被覆盖 | 不增加层(仅元数据) | ENTRYPOINT ["java", "-jar"] |
最佳实践:将多条
RUN命令用&&连接成一条,减少层数。例如:RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*。这比三条独立的RUN指令少两层。
2.2 CMD与ENTRYPOINT的区别
这两条指令都定义容器启动行为,但协作方式不同。理解它们的组合规则,对编写正确的Dockerfile至关重要:
| Dockerfile写法 | docker run附加参数 |
实际执行命令 | 说明 |
|---|---|---|---|
| 只有CMD | 无 | CMD内容 |
默认行为 |
| 只有CMD | bash |
bash |
CMD被覆盖 |
| ENTRYPOINT + CMD | 无 | ENTRYPOINT CMD |
CMD作为默认参数 |
| ENTRYPOINT + CMD | --debug |
ENTRYPOINT --debug |
附加参数追加到ENTRYPOINT后 |
生产建议:对于后端Java/Go应用,推荐使用
ENTRYPOINT固定可执行文件,CMD提供默认JVM参数或配置文件路径。这样运维人员可以通过docker run追加参数调整启动行为,而不必修改Dockerfile。
2.3 镜像分层原理
Docker镜像由一系列只读层堆叠而成。每一层代表Dockerfile中的一条指令(FROM、RUN、COPY等)。当容器运行时,Docker在镜像层之上添加一个可写容器层。
2.4 分层带来的镜像体积陷阱
分层机制虽然节省了存储和网络传输,但也带来了一个常见的体积陷阱:文件删除不会减小镜像体积。
典型错误:很多开发者这样写Dockerfile:
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get remove -y curl
RUN apt-get autoremove -y
结果:镜像体积仍然很大!因为apt-get install层已经包含了curl及其依赖文件,后续的remove层只是在容器层标记删除,但镜像层中的文件依然存在。
正确的做法是:在同一层中完成安装、使用、清理:
FROM ubuntu:22.04
RUN apt-get update && \
apt-get install -y curl && \
# 使用curl完成某些操作
curl -o /tmp/init.sh https://example.com/init.sh && \
# 最后清理,所有操作在同一层
apt-get remove -y curl && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
2.5 构建缓存与层复用
Docker在构建时会缓存每一层。如果Dockerfile的某条指令和缓存命中,Docker会直接使用缓存层,跳过执行。这是加速构建的关键机制,但也可能导致缓存失效问题。
2.6 缓存失效的常见原因
- COPY/ADD文件内容变化:如果
COPY的文件内容发生改变,该层缓存失效,后续所有层缓存也失效。 - 基础镜像更新:
FROM ubuntu:22.04如果拉取到新版本,缓存失效。 - 构建上下文变化:即使Dockerfile没变,构建上下文中的文件变化也会导致
COPY缓存失效。 - RUN指令内容变化:如果
RUN apt-get install -y curl改为RUN apt-get install -y wget,缓存失效。
缓存优化技巧:对于后端项目,将
COPY package.json和RUN npm install放在COPY 源代码之前。这样只要package.json不变,依赖安装层就可以复用缓存,即使源代码频繁修改也不会触发依赖重新安装。
2.7 本章小结
| 知识点 | 关键要点 | 对镜像瘦身的意义 |
|---|---|---|
| FROM | 选择合适的基础镜像(如alpine) | 基础镜像越小,最终镜像越小 |
| RUN | 合并命令、清理临时文件 | 减少层数,避免无用文件残留 |
| COPY | 只复制必要文件,使用.dockerignore |
减少镜像层大小 |
| 分层原理 | 删除操作不减小体积,需在同一层清理 | 理解此原理才能正确瘦身 |
| 构建缓存 | 合理安排指令顺序,最大化缓存命中 | 加速构建,不影响体积但影响效率 |