Docker 镜像之存储管理

前端之家收集整理的这篇文章主要介绍了Docker 镜像之存储管理前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

》中介绍了镜像分层、写时复制以及内容寻址存储(content-addressable storage)等技术特性,为了支持这些特性,docker 设计了一套镜像元数据管理机制来管理镜像元数据。另外,为了能够让 docker 容器适应不同平台不同应用场景对存储的要求,docker 提供了各种基于不同文件系统实现的存储驱动来管理实际镜像文件

文件。

文件的存储完全隔离开了。Docker 在管理镜像层元数据时采用的是从上至下 repository、image 和 layer 三个层次。由于 docker 以分层的形式存储镜像,所以 repository 和 image 这两类元数据并没有物理上的镜像文件与之对应,而 layer 这种元数据则存在物理上的镜像层文件与之对应。接下来我们就介绍这些元数据的管理与存储。

功能的 docker 镜像的所有迭代版本构成的镜像库。Repository 在本地的持久化文件存放于 /var/lib/docker/image//repositories.json 中,下图显示了 docker 使用 aufs 存储驱动时 repositories.json 文件的路径:

内容,并通过命令 :%!python -m json.tool 进行格式化:

文件中存储了所有本地镜像的 repository 的名字,比如 ubuntu ,还有每个 repository 下的镜像的名字、标签及其对应的镜像 ID。当前 docker 默认采用 SHA256 算法根据镜像元数据配置文件计算出镜像 ID。上图中的两条记录本质上是一样的,第二条记录和第一条记录指向同一个镜像 ID。其中 sha256:c8c275751219dadad8fa56b3ac41ca6cb22219ff117ca98fe82b42f24e1ba64e 被称为镜像的摘要,在拉取镜像时可以看到它:

摘要(Digest)是对镜像的 manifest 内容计算 sha256sum 得到的。我们也可以直接指定一个镜像的摘要进行 pull 操作:摘要来对应 ubuntu:latest)。

包括了镜像架构(如 amd64)、操作系统(如 linux)、镜像默认配置、构建该镜像的容器 ID 和配置、创建时间、创建该镜像的 docker 版本、构建镜像的历史信息以及 rootfs 组成。其中构建镜像的历史信息和 rootfs 组成部分除了具有描述镜像的作用外,还将镜像和构成该镜像的镜像层关联了起来。Docker 会根据历史信息和 rootfs 中的 diff_ids 计算出构成该镜像的镜像层的存储索引 chainID,这也是 docker 1.10 镜像存储中基于内容寻址的核心技术。文件 /var/lib/docker/image//imagedb/content/sha256/ 中。

452a96d81c30a1e426bc250428263ac9ca3f47c9bf086f876d11cb39cf57aeec 就是镜像的ID。其内容如下(简洁起见,省略中间大部分的内容):

内容寻址的索引(chainID) 来获取 layer 相关信息,进而获取每一个镜像层的文件内容。注意,每个 diff_id 对应一个镜像层。上面的 diff_id 的排列也是有顺序的,从上到下依次表示镜像层的最低层到最顶层:

文件包。用户在 docker 宿主机上下载了某个镜像层之后,docker 会在宿主机上基于镜像层文件包和 image 元数据构建本地的 layer 元数据,包括 diff、parent、size 等。而当 docker 将在宿主机上产生的新的镜像层上传到 registry 时,与新镜像层相关的宿主机上的元数据也不会与镜像层一块打包上传内容主要有索引该镜像层的 chainID、该镜像层的校验码 diffID、父镜像层 parent、graphdriver 存储当前镜像层文件的 cacheID、该镜像层的 size 等内容。这些元数据被保存在 /var/lib/docker/image//layerdb/sha256// 文件夹下。/layerdb/sha256/ 目录下的目录名称都是镜像层的存储索引 chainID:

内容为:

文件的内容即 diffID,其内容就是 image 元数据中对应层的 diff_id)。chainID 和父镜像层 parent 需要从所属 image 元数据中计算得到。而 cacheID 是在当前 docker 宿主机上随机生成的一个 uuid,在当前的宿主机上,cacheID 与该镜像层一一对应,用于标识并索引 graphdriver 中的镜像层文件

属性中,diffID 采用 SHA256 算法,基于镜像层文件包的内容计算得到。而 chainID 是基于内容存储的索引,它是根据当前层与所有祖先镜像层 diffID 计算出来的,具体算如下:

  • 加上一个空格和当前层的 diffID,再计算 SHA256 校验码。

内容主要为索引某个容器的可读写层(也叫容器层)的 ID(也对应容器层的 ID)、容器 init 层在 graphdriver 中的ID(initID)、读写层在 graphdriver 中的 ID(mountID) 以及容器层的父层镜像的 chainID(parent)。相关文件位于 /var/lib/docker/image//layerdb/mounts// 目录下。/layerdb/mounts// 目录下的内容

