docker镜像与容器存储结构分析

lrz921

贡献于2018-02-03

字数:0 关键词: Docker 虚拟化

docker 镜像与容器存储结构分析 2014 年 10 月 24 日 Docker 是一个开源的应用容器引擎,主要利用 linux 内核 namespace 实 现沙盒隔离,用 cgroup 实现资源限制。 Docker 支持三种不同的镜像层次存储的 drivers: aufs、devicemapper、 btrfs ; Aufs: AUFS (AnotherUnionFS) 是一种 Union FS, 简单来说就是支持将不同 目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)的文件系统。 Aufs driver 是 docker 最早支持的 driver, 但是 aufs 只是 linux 内核的一个补丁集而且不太可以会被合并加入到 linux 内核中。但是由于 aufs 是唯一一个 storage driver 可以实现容器间 共享可执行及可共享的运行库, 所以当你跑成千上百个拥有相同程序代 码或者运行库时时候,aufs 是个相当不错的选择。 Device Mapper: Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备 的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定 实现存储资源的管理策略(详 见:http://www.ibm.com/developerworks/cn/linux/l-devmapper/index.html) 。 Device mapper driver 会创建一个 100G 的简单文件包含你的镜像和容器。 每一个容器被限制在 10G 大小的卷内。(如果想要调整,参 考:http://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/ 。中文 译 文:http://zhumeng8337797.blog.163.com/blog/static/100768914201452405 120107/ ) 你可以在启动 docker daemon 时用参数-s 指定 driver: docker -d -s devicemapper ; Btrfs: Btufs driver 在 docker build 可以很高效。但是跟 devicemapper 一样不 支持设备间共享存储(文档里是 does not share executable memory between devices)。 下面笔者就已有的条件去分析下 docker 的镜像与容器的存储结构。 环境: opensuse 13.10 + Docker version 1.2.0, build fa7b24f Ubuntu 14.10 + Docker version 1.0.1, build 990021a 在没有 aufs 支持的 linux 发行版本上(CentOS,opensuse 等)安装 docker 可 能就使用了 devicemapper driver。 查看你的 linux 发行版有没有 aufs 支持:lsmod | grep aufs 笔者 opensuse 13.10 里是没有加载这个模块的: 而虚拟机里的 ubuntu 14.10 是加载了这个模块的: 而我们列出/var/lib/docker 这个目录的内容也可以看出你那个 docker 是 使用了哪个 storage driver: opensuse 13.10 上的/var/lib/docker 这里应该看出是使用了 device mapper 这个 driver ; 然后再来看看虚拟机 ubuntu 14.10 上/var/lib/docker 目录: 这里也可以看出笔者 ubuntu 里 docker 是使用了 aufs 这个 driver : 下文 就这两个不同的 driver 作对比。 请注意分析的是哪一个。 那么镜像文件是本地存放在哪里呢? 笔者在 opensuse 和 ubuntu 里把 docker 彻底重新安装了一遍删除了所有 镜像,并只 Pull 下来一个 ubuntu:14.10 的镜像,这样分析起来会比较简单 明了: 现在两个系统都只有一个 ubuntu:14.10 的镜像: opensuse: Ubuntu : 好了。首先现在我们来看看/var/lib/docker 里都是什么文件。 1、首先用 Python 的 json.tool 工具查看下 repositories-* 里的内容。 opensuse: 里面的 json 数据记录的正是本地上存放的镜像的名称及其 64 位长度的 ID.这个 ID 可以有其 12 位的简短模式。 Ubuntu 上也是一样的: 而且我们可以发现这两个 ID 是一样。这时我们其实可以猜想到:这个 ID 是全局性的,就是说你这个镜像在镜像仓库上的 ID 也是这个。被其 它机器上 ID 也是这个。这样的好处无疑是方便管理镜像。 2、/var/lib/docker/graph 目录里的内容: opensuse: Ubuntu: Graph 目录里有 7 个长 ID 命名的目录,其中第二个长 ID 是我们所 pull 下来的 ubuntu14.10 镜像的对应的长 ID..那么其它 6 个是怎么来的呢? 这里我们用 docker images -tree 列出镜像树形结构: 可以看到最下层的镜像是我们的 ubuntu14.10。那么上面对应的是6个 layer。就是说在这个树中第 n+1个层是基于第 n 个层上改动的。而第 个层在 graph 目录里都对应着一个长 ID 目录。 我们来看看虚拟机里 ubuntu14.10 里的 docker images -tree: 大小数量一致。但是到了最后一个层的大小不一样(这里原因可能会是 系统问题,也可能是 docker 版本问题。具体原因需要另外考察) 再分析一下各个层的大小,第一个为 0B, 第二个层就应该为 198.9MB, 第三个层大小为 0.2MB(199.1-198.9)…如此类推下去。 上层的 image 依赖下层的 image(注:这里的逻辑上层是上图树形结构的 下层),因此 docker 中把下层的 image 称作父 image,没有父 image 的 image 称作 base image ; 例如我要用这里的 ubuntu:14.10 为模板启动一个容器时,docker 会加载 树形结构中的最下层( 2185fd5…),然后加载其父层(f180ea…),这样一直 加载到第一层(511136…)才算加载这个 rootfs。那么一个层在哪里保存它 的父层信息呢?在下面长 ID 目录里的 json 文件其实也可以看到这个信 息。 graph 长 ID 目录内容:(对于 ubuntu 里是一样的,这里以 opensuse 为 例) 我们进入长 ID 目录里看看里面的内容: opensuse : 我们进入最后一个层长 ID 目录里。里面有一个 json 文件及一个名为 layersize 的文件。 用 cat 查看 layersize 里的内容,里面记录的数字是指 这个层的大小。这里(绿色前头)是 0。而我们从上面的目录树可以算出 最后一个层确实是 0。如果还不相信。我们再算算倒数第二个层的大小 (opensuse 里的树形图里短 id 为 f180ea115597 的层)应该为 37.8M。现在 进入对应长 ID 目录: 可以看到是是 37816084(B),约 37.8M,与我们计算的刚刚吻合。 而另一个文件 json 又是什么呢?用 python 工具看看:(内容有点多, 没有截完) 可以看到 json 这个文件保存的是这个镜像的元数据。 拉到底部可以看到有个 parent:的值: 这个就是保存了其父层长 ID 的值。对照树形结构看 f180ea115597 的父 层是不是 0f154c52e965 。 但是注意在 graph 这个目录里并没有找到我们想找到的镜像内容存放地。 只是一些镜像相关的信息数据。 镜像里的内容存放在哪里 opensuse : 在 opensuse 下的/var/lib/docker/devicemapper/devicemapper/这个目录下 找到两个文件,并列出其大小。 其中一个 data 的文件大小为 100G(非真实占用)。真实占用的情况如下: 100G 的只占用了 590M。 上面我们讲到:Device mapper driver 会创建一个 100G 的简单文件包含 你的镜像和容器。每一个容器被限制在 10G 大小的卷内。那么看来这个 100G 的简单文件正是这个名为 data 的文件,那么镜像和容器下是存放 在这里的。 好了。这时我在 opensuse 上再 pull 下一个 ubuntu:12.10 镜像看看这个 文件大小有什么变化: 这次一下子截了三个命令的信息: Pull 下来的 ubuntu 是 172.1M。树形结构可以看到各个层的关系。而 data 的大小变成了 787M. 没 pull ubuntu:12.10 之前是 590M.增加了 197M, 跟pull下来的172.1M有点差距。这里可认为是存储了额外的某些信息。 那么容器是不是也存放在这里呢? 我们用 ubuntu14.10 启动一个模板看看情况如何: 这次我也是一下子截了几个命令: 可以看到了一个基于 ubuntu:14.10 镜像的容器在运行中,简短 ID 是 a9b35d72fcd4, 第二个命令 du 列出了 data 的大小为 789M,增加了 2M。 第三个命令列出了 container 目录内出现一个长 ID 的目录,ID 就是运行 的容器的 ID。但是里面的文件应该都是些配置文件。并没有我们想要 的内容目录。 这样的话我们进一步做测试:在运行的容器内使用 dd if=/dev/zero of=test.txt bs=1M count=8000 创建一个8G 大小的文件后: 这里 data 变成了 8.6G,增长了接近 8G,这样也证实了容器里的内容是 保存在 data 这个简单文件内的。 这样的话证实了 devicemapper driver 是把镜像和容器的文件都存储在 data 这个文件内。 Ubuntu 的 aufs driver 又如何呢: Ubuntu 上由于是 aufs driver 所以/var/lib/docker 目录下有 aufs 目录而不 是 devicemapper 目录: 这里的 aufs 目录有三个目录,diff 、layers 、mnt 三个目录。 这里 layers 目录是保存了 layers 层次信息,并不是 layers 里面的内容。 而 diff 目录时有数个长 ID 目录: 列出这几个目录的大小可以看出基本与上面树形结构的所能计算的大 小相对应(相关部分可能是由于压缩或者其它原因造成,这里纯属猜测)。 那我们进入 f180ea115597 这个 ID 对应的目录看看里面是什么: 里面是一些文件夹,但是只有几个,并不像我们平时常规 linux 发行版 里的那么齐全。 这里的话其实我们可以想到了因为一个层是基于另一个层之上的。Aufs 文件系统可以做到增量修改,所以这里的几个文件夹是基于上一个层做 的修改内容增量地保存在这里,因为上一个层对于这个层来说不可写: 在这里我需要先引用一张网上的图片: 这里我们可以看到一个我们想象中的运行中的 container 是包含了若干 个 readonly 的 image 层,然后最上面的 writable 层才是我们可写的层。 第一个 readonly 的层会加载其父层。直到最下面的 base image 层。 我们所做的改动会被保存在最上面的那个 writable 层里。当我们用 commit 把容器固化成镜像时那个层就会变成我们上面看到的“目录不 齐全的”长 ID 目录。 为了证实这一点,我们在运行一个基于 ubuntu:14.10 镜像的容器: 可以看到运行的容器简短 ID 为 7b3c13323d8c 。 这时再列出 diff 目录的内容: 多了两个长 ID 目录,正是我们运行的容器的 ID,列出内容: 然后我们在运行的容器中创建一个/test 目录,并在里面用 dd 命令创建 一个 8G 的 test.txt 文件:完成这些后再列出这两个目录内容: 可以看到其中一个目录(没有 init 后缀)变成了 7.5G,而另一个目录还 是 24K。 在长 ID 目录里还多了一个 test 文件夹,正是我们在容器里创建的,这 样的话里面无疑问就是 test.txt 文件了。容器通过这种方法在 writable 层 里记录了修改过的内容(增量记录) (这里有个小问题笔者也还不清 楚:怎么记录删除了东西呢?这个问题以后再考察) 从上面我们可以知道容器的 writable 层是保存在以容器 ID 为名的长 ID 目录里的,而 ID+init 后缀目录是保存容器的初始信息的。 好了,现在我们进行最后一个实验:把容器固化成镜像。 (这里要做个小小调整。把上面 8G 的文件删除了再建一个 3G 大小的文 件 test_3G.txt 代替) Commit 后把容器固化成了 test_image 的镜像。得到那个镜像的长 ID。 现在看看变化: 那个窗口目录还在,原因是我们还没用 rm 命令删除那个容器。而多出 来的镜像目录正是我们固化所得到的,其大小与上面容器 writable 层大 小一致为 3GB。现在看看里面是什么内容: 里面有一个 test 目录,目录下对应我们创建的 3GB 大小的 test_3G.txt 文件。 这就是我们改动过的内容保存了在这个目录内。 现在我们用 rm 命令删除容器看看结果: 容器被删除了,其对应的长目录 ID 也被删除了。而那个固化的得到的 镜像( c7560af30 )被保存了下来。 通过上面的小实验基本可以看清 docker 在 devicemapping 和 aufs 这 两个 driver 的存储结构,但是这些目录是怎样灵活地在运行容器时被加 载到一起就需要读者去了解更深层的关于 aufs 及 devicemapping 相关的 知识。 http://www.programfish.com/blog/?p=9

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享文档获得金币 ] 0 人已下载

下载文档

相关文档