Docker 镜像构建原理与实战:从分层存储到自定义镜像制作
在 Docker 的世界中,镜像 (Image) 是一切的起点。理解镜像的分层结构、加载原理,并掌握自定义镜像的构建方法,是迈向高效容器化开发的关键一步。本文将深入剖析 Docker 镜像的底层机制,并通过两种主流方式(docker commit 与 Dockerfile)演示如何创建自己的镜像。
一、Docker 镜像的核心:分层存储架构
1. 镜像为什么是分层的?
Docker 镜像采用 UnionFS (联合文件系统) 技术,将文件系统拆分为多个只读层(Read-only Layers),每一层代表一次变更(如安装软件、添加文件)。这种设计带来三大核心优势:
- ✅ 资源共享:多个镜像可共享相同的基础层(如 Ubuntu 20.04),节省磁盘空间
- ✅ 高效传输:拉取镜像时仅下载缺失的层,加速分发
- ✅ 版本控制:每层都有唯一 ID,支持回滚与审计

📌 关键概念:
- 基础镜像 (Base Image):最底层,通常为精简 OS(如
scratch,alpine)- 中间层 (Intermediate Layers):由
RUN,COPY等指令生成- 可写层(Container Layer):容器运行时在顶层创建的读写层
2. 镜像加载原理:bootfs 与 rootfs
当 Docker 启动容器时,会按顺序加载两层文件系统:
| 层级 | 组件 | 作用 | 容器运行时状态 |
|---|---|---|---|
| bootfs | bootloader + kernel | 引导系统启动 | 卸载(由宿主机内核接管) |
| rootfs | /bin, /etc, /usr... | 标准 Linux 目录结构 | 只读挂载(作为镜像层) |
🔍 为什么 bootfs 会被卸载 ?
Docker 容器直接复用宿主机内核,无需 Guest OS。因此启动后立即卸载 bootfs,仅保留 rootfs 作为应用运行环境。
3. 容器的读写层:Copy-on-Write (CoW)

