6.2.10 镜像和分层
Docker镜像由一些松耦合的只读镜像层组成。如图6.3所示。
Docker负责堆叠这些镜像层,并且将它们表示为单个统一的对象。
有多种方式可以查看和检查构成某个镜像的分层,本书在前面已经展示了其中一种。接下来再回顾一下 docker image pull ubuntu:latest
命令的输出内容。
$ docker image pull ubuntu:latest
latest: Pulling from library/ubuntu
952132ac251a: Pull complete
82659f8f1b76: Pull complete
c19118ca682d: Pull complete
8296858250fe: Pull complete
24e0251a0e2c: Pull complete
Digest: sha256:f4691c96e6bbaa99d...28ae95a60369c506dd6e6f6ab
Status: Downloaded newer image for ubuntu:latest
在上面输出内容中,以 Pull complete
结尾的每一行都代表了镜像中某个被拉取的镜像层。可以看到,这个镜像包含5个镜像层。图6.4以图片形式将镜像层ID作为标识展示了这些分层。
另一种查看镜像分层的方式是通过 docker image inspect
命令。下面同样以 ubuntu:latest
镜像为例。
$ docker image inspect ubuntu:latest
[
{
"Id": "sha256:bd3d4369ae.......fa2645f5699037d7d8c6b415a10",
"RepoTags": [
"ubuntu:latest"
<Snip>
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:c8a75145fc...894129005e461a43875a094b93412",
"sha256:c6f2b330b6...7214ed6aac305dd03f70b95cdc610",
"sha256:055757a193...3a9565d78962c7f368d5ac5984998",
"sha256:4837348061...12695f548406ea77feb5074e195e3",
"sha256:0cad5e07ba...4bae4cfc66b376265e16c32a0aae9"
]
}
}
]
缩减之后的输出也显示该镜像包含5个镜像层。只不过这次的输出内容中使用了镜像的SHA256散列值来标识镜像层。不过,两中命令都显示了镜像包含5个镜像层。
注:
docker history
命令显示了镜像的构建历史记录,但其并不是严格意义上的镜像分层。例如,有些Dockerfile中的指令并不会创建新的镜像层。比如ENV、EXPOSE、CMD以及ENTRY- POINT。不过,这些命令会在镜像中添加元数据。
所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。该镜像当前已经包含3个镜像层,如图6.5所示(这只是一个用于演示的很简单的例子)。
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。图6.6中举了一个简单的例子,每个镜像层包含3个文件,而镜像包含了来自两个镜像层的6个文件。
注:
图6.6中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
图6.7中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本。这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。Linux上可用的存储引擎有 AUFS
、 Overlay2
、 Device Mapper
、 Btrfs
以及 ZFS
。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。Docker在Windows上仅支持 windowsfilter
一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1]。
图6.8展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。