Docker的本质是提供“容器”服务,Docker使用的“容器”技术,主要是以Linux的cgroup、namespace等内核特性为基础,保障进程或者进程组处于一个隔离、安全的环境。

Docker镜像介绍

什么是Docker镜像?据Docker官网的技术文档描述,Image(镜像)是Docker术语的一种,代表一个只读的layer。而layer则具体代表Docker Container文件系统中可叠加的一部分。如此介绍docker镜像,想必理解起也是云里雾里,不如我们先从docker镜像的四个组成说起:rootfs、Union mount、image以及layer。

rootfs

 代表一个Docker Container在启动时(而非运行后)其内部进程可见的文件系统视角,或者是Docker Container的根目录。
传统来说,Linux操作系统内核启动时,内核首先会挂载一个只读(read-only)的rootfs,当系统检测其完整性之后,决定是否将其切换为读写(read-write)模式,或者最后在rootfs之上另行挂载一种文件系统并忽略rootfs。Docker架构下,依然沿用Linux中rootfs的思想。当Docker Daemon为Docker Container挂载rootfs的时候,与传统Linux内核类似,将其设定为只读(read-only)模式。在rootfs挂载完毕之后,和Linux内核不一样的是,Docker Daemon没有将Docker Container的文件系统设为读写(read-write)模式,而是利用Union mount的技术,在这个只读的rootfs之上再挂载一个读写(read-write)的文件系统,挂载时该读写(read-write)文件系统内空无一物
[30.jpg]
 观察图可以发现:容器虽然只有一个文件系统,但该文件系统由“两层”组成,分别为读写文件系统和只读文件系统。这样的理解已然有些层级(layer)的意味。

Union mount

代表一种文件系统挂载的方式,允许同一时刻多种文件系统挂载在一起,并以一种文件系统的形式,呈现多种文件系统内容合并后的目录。
一般情况下,通过某种文件系统挂载内容至挂载点的话,挂载点目录中原先的内容将会被隐藏。而Union mount则不会将挂载点目录中的内容隐藏,反而是将挂载点目录中的内容和被挂载的内容合并,并为合并后的内容提供一个统一独立的文件系统视角。通常来讲,被合并的文件系统中只有一个会以读写(read-write)模式挂载,而其他的文件系统的挂载模式均为只读(read-only)。实现这种Union mount技术的文件系统一般被称为Union Filesystem。
[31.jpg
可以暂且将该容器整个rootfs当成是一个文件系统。上文也提到,挂载时读写(read-write)文件系统中空无一物。既然如此,从用户视角来看,容器内文件系统和rootfs完全一样,用户完全可以按照往常习惯,无差别的使用自身视角下文件系统中的所有内容;然而,从内核的角度来看,两者在有着非常大的区别。追溯区别存在的根本原因,那就不得不提及AUFS等文件系统的COW(copy-on-write)特性。
COW文件系统和其他文件系统最大的区别就是:从不覆写已有文件系统中已有的内容。由于通过COW文件系统将两个文件系统(rootfs和read-write filesystem)合并,最终用户视角为合并后的含有所有内容的文件系统,然而在Linux内核逻辑上依然可以区别两者,那就是用户对原先rootfs中的内容拥有只读权限,而对read-write filesystem中的内容拥有读写权限。

image

Docker中rootfs的概念,起到容器文件系统中基石的作用。对于容器而言,其只读的特性,也是不难理解。神奇的是,实际情况下Docker的rootfs设计与实现比上文的描述还要精妙不少。
虽然通过Union mount可以实现rootfs与read-write filesystem的合并,但是考虑到rootfs自身接近200MB的磁盘大小,如果以这个rootfs的粒度来实现容器的创建与迁移等,是否会稍显笨重,同时也会大大降低镜像的灵活性。而且,若用户希望拥有一个ubuntu 14.10的rootfs,那么是否有必要创建一个全新的rootfs。
Docker中image的概念,非常巧妙的解决了以上的问题。最为简单的解释image,就是 Docker容器中只读文件系统rootfs的一部分。换言之,实际上Docker容器的rootfs可以由多个image来构成。
多个image构成rootfe的示意图如下:
[32.jpg]
图中的容器rootfs包含4个image,其中每个image中都有一些用户视角文件系统中的一部分内容。4个image处于层叠的关系,除了最底层的image,每一层的image都叠加在另一个image之上。另外,每一个image均含有一个image ID,用以唯一的标记该image。
通过image的形式,原先较为臃肿的rootfs被逐渐打散成轻便的多层。Image除了轻便的特性,同时还有上文提到的只读特性,如此一来,在不同的容器、不同的rootfs中image完全可以用来复用
多image组织关系与复用关系如图
[33.jpg]
共罗列了11个image,这11个image之间的关系呈现一副森林图。森林中含有两棵树,左边树中包含5个节点,即含有5个image;右边树中包含6个节点,即含有6个image。图中,有些image标记了红色字段,意味该image代表某一种容器镜像rootfs的最上层image。如图中的ubuntu:14.04,代表imageID_3为该类型容器rootfs的最上层,沿着该节点找到树的根节点,可以发现路径上还有imageID_2,imageID_1和imageID_0。特殊的是,imageID_2作为imageID_3的Parent Image,同时又是容器镜像ubuntu:12.04的rootfs中的最上层,可见镜像ubuntu:14.04只是在镜像ubuntu:12.04之上,再另行叠加了一层。因此,在下载镜像ubuntu:12.04以及ubuntu:14.04时,只会下载一份imageID_2、imageID_1和imageID_0,实现image的复用。

layer

Docker术语中,layer是一个与image含义较为相近的词。容器镜像的rootfs是容器只读的文件系统,rootfs又是由多个只读的image构成。于是,rootfs中每个只读的image都可以称为一层layer。总结而言,Docker容器中的每一层只读的image,以及最上层可读写的文件系统,均被称为layer。如此一来,layer的范畴比image多了一层,即多包含了最上层的read-write filesystem。
上文对于image的分析中,提到了image有复用的特性,既然如此,再提一个更为大胆的假设:容器的top layer是否可以转变为image?
答案是肯定的。Docker的设计理念中,top layer转变为image的行为(Docker中称为commit操作),大大释放了容器rootfs的灵活性。Docker的开发者完全可以基于某个镜像创建容器做开发工作,并且无论在开发周期的哪个时间点,都可以对容器进行commit,将所有top layer中的内容打包为一个image,构成一个新的镜像。
Docker引入优秀的“镜像”技术时,着实使容器的使用变得更为便利,也拓宽了Docker的使用范畴。然而,于此同时,我们也应该理性地看待镜像技术引入时,是否会带来其它的副作用。

Last modification:November 15th, 2019 at 04:42 pm
如果觉得我的文章对你有用,请随意赞赏