02_basic_concept/2.1_image.md
版本说明:本节示例基于 Docker v29.x 编写。示例中使用的镜像标签(如
ubuntu:24.04、nginx:latest)为演示用途,建议查阅 Docker Hub 或相应镜像的官方页面确认最新可用版本。
Docker 镜像作为容器运行的基石,其设计理念和实现机制至关重要。本节将深入探讨镜像的本质、与操作系统的关系、内容构成以及核心的分层存储机制。
Docker 镜像是一个只读的模板,包含了运行应用所需的一切:代码、运行时、库、环境变量和配置文件。 如果用一个类比:镜像就像是一张光盘或 ISO 文件。你可以用同一张光盘在不同电脑上安装系统,而光盘本身不会被修改。同样,一个镜像可以创建多个容器,而镜像本身保持不变。
我们都知道,操作系统分为 内核 和 用户空间:
flowchart TD
subgraph UserSpace ["用户空间"]
direction TB
App["应用程序、工具、库、配置文件...
(这部分被打包成 Docker 镜像)"]
end
subgraph KernelSpace ["Linux 内核"]
direction TB
Kernel["容器共享宿主机的内核"]
end
UserSpace --- KernelSpace
对于 Linux 而言,内核启动后会挂载 root 文件系统来提供用户空间支持。Docker 镜像 本质上就是一个 root 文件系统。
例如,官方镜像 ubuntu:24.04 包含了一套完整的 Ubuntu 24.04 最小系统的 root 文件系统——但 不包含 Linux 内核 (因为容器共享宿主机的内核)。
Docker 镜像是一个特殊的文件系统,包含:
| 内容类型 | 示例 |
|---|---|
| 程序文件 | 应用二进制文件、Python/Node 解释器 |
| 库文件 | libc、OpenSSL、各种依赖库 |
| 配置文件 | nginx.conf、my.cnf 等 |
| 环境变量 | PATH、LANG 等预设值 |
| 元数据 | 启动命令、暴露端口、数据卷定义 |
关键特性:
镜像的分层存储机制是 Docker 最具创新性的特性之一。通过 Union FS 技术,Docker 能够高效地构建和管理镜像。
笔者认为,分层存储是 Docker 最巧妙的设计之一。
假设你有三个应用,都基于 Ubuntu 运行:
flowchart TD
subgraph Traditional ["传统方式(不分层)总计: 1.5GB ❌"]
direction LR
AppA_Trad["App A
Ubuntu 500MB"]
AppB_Trad["App B
Ubuntu 500MB"]
AppC_Trad["App C
Ubuntu 500MB"]
end
subgraph DockerLayered ["Docker 分层方式 总计: 620MB ✅"]
direction TB
subgraph Apps ["应用层"]
direction LR
AppA["App A 50MB"]
AppB["App B 30MB"]
AppC["App C 40MB"]
end
Ubuntu["Ubuntu
(共享)500MB"]
AppA --> Ubuntu
AppB --> Ubuntu
AppC --> Ubuntu
end
笔者用一个更贴近最佳实践的 Dockerfile 来解释分层:
FROM ubuntu:24.04 # 第 1 层:基础系统(约 78MB)
RUN apt-get update && apt-get install -y nginx # 第 2 层:更新索引并安装 nginx
COPY app.conf /etc/nginx/ # 第 3 层:复制配置文件
这里特意把 apt-get update 和 apt-get install 放在同一个 RUN 里,避免构建缓存导致包索引过期。
构建后的镜像结构:
flowchart TD
Layer3["第 3 层: COPY app.conf (只读)
← 最新添加的层"]
Layer2["第 2 层: nginx 安装文件与包索引 (只读)"]
Layer1["第 1 层: Ubuntu 基础系统 (只读)
← 基础镜像层"]
Layer3 --> Layer2 --> Layer1
每一层的特点:
⚠️ 笔者特别提醒:理解这一点可以帮你避免构建出臃肿的镜像。关键原理:每一层的文件变化会被记录,但 删除操作只是标记,不会真正减小镜像体积。
## 错误示范 ❌
FROM ubuntu:24.04
RUN apt-get update
RUN apt-get install -y build-essential # 安装编译工具(约 200MB)
RUN make && make install # 编译应用
RUN apt-get remove build-essential # 试图删除编译工具
## 结果:镜像仍然包含 200MB 的编译工具!
## 正确做法 ✅
FROM ubuntu:24.04
RUN apt-get update && \
apt-get install -y build-essential && \
make && make install && \
apt-get remove -y build-essential && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/*
## 在同一层完成安装、使用、清理
## 查看镜像的历史(每层的构建记录)
$ docker history nginx:latest
IMAGE CREATED CREATED BY SIZE
a6bd71f48f68 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
<missing> 2 weeks ago STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago EXPOSE map[80/tcp:{}] 0B
<missing> 2 weeks ago ENTRYPOINT ["/docker-entrypoint.sh"] 0B
<missing> 2 weeks ago COPY 30-tune-worker-processes.sh /docker-ent… 4.62kB
...
需要注意:docker history 展示的是镜像的构建历史。像 CMD、ENTRYPOINT、EXPOSE、STOPSIGNAL 这类 0B 项主要是镜像配置元数据,并不等同于新增了可见的文件系统内容;真正占用空间的通常是 RUN、COPY、ADD 等带来的文件变化。
Docker 镜像有多种标识方式:
格式:[仓库地址/]仓库名[:标签]
## 完整格式
registry.example.com/myproject/myapp:v1.2.3
## 简写(使用 Docker Hub)
nginx:1.30
ubuntu:24.04
## 省略标签(默认使用 latest)
nginx # 等同于 nginx:latest
每个镜像有一个基于内容计算的唯一 ID:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a6bd71f48f68 2 weeks ago 187MB
ubuntu 24.04 ca2b0f26964c 3 weeks ago 78.1MB
更精确的标识,基于镜像内容的 SHA256 哈希:
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID
nginx latest sha256:6db391d1c0cfb30588ba0bf72ea999404f2764184d8b8d10d89e8a9c6... a6bd71f48f68
💡 笔者建议:在生产环境使用镜像摘要而非标签,因为标签可以被覆盖,但摘要是不可变的。
Docker 镜像可以通过以下方式获取:
| 方式 | 说明 | 示例 |
|---|---|---|
| 从 Registry 拉取 | 最常用的方式 | docker pull nginx |
| 从 Dockerfile 构建 | 自定义镜像 | docker build -t myapp . |
| 从容器提交 | 保存容器状态 (不推荐) | docker commit |
| 从文件导入 | 离线传输 | docker load < image.tar |