支持提供了针对某种文件系统的初始化操作以及对镜像层的增、删、改、查和差异比较等操作。目前存储系统的接口已经有 aufs、btrfs、devicemapper、voerlay2 等多种。在启动 docker deamon 时可以指定使用的存储驱动,当然指定的驱动必须被底层操作系统支持

支持联合挂载的文件系统。简单来说就是支持将不同目录挂载到同一个目录下,这些挂载操作对用户来说是透明的,用户在操作该目录时并不会觉得与其他目录有什么不同。这些目录的挂载是分层次的,通常来说最上层是可读写层,下面的层是只读层。所以,aufs 的每一层都是一个普通的文件系统。文件 A 时,会从最顶层的读写层开始向下寻找,本层没有,则根据层之间的关系到下一层开始找,直到找到第一个文件 A 并打开它。文件 A 时,如果这个文件不存在,则在读写层新建一个,否则像上面的过程一样从顶层开始查找,直到找到最近的文件 A,aufs 会把这个文件复制到读写层进行修改修改某个已有文件时,如果这个文件很大,即使只要修改几个字节,也会产生巨大的磁盘开销。删除一个文件时,如果这个文件仅仅存在于读写层中,则可以直接删除这个文件,否则就需要先删除它在读写层中的备份,再在读写层中创建一个 whiteout 文件来标志这个文件不存在,而不是真正删除底层的文件文件时,如果这个文件在读写层存在对应的 whiteout 文件,则先将 whiteout 文件删除再新建。否则直接在读写层新建即可。

文件在本地存放在哪里呢?内容:

$ /var/lib/docker/

包括只读层和可读写层,所有这些层最终一起被挂载在 mnt 下面的目录上,layers 下为与每层依赖有关的层描述文件

文件数据都在 diff 目录下。一个 docker 容器创建与启动的过程中,会在 /var/lib/docker/aufs 下面新建出对应的文件和目录。由于 docker 镜像管理部分与存储驱动在设计上完全分离了,。在 Linux 环境下,mountID 是随机生成的并保存在 mountedLayer 的元数据 mountID 中,持久化在 image/aufs/layserdb/mounts//mount-id 中。下面以 mountID 为例,介绍创建一个新读写层的步骤:文件夹。文件,用来记录该层所依赖的所有的其它层。文件里。然后 GraphDriver 还需要在 layers 目录下读取与上述 parent 同 mountID 的文件,将 parent 层的所有依赖层也复制到这个新创建层对应的层描述文件中,这样这个文件才记录了该层的所有依赖。创建成功后,这个新创建的层描述文件如下:

生成一个以当前容器对应的 -init 命名的文件夹作为最后一层只读层,这个文件夹用于挂载并重新生成如下代码段所列的文件文件与这个容器内的环境息息相关,但并不适合被打包作为镜像的文件内容(毕竟文件里的内容是属于这个容器特有的),同时这些内容又不应该直接修改在宿主机文件上,所以 docker 容器文件存储中设计了 mountID-init 这么一层单独处理这些文件。这一层只在容器启动时添加,并会根据系统环境和用户配置自动生成具体的内容(如 DNS配置等),只有当这些文件在运行过程中被改动后并且 docker commit 了才会持久化这些变化,否则保存镜像时不会包含这一层的内容文件系统有 3 层:可读写层、init 层和只读层。但是这并不影响我们传统认识上可读写层 + 只读层组成的容器文件系统:因为 init 层对于用户来说是完全透明的。生成一个以容器对应 mountID 为名的可读写目录,也挂载到 mnt 目录下。所以,将来用户在容器中新建文件就会出现在 mnt 下一 mountID 为名的目录下,而该层对应的实际内容则保存在 diff 目录下。文件的实际内容均保存在 diff 目录下,包括可读写层也会以 mountID 为名出现在 diff 目录下,最终会整合到一起联合挂载到 mnt 目录下以 mountID 为名的文件夹下。接下来我们统一观察 mnt 对应的 mountID 下的变化。

$ docker container create -it --name mycon ubuntu bash

文件的内容如下:

819e3e9a67f4440cecf29086c559a57a1024a078eeee42f48d5d3472e59a6c94

$ -h . --max-depth= | 819e

文件夹下是空的。

$ docker container start -i mycon

文件夹中可以看到挂载好的文件系统:

文件文件:

文件执行的操作。

$ docker container stop mycon

文件都还存在。

生成一个新的 cacheID 命名的文件夹,它存放了最新的差异变化文件,这时一个新的镜像层就诞生了。而原来的以 mountID 为名的文件夹会继续存在,直至对应容器被删除

文件存储。由于 docker 镜像管理部分与存储驱动在设计上的完全分离,使得这部分内容初看起来并不是那么直观。希望本文能对大家理解 docker 镜像及其存储有所帮助。

猜你在找的Docker相关文章