- 镜像层(Image Layers):全部只读,不可修改
- 容器层(Container Layer):位于最顶层,所有写操作(创建/修改/删除文件)都在此进行
- CoW 机制 :
- 读文件 → 从上至下搜索各层,返回首个匹配
- 修改文件 → 将原文件复制到容器层再修改(避免污染镜像层)
- 删除文件 → 在容器层标记"whiteout"(隐藏下层同名文件)
💡 重要结论 :
容器停止后,容器层数据会丢失 !持久化数据必须通过 Volume 或 Bind Mount 实现。
二、方法一:通过 docker commit 制作镜像(不推荐用于生产)
docker commit 允许你基于正在运行的容器创建新镜像。虽然简单直观,但存在严重缺陷:
- ❌ 无法追溯构建步骤(无历史记录)
- ❌ 镜像臃肿(包含临时文件、缓存)
- ❌ 不可重复(依赖容器当时的状态)
实战案例:为 Ubuntu 镜像添加 Vim
步骤 1:启动并进入容器
bash
# 运行 Ubuntu 容器(交互式)
$ docker run -it --name ubuntu-dev ubuntu:20.04 /bin/bash
# 在容器内更新包索引并安装 Vim
root@container-id:/# apt-get update
root@container-id:/# apt-get install -y vim
root@container-id:/# exit # 退出但不删除容器
步骤 2:提交为新镜像
bash
# 语法:docker commit [OPTIONS] CONTAINER REPOSITORY[:TAG]
$ docker commit \
-a "Your Name" \ # 作者信息
-m "Install vim editor" \ # 提交说明
ubuntu-dev \ # 源容器名称或ID
my-ubuntu:vim-1.0 # 新镜像名:标签
# 输出新镜像ID
sha256:8a3b5c7d9e1f2a4b6c8d0e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4
步骤 3:验证新镜像
bash
# 查看镜像列表
$ docker images | grep my-ubuntu
my-ubuntu vim-1.0 8a3b5c7d9e1f 2 minutes ago 120MB
# 测试 Vim 是否可用
$ docker run --rm -it my-ubuntu:vim-1.0 vim --version
VIM - Vi IMproved 8.1 (2018 May 18)
⚠️ 为什么生产环境不推荐
commit?
- 镜像包含
apt-get缓存(浪费 50MB+ 空间)- 无法自动化重建(下次需手动重复操作)
- 违反 不可变基础设施 原则
三、方法二:通过 Dockerfile 构建镜像(生产标准)
Dockerfile 是一个文本指令集 ,明确定义了镜像的构建步骤。它解决了 commit 的所有痛点:
- ✅ 可重复 :任何人执行
docker build都能得到相同结果 - ✅ 轻量:通过多阶段构建剔除构建依赖
- ✅ 可审计:每条指令对应一个镜像层,清晰透明
实战案例:编写 Vim 镜像的 Dockerfile
步骤 1:创建项目目录
bash
mkdir my-ubuntu-vim && cd my-ubuntu-vim
步骤 2:编写 Dockerfile
dockerfile
# 使用官方 Ubuntu 20.04 作为基础镜像
FROM ubuntu:20.04
# 设置非交互式安装(避免 apt 弹窗)
ENV DEBIAN_FRONTEND=noninteractive
# 更新包索引并安装 Vim,最后清理缓存(关键!)
RUN apt-get update && \
apt-get install -y vim && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 设置默认命令(可选)
CMD ["/bin/bash"]
步骤 3:构建镜像
bash
# 语法:docker build -t <镜像名:标签> <上下文路径>
$ docker build -t my-ubuntu:vim-df-1.0 .
# 构建过程输出(关键层)
Step 1/4 : FROM ubuntu:20.04
---> ba6acccedd29
Step 2/4 : ENV DEBIAN_FRONTEND=noninteractive
---> Running in abc123...
---> def456... # 新层ID
Step 3/4 : RUN apt-get update && ...
---> ghi789... # 包含 Vim 的层
Step 4/4 : CMD ["/bin/bash"]
---> jkl012...
Successfully built jkl012...
Successfully tagged my-ubuntu:vim-df-1.0
步骤 4:对比镜像大小
bash
$ docker images | grep my-ubuntu
my-ubuntu vim-1.0 8a3b5c7d9e1f 120MB # commit 方式
my-ubuntu vim-df-1.0 jkl012... 75MB # Dockerfile 方式(清理缓存后)
✅ Dockerfile 优势体现 :
通过
apt-get clean && rm -rf /var/lib/apt/lists/*清理缓存,减少 45MB 体积!
四、Dockerfile 最佳实践
1. 减少镜像层数
- 合并
RUN指令(用&&连接命令) - 避免不必要的层(如单独
ENV可合并到RUN)
2. 利用构建缓存
- 将变化频率低 的指令放在前面(如
FROM,COPY requirements.txt) - Docker 会复用未变化层的缓存,加速构建
3. 多阶段构建(Multi-stage Build)
dockerfile
# 第一阶段:编译应用
FROM golang:1.19 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# 第二阶段:运行时镜像
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]
- 效果:最终镜像仅包含可执行文件,不含 Go 编译器(体积减少 90%+)
4. 安全加固
-
使用非 root 用户运行:
dockerfileRUN adduser --disabled-password --gecos '' appuser USER appuser -
扫描漏洞:
docker scan my-ubuntu:vim-df-1.0
五、总结:两种方法对比
| 特性 | docker commit |
Dockerfile |
|---|---|---|
| 可重复性 | ❌ 依赖容器状态 | ✅ 指令明确 |
| 镜像大小 | ❌ 包含冗余数据 | ✅ 可优化清理 |
| 可维护性 | ❌ 无构建历史 | ✅ 版本可控 |
| 适用场景 | 临时调试、紧急修复 | 所有生产环境 |
🚀 行动建议:
- 日常开发:用
commit快速验证想法- 交付生产:必须使用
Dockerfile- 进阶学习:掌握多阶段构建、BuildKit 优化
掌握镜像构建原理与 Dockerfile 编写,你就拥有了打造标准化、轻量化、安全化 容器应用的能力。下一步,我们将探索如何用 docker-compose 编排多容器应用!
🔗 延伸阅读: