Docker是如何利用union file systems的?


我们都知道Docker使用union file systems 来按层构建镜像,但是Docker具体是如何区分出这些层的呢?
或者说它使用什么策略决定哪些内容在一层,哪些不在一层呢?
已邀请:

xiaolunsanguo - 京东商城-基础平台部-JDOS团队

赞同来自: 田浩浩

  1. docker的层


在这里,我们首先做一个样例,样例设定为一个镜像D。当然,这个D镜像不是单层,而是从其他层继承来的。

继承关系如下所示:

─a Virtual Size:10 KB
└─b Virtual Size: 10 MB
└─c Virtual Size: 100 MB
└─d Virtual Size: 200 MB

D镜像由a/b/c/d四层组成,我们以最后一层的大写来称谓这个镜像,叫他D镜像。同理,a/b/c三层组成的镜像就是C镜像。
  1. docker的层是怎么来的


层嘛,当然是一层层堆叠起来的。

具体来说,就是比如你现在有了一个C的镜像,然后你打算在上面增加一个软件,比如tomcat。那么你无论是通过Dockerfile,异或是通过创建一个C的实例容器,然后commit出来,这个时候你就可以得到一个新的镜像D。镜像D除了有镜像C之外,还有一部分新增的内容。所以这就有了新的一层d。以此类推,就有了诸多的层。
  1. docker是如何区分这些层


docker如何区分这些层,我们从两个角度来看,一个角度,是docker将这些层存储在了本地的graph里,并使之受自己的管理。根据不同的存储driver,当然会有一些细节的不同,但是大同小异。我这里用vfs和devicemapper的driver作为举例。其他可以自己研习。另一个角度就是docker的镜像在导入导出时,进行层次的区分。

3.1 docker镜像是如何区分这些层的

先来介绍镜像是如何区分的,这样有利于理解在本地的存储。

还是刚刚的例子,D镜像有a/b/c/d四层。

那么我们以c和d两层作为样例来举例。

显然,镜像C和D的关系是C是D的parent层。

那么从C到D,其实无外乎有四种修改:

增加了某文件(文件夹也可以看做是文件,下面统写为文件)
删除了某文件
修改了某文件
某文件不变
好了,现在我们有了c镜像,然后如何来记录D镜像,也就是如何添加d层呢?显然,有一个假定就是从C到D,大部分文件都没有变化,所以只需要采用增量式记录就可以了。具体来说就是

增加了某文件:则在d层中直接增加该文件。比如C中没有/d.txt,而在D中有,那么D中直接在根中加一个d.txt
删除了某文件:在d中进行标注,比如删除了/c.txt,那么在D中,则生成一个/..c.txt的文件,表明这个文件已经奔删除了。
修改了某文件:因为跟C中的文件不同了,同增加了某文件。
某文件不变:C中有/a.txt,D中就不必再包含了。
这样就得到了d层。有了d层和C镜像,根据上面的规则反推,就可以得到一个完整的D镜像了。这就是分层的精髓。

3.2 docker的层在本地的存储

我们从一个镜像说起,我们现在得到了一个D镜像,里面已经有了a/b/c/d四个层。好了,我现在来分别存储。

因为存储的目标是为了后面进行使用,所以在本地存储,其实是为了得到四个完整的镜像。所以,我要在本地其实要存储A/B/C/D四个完整的镜像。

docker的镜像在本地的存储。因为具体驱动不同,所以最终实现的效果不相同。

3.2.1 vfs

vfs是采用全量的方式来存储镜像的。比如有a层,那么镜像A在vfs中存储有a层的所有文件。镜像B在vfs中将首先将a/b两层的文件进行拼合(前文已经叙述),得到拥有所有文件的完整镜像B,然后存在另一个目录中。以此类推。

vfs的这种方式,有个很大弊端,就是对于层次多的镜像,需要每次要拷贝很久。

比如本例中,A镜像,需要10KB;B镜像需要10MB;C镜像需要100MB,D镜像200MB。共需要310MB。

虽然D镜像较之于C镜像,只增加了d层,100MB的东西,但是在vfs中仍然要全部存储。

3.2.2 devicemapper

vfs之所以要存储全部,就是因为他没有联合的概念。与之相对的,就是devicemapper(dm)和aufs等联合文件系统在分层上表现出来的强大能力。

联合存储是一种很神奇的东西。他可以把不同目录下的文件融合到一个目录下。比如在目录dirA下有a.txt,dirB下有b.txt。联合文件系统可以对用户提供一个dir文件,里面包含了dirA和dirB下的内容,那么对于用户,就可以看到dir下有两个文件,a.txt和b.txt了。

在本例中,存储镜像A,是需要存储完整的a层。但是在存储镜像B时,可以利用a层和b层之间的关系,只需要存储增量部分的内容即可。同理,存储到了D镜像,只要存储较之于C增量的100MB。较之于vfs,节省了很大的空间。

具体的dm或者aufs的原理,就需要再去研究他们是如何实现的联合存储了。

shlallen - DaoCloud软件工程师,合伙人

赞同来自: 郑伟-风刃


随着大家对Docker的深入,这些原理性的问题,肯定会遇到的。关于Docker镜像,我已经写好了文章供稿InfoQ了,目前由于档期的缘故,还没有发布出来。

对于博主的观点“我们都知道Docker使用union file systems 来按层构建镜像“,这样的观点,我想补充一下。

Aufs这种union filesystem,是用来联合image,最终统一提供给用户文件视角的文件系统技术。对于一个ubuntu:14.04的镜像,其实是首先下载多个镜像,镜像之间有一定的联系,其实就是标记一个image是另一个image的父镜像。

在创建容器的时候,使用aufs等union fs将这些镜像联合起来同一个交给用户一个文件系统视角,最后在最上层添加一个layer,用户权限是read-write的.

关于Docker镜像,需要清楚的概念有image,layer,rootfs,image JSON,这些内容清楚了,镜像方面内容的理解就不再话下了。

另外,一旦我关于Docker镜像的文章在InfoQ上发布,我也会再在这个post下回复,希望对大家有帮助

田浩浩 - wizmacau developer

赞同来自:


Dockerfile用来构建镜像,由于按层结构构建,其命令前后位置的不同会影响构建后镜像的略微不同,详情请参考Dockerfile最佳实践,这个系列对此问题的做了深入透彻的分析研究。

PS:推荐题主另两篇文章Docker构建脚本解析以及深入理解Docker Volume,这两篇对于理解镜像以及Docker运行容器的文件结构也很有帮助。

要回复问题请先登录注册