阅读完需:约 12 分钟
镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
Docker镜像加载原理
UnionFS (联合文件系统)
UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系UnionFS。
bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
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。
分层理解
分层的镜像
我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!
思考:为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过 docker image inspect 命令!
[root@VM-0-13-centos ~]# docker image inspect tomcat
[
{
"Id": "sha256:625b734f984e48718bb88230d7ec046a58598ae2bf729a748857a4c0ced1ad9a",
"RepoTags": [
"tomcat:latest"
],
"RepoDigests": [
"tomcat@sha256:753eb1ac3f014a0fe5ba66d5563ff6523b2da5cb683f8b7d6c02f9f671335abe"
],
"Parent": "",
"Comment": "",
"Created": "2020-10-23T01:09:17.786564714Z",
"Container": "246bc70015a0001f168e1190820c867e4c41d8207ed44000b79809b44557a7e1",
"ContainerConfig": {
"Hostname": "246bc70015a0",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"8080/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/tomcat/bin:/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"JAVA_HOME=/usr/local/openjdk-11",
"JAVA_VERSION=11.0.9",
"CATALINA_HOME=/usr/local/tomcat",
"TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib",
"LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib",
"GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23",
"TOMCAT_MAJOR=9",
"TOMCAT_VERSION=9.0.39",
"TOMCAT_SHA512=307ca646bac267e529fb0862278f7133fe80813f0af64a44aed949f4c7a9a98aeb9bd7f08b087645b40c6fefdd3a7fe519e4858a3dbf0a19c38c53704f92b575"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"catalina.sh\" \"run\"]"
],
"ArgsEscaped": true,
"Image": "sha256:7c27f966de2ab17d8ba252006f1a580c71edd3a69dbfaa4c13a7ae3d954d035f",
"Volumes": null,
"WorkingDir": "/usr/local/tomcat",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "18.09.7",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"8080/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/tomcat/bin:/usr/local/openjdk-11/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"JAVA_HOME=/usr/local/openjdk-11",
"JAVA_VERSION=11.0.9",
"CATALINA_HOME=/usr/local/tomcat",
"TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib",
"LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib",
"GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23",
"TOMCAT_MAJOR=9",
"TOMCAT_VERSION=9.0.39",
"TOMCAT_SHA512=307ca646bac267e529fb0862278f7133fe80813f0af64a44aed949f4c7a9a98aeb9bd7f08b087645b40c6fefdd3a7fe519e4858a3dbf0a19c38c53704f92b575"
],
"Cmd": [
"catalina.sh",
"run"
],
"ArgsEscaped": true,
"Image": "sha256:7c27f966de2ab17d8ba252006f1a580c71edd3a69dbfaa4c13a7ae3d954d035f",
"Volumes": null,
"WorkingDir": "/usr/local/tomcat",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 648456727,
"VirtualSize": 648456727,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/af94439eb4795b87c90cedbd4b4a8cd08a6c1ce64767fb9eb050a13b65c50e49/diff:/var/lib/docker/overlay2/13bad8fda8ed974c297869925ab2d6026bb2490ac6b1a8242278e61f0b73f6f0/diff:/var/lib/docker/overlay2/1d61e0603376b036e1994e39b86a21c57c1fd035d6664d10108845cc7605fbf6/diff:/var/lib/docker/overlay2/c9ac70244721da64850fb8d832824e8f03a71f990ccd5471806a8d37f837d6df/diff:/var/lib/docker/overlay2/e47f63c7720441ffa309a1de98eb8abaa0435eec3e68a568e86c9e0891010f6e/diff:/var/lib/docker/overlay2/c75caabf79e585c842a731b9b4d64a56f5dba2e50286ba6025197a36445080b8/diff:/var/lib/docker/overlay2/3deb22eaa5b2ba4e82453e1bec4cb5345c246d59d2d0d83c446e98daab77b0e4/diff:/var/lib/docker/overlay2/2be6bd484b6db79d669d9589d7f9fb2c658b59b41015fbde8f5f1cb7a17f41c9/diff:/var/lib/docker/overlay2/982762535eeb22413c59fff9b5eee464a224a4a69cae717df2283f2526e83560/diff",
"MergedDir": "/var/lib/docker/overlay2/8507329f3068c82c529a3219d7076febf4a1513e39a7c2c103b47a66de45e3ed/merged",
"UpperDir": "/var/lib/docker/overlay2/8507329f3068c82c529a3219d7076febf4a1513e39a7c2c103b47a66de45e3ed/diff",
"WorkDir": "/var/lib/docker/overlay2/8507329f3068c82c529a3219d7076febf4a1513e39a7c2c103b47a66de45e3ed/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:9780f6d83e45878749497a6297ed9906c19ee0cc48cc88dc63827564bb8768fd",
"sha256:630e4f1da707756a13794d97734f7d80cade3fd69791ccfe1b7b13f06f238a2e",
"sha256:fe342cfe5c83d392b20579683f75e7fd1c7524a9a2385b74c0998096e5e074bd",
"sha256:f49d20b92dc8af0dd805860e55a56dc74ef524cc069f286ceeb00d51fd9d07e3",
"sha256:849ea2764450796788a61453fa873dba6f14c0dbd99610445dc07761d54062d1",
"sha256:5a3f3d0b6aa3eee974cc47cbe6f869dc2c0e197bc14310837681918deb8e1f54",
"sha256:6b84b6a64171935b1a299dbed0996b8970c443ff58f203a3baf5b078731f5f5c",
"sha256:20bdfd7affa9b1b1a6f6c2adc662334a781ae44ab7fcd2529e482730ba61a30c",
"sha256:181e735808a5cebdc2dbcaa1ba63b01c0f44685f0e93a03e8d974552f98471d9",
"sha256:d208025b336deb6b51e974e1be19501b06388a7fc06b39e9442c5aba62a13bc9"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]
理解:
所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含 3 个文件,而镜像包含了来自两个镜像层的 6 个文件。
上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有 6 个文件,这是因为最上层中的文件7 是文件 5 的一个更新版本。
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker 在 Windows 上仅支持 windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW[1]。
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层!
镜像Commit
docker commit 从容器创建一个新的镜像。
docker commit 提交容器副本使之成为一个新的镜像!
# 语法
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]
测试
# 1、从DockerHub下载tomcat镜像到本地并运行-it交互终端-p端口映射
docker run -it -p 8080:8080 tomcat
# 注意:坑爹:docker启动官方tomcat镜像的容器,发现404是因为使用了加速器,而加速器里的tomcat的webapps下没有root等文件!
# 下载tomcat官方镜像,就是这个镜像(阿里云里的tomcat的webapps下没有任何文件)
# 进入tomcat查看cd到webapps下发现全部空的,反而有个webapps.dist里有对应文件,cp-r到webapps下!
root@aba865b53114:/usr/local/tomcat# cp -r webapps.dist/* webapps
docker images #查看,我们自己提交的镜像已经OK了!
# 4、这个时候,我们的镜像都是可以使用的,大家可以启动原来的tomcat,和我们新tomcat02来测试看看!
[root@kuangshen~]# docker run -it -p 8080:8080 tomcat02:1.1
# 如果你想要保存你当前的状态,可以通过commit,来提交镜像,方便使用,类似于VM中的快照!