基础篇 Docker 镜像
内容概述
Docker 镜像是学习docker 的关键。
# 1. Docker 镜像简介
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
所有应用,直接打包docker 镜像,就可以直接跑起来!
如何得到镜像:
- 从远程仓库下载
- 别人拷贝给你
- 自己制作一个镜像 Dockerfile
# 2. Docker 镜像原理
# 2.1 UnionFS(联合文件系统)
我们镜像pull下载的时候看到的一层层就是这个!

UnionFS(联合文件系统):union文件系统(UnionFS) 是一种分层、轻量级摒弃高性能的文件系统,它支持对文件系统的修改作为一次提交来层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。 union 文件系统是docker 镜像基础,镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
# 2.2 加载原理剖析
docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system) 主要包含bootloader 和kernel,bpotloader 主要是引导加载kernel,当我们加载镜像的时候,会通过bootloader 加载kernal,Docker 镜像最底层是bootfs,当boot 加载完成后整个kernal 内核都在内存中了,bootfs 也就可以卸载,值得注意的是,bootfs 是被所有镜像共用的,许多镜像images 都是在base image(rootfs) 基础上叠加的
rootfs (root file system),在bootfs 之上。包含的就是典型Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件。rootfs 就是各种不同的操作系统发行版,比如Ubuntu, Centos 等等

思考: 平时我们发现安装虚拟机的centos都有好几个G。为什么docker这里才200M?
- 对于一个精简的OS,rootfs 可以很小,只需要包含最基本的命令、工具和程序库就可以了,因为底层直接用到host 的kernel。自己只需要提供rootfs 就可以了。由此可见对于不同的linux发行版,bootfs 基本是一致的,rootfs 会有差别,因此不同的发行版可以公用bootfs。
# 3. Docker 分层理解
# 3.1 镜像层
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!

思考:为什么docker镜像要采用这种分层的结构呢?
- 其实答案很简单,就是资源共享,减少内存浪费。
查看镜像分层
docker image inspect
示例:
[root@node1 ~]# docker image inspect redis:latest
[
//.....
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:cb42413394c4059335228c137fe884ff3ab8946a014014309676c25e3ac86864",
"sha256:8e14cb7841faede6e42ab797f915c329c22f3b39026f8338c4c75de26e5d4e82",
"sha256:1450b8f0019c829e638ab5c1f3c2674d117517669e41dd2d0409a668e0807e96",
"sha256:f927192cc30cb53065dc266f78ff12dc06651d6eb84088e82be2d98ac47d42a0",
"sha256:a24a292d018421783c491bc72f6601908cb844b17427bac92f0a22f5fd809665",
"sha256:3480f9cdd491225670e9899786128ffe47054b0a5d54c48f6b10623d2f340632"
]
}
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3.2 分层理解
所有的docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu linux 16.04创建一个新镜像,这就是新镜像的第一层;如果在该镜像中添加python 包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就回创建第三个镜像层。

在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点很重要。
下图中举了一个简单的例子,总共有三个镜像层,每个镜像层分别包含2-2-1个文件,而镜像包含了来自两个镜像层的4个文件。

上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
如图,在外部来看整个镜像只有4个文件,这是因为第二层的文件2是第一层文件2的一个更新版本。
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件,这样就使得文件的更新版本作为一个新镜像层添加到镜像中。
docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保存多镜像层对外展示为统一的文件系统。
Linux 上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs 以及ZFS。顾名思义,每种存储引擎都是基于Linux 中对应的文件系统或者块设备技术, 并且每种存储引擎都有其独特的性能特点。
Docker在Windows 上仅支持windiwsfilter 一种存储引擎。该引擎基于NTFS 文件系统之上实现分层和COW。
综上所述,所有镜像堆叠并合并,形成了我们需要的最终镜像。
PS:上图中“各种镜像汇总结果”图层就是对外提供统一的视图的展现
# 3.3 分层特点
Docker镜像都是只读的,当容器启动时,一个新的可写层就被加载到镜像顶部。
这一层就是我问通常说的容器层,容器之下的都叫镜像层。
什么意思呢,我们一起来理解下,如下图:

- 首先,我们通过
pull命令从docker 镜像仓库拉取Tomcat 镜像,通过分层下载的方式我们看到一共下载了6 个文件; - 然后,这6 个文件行成了一个Tomcat 镜像,我们通过
run命令,让镜像在容器里面运行起来; - 最后,如图最后示意图所示,Tomcat 镜像运行起来以后,我们后续的一系列的操作都是在容器层完成,并不是基于镜像层直接操作;
# 4. 制作镜像
我们可以将上述我们操作的包括容器层、镜像层在内的打包制作成一个新的镜像,那么如何提交一个自己的镜像呢?
docker commit 提交容器成为一个新的副本,命令个Git 原理类似:
docker commit -m="提交的描述信息" -a="作者" 原容器id 目标镜像名:[tag]
# 4.1 实操案例
下面我们来实操提交一个Tomcat 镜像,分析步骤如下:
- step 1:我们下载并启动一个默认的Tomcat 镜像;
- step 2:发现这个默认的Tomcat 是没有webapps 应用,分析发现镜像原因,官方的镜像默认WebApps 下面是没有文件的;
- step 3:自己把基本的文件拷贝到WebApps 目录下,Tomcat 有了应用可以正常打开;
- step 4:将我们操作过得容器通过commit提交为一个镜像,我们以后就使用修改后的镜像。
镜像提交示例:
[root@node1 ~]# docker commit -a="ibytehorizon" -m="add webapps app" bb23ac67bf2c tomcat02:1.0
sha256:0d802e29f5d07c5ca11fa55b8a64dfae74288b375ca55e8d1f9fc2dd01a83873
[root@node1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat02 1.0 0d802e29f5d0 13 seconds ago 653 MB
redis latest 621ceef7494a 5 weeks ago 104 MB
tomcat 9.0 040bdb29ab37 5 weeks ago 649 MB
tomcat latest 040bdb29ab37 5 weeks ago 649 MB
nginx latest f6d0b4767a6c 5 weeks ago 133 MB
centos latest 300e315adb2f 2 months ago 209 MB
portainer/portainer latest 62771b0b9b09 7 months ago 79.1 MB
elasticsearch 7.6.2 f29a1ee41030 11 months ago 791 MB
2
3
4
5
6
7
8
9
10
11
12
如果想要保存当前容器的状态,就可以通过commit 来提交,获得一个镜像,就好生成快照!