Skip to main content

镜像分层原理

镜像(Image)由一组只读层(Layer)叠加而成。每一条 Dockerfile 指令通常生成一个新层。容器启动时,Docker 在镜像层之上再加一个可写层。 分层带来两个核心优势:
  • 层复用:多个镜像如果基于同一个基础镜像,相同的层在本地只存一份。拉取镜像时已有的层不会重复下载。
  • 写时复制 Copy-on-Write:容器修改文件时,先把文件从只读层复制到可写层再修改。镜像本身永远不变。
层的顺序直接影响构建缓存。变化频率低的指令(如安装系统依赖)应放在前面,变化频率高的指令(如复制代码)放在后面。

Dockerfile 逐指令讲解

Dockerfile 是定义镜像构建过程的文本文件。常用指令如下:
官方推荐优先使用 COPY 而不是 ADDADD 的自动解压行为不直观,容易踩坑。

CMD 与 ENTRYPOINT 的区别

两者可以组合使用:ENTRYPOINT 定命令,CMD 定默认参数。

完整 Dockerfile 示例

下面是一个 Node.js 应用的 Dockerfile:
CMD 推荐使用 JSON 数组格式(exec 形式)。这样应用进程会成为 PID 1,能正常接收停止信号。Shell 形式会让命令变成 sh 的子进程。

构建与发布

1

构建镜像

在 Dockerfile 所在目录执行:
-t 指定镜像名称和标签,末尾的点表示构建上下文是当前目录。
2

打标签

推送前需要给镜像加上仓库前缀:
3

推送到镜像仓库

推送到 Docker Hub 或私有仓库前,先用 docker login 登录。

多阶段构建

编译型语言(如 Go)只需要编译产物,不需要编译环境。多阶段构建把编译和运行拆到两个阶段:
最终镜像不包含 Go 编译器和源代码,体积可以从近 1 GB 降到十几 MB。

构建缓存与 .dockerignore

Docker 按指令逐层缓存。某一层变化后,其后的所有层都会重新构建。 .dockerignore 文件用于排除不需要进入构建上下文的文件,作用类似 .gitignore:
排除无关文件有两个好处:加快上下文传输速度,避免本地文件变动意外使缓存失效。

镜像瘦身最佳实践

  • 使用 alpinedistroless 等精简基础镜像
  • 合并相邻的 RUN 指令,减少层数
  • 同一个 RUN 中安装完立即清理缓存,例如 apt-get clean
  • 不安装编辑器、curl 等调试用工具,生产镜像用不上
  • .dockerignore 排除无关文件
  • 编译型语言一律使用多阶段构建
  • 固定基础镜像版本,不要直接使用 latest

延伸阅读