学习本文需要一些了解Docker的概念以及一些名词。
个人网站:https://linzyblog.netlify.app/
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时的库、环境变量和配置文件。
镜像是一个只读模板,带有创建Docker容器的说明。通常,一个镜像基于另一个镜像,并带有一些额外的定制。例如,您可以构建一个基于ubuntu镜像的镜像,但是要安装Apache web服务器和您的应用程序,以及运行应用程序所需的配置细节。
Docker 镜像(Image),就相当于是一个 模板,其中包含创建 Docker 容器的说明,可以通过模板来创建容器服务,通过这个镜像我们可以创建多个容器,最终服务运行或项目运行都是在容器中的。
你可以创建自己的镜像,也可以只使用其他人创建并在Docker Hub中发布的镜像。要构建自己的镜像,需要创建一个Dockerfile,使用简单的语法定义创建和运行镜像所需的步骤。Dockerfile中的每一条指令都会在图像中创建一个层。当你更改Dockerfile并重新构建镜像时,只有那些已经更改的层才会重新构建。与其他虚拟化技术相比,这是镜像如此轻量级、小巧和快速的原因之一。
Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统被称为UnionFS。
Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,而且目录的物理位置是分开的。
Union文件系统可以把只读和可读写文件系统合并在一起,具有Copy-on-Write功能,允许只读文件系统的修改可以保存到可写文件系统当中。
Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统回包含所有底层的文件和目录。
base 镜像简单来说就是不依赖其他任何镜像,完全从0开始建起,其他镜像都是建立在他的之上,可以比喻为大楼的地基,docker镜像的鼻祖。
base 镜像的特性:
(1)不依赖其他镜像,从 scratch 构建。
(2)其他镜像可以之为基础进行扩展。
所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等。
我们以 CentOS 为例查看 base 镜像包含哪些内容。
提问:docker cnetos的镜像大小200多M,和平时的所用的发行版的大小(几G)相差很大,为什么?
Linux 操作系统由内核空间和用户空间组成。
对于 base 镜像来说,底层直接用 Host 的 kernel,自己只需要提供 rootfs 就行了。
而对于一个精简的 OS,rootfs 可以很小,只需要包括最基本的命令、工具和程序库就可以了。相比其他 Linux 发行版,CentOS 的 rootfs 已经算臃肿的了,alpine 还不到 10MB。
由此可见对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
我们平时安装的 CentOS 除了 rootfs 还会选装很多软件、服务、图形桌面等,需要好几个 GB 就不足为奇了。
base 镜像提供的是最小安装的 Linux 发行版。
bootfs(boot file system):主要包含 bootloader 和 kernel。
rootfs(root file system):在bootfs之上,包含类似于典型Linux系统中的/dev,/proc,/bin,/etc等标准目录文件。
参考文章:https://blog.51cto.com/wzlinux/2044797
Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的。比如我们现在构建一个新的镜像,Dockerfile 如下:
# Version: 0.0.1
FROM debian 1.新镜像不再是从 scratch 开始,而是直接在 Debian base 镜像上构建。
MAINTAINER wzlinux
RUN apt-get update && apt-get install -y emacs 2.安装 emacs 编辑器。
RUN apt-get install -y apache2 3.安装 apache2。
CMD ["/bin/bash"] 4.容器启动时运行 bash。
构建过程:
可以看到,Docker镜像都起始于一个基础镜像层,新镜像是从 Base 镜像一层一层叠加生成的。
当进行修改或增加新的内容时,就会在当前镜像层上,创建新的镜像层。
如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是否也会被修改?
答案是不会!
修改会被限制在单个容器内。
这就是我们接下来要说的容器 Copy-on-Write(COW) 特性。
- 新数据会直接存放在最上面的容器层。
- 修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变。
- 如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件。
Copy-on-write 是一种共享和复制文件以实现最大效率的策略。当我们试图读取一个文件时,Docker 会从上到下一层层去找这个文件,找到的第一个就是我们的文件,所以下面层相同的文件就被“覆盖”了。
而修改就是当我们找到这个文件时,将它“复制”到读写层并修改,这样读写层的文件就是我们修改后的文件,并且“覆盖”了镜像中的文件了。这最大限度地减少了 I/O 和每个后续层的大小。而删除就是创建了一个特殊的 whiteout 文件,这个 whiteout 文件覆盖的文件即表示删除了。
这是一个三层的镜像分层结构图,在外部看整个镜像只有6个文件,app2.0 是 app1.0的更新版。
在这种情况下,上层镜像层中的文件会覆盖底层镜像层的文件。这样就使得文件的更新版本作为新镜像层添加到镜像中。
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
- 存储引擎的选择文档:https://docs.docker.com/storage/storagedriver/select-storage-driver/
Linux 上可用存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker 在 Windows 上仅支持 Windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和COW。
Docker 镜像都是只读的,当你创建一个新的容器时,你会在基础层之上添加一个新的可写层,该层通常都称为 容器层。
对正在运行的容器所做的所有更改,例如写入新文件、修改现有文件和删除文件,都被写入这个可写的容器层。
下图显示了基于ubuntu:15.04图像的容器。
存储引擎处理有关这些层相互交互方式的详细信息。有不同的存储引擎可用,它们在不同的情况下各有优缺点。
将容器提交后,创建为一个新的镜像,命令与git原理类似。
docker commit [OPTIONS] 容器id [目标镜像名[:TAG]]
名称,简写 | 默认 | 描述 |
---|---|---|
–author,-a | 作者 | |
–change,-c | 将 Dockerfile 指令应用于创建的镜像 | |
–message,-m | 提交的描述消息 | |
–pause,-p | true | 提交期间暂停容器 |
将容器的文件更改或设置后创建为新镜像。这允许您通过运行交互式shell调试容器,或者将工作数据集导出到另一个服务器。一般来说,最好使用Dockerfiles以文档化和可维护的方式来管理映像。
提交操作将不包括容器内挂载的卷中包含的任何数据。
默认情况下,正在提交的容器及其进程将在映像提交时暂停。这减少了在创建提交过程中遇到数据损坏的可能性。如果不希望出现这种行为,请将 --pause 选项设置为false。
–change选项将对所创建的映像应用Dockerfile指令。
支持的Dockerfile指令:CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | ONBUILD | USER | VOLUME | WORKDIR
docker ps 找到一个新的容器,用commit命令提交一个容器,新增了一个命名为commit/test的镜像 tag为test1.0,用docker images查看本地镜像是否成功提交了。
提交一个新配置的容器,新增了一个命名为commit/test的镜像 tag为test2.0,跟上一个例子区分