原文:https://blog.csdn.net/qq_29999343/article/details/78274033 

一、镜像、容器和存储驱动的关系
    前面也已经讲过说,镜像是程序和文件的集合,容器是镜像的运行实例。

    Docker为了节约存储空间共享数据会对镜像和容器进行分层,不同镜像可以共享相同数据,并且在镜像上为容器分配一个RW层来加快容器的启动顺序

1. 镜像层和容器层

    每个镜像都由多个镜像层组成,从下往上以栈的方式组合在一起形成容器的根文件系统,Docker的存储驱动用于管理这些镜像层,对外提供单一的文件系统

    当容器启动时,Docker就会创建thin类型的可读写容器层,使用预分配存储空间,也就是开始时并不分配存储空间,当需要新建文件或修改文件时,才从存储池中分配一部分存储空间

    每个容器运行时都有自己的容器层,保存容器运行相关数据(所有文件变化数据),因为镜像层是只读的,所以多个容器可以共享同一个镜像

    删除容器时,Docker Daemon会删除容器层,保留镜像层

2. 镜像存储方式

    为了区别镜像层,Docker为每个镜像层都计算了UUID,在Docker 1.10之前根据镜像层中的数据产生一个随机码作为UUID;Docker 1.10版本中则是根据镜像层中的数据使用加密哈希算法生成UUID,这种方式避免了UUID冲突并且也保证了在pull、push、load等操作中的镜像数据完整性,而容器层仍然是采用随机数方式生成UUID

    在Docker 1.10版本前,镜像之间不能共享镜像层,而在Docker 1.10版本中,只要镜像层的数据相同就可以在不同镜像之间共享。当Docker升级到1.10版本后,宿主机上可能存在以前下载的镜像仍使用旧的存储方式,当Docker 1.10版本的Docker Daemon第一次启动时会自动迁移镜像,使用加密哈希算法重新计算每个镜像层的UUID,当重新计算UUID会花费很长时间并且迁移过程中Docker Daemon不能响应其他Docker命令,在实际中并不推荐

3. 写时复制策略(Copy On Write)

    Docker中的存储驱动用于管理镜像层和容器层,不同的存储驱动采用不同的算法和管理方式,在管理中使用的两大技术是栈层式管理和写时复制

    写时复制采用了共享和复制技术,针对相同的数据系统只保留一份,所有操作都访问这一份数据。当有操作需要修改或添加数据时,操作系统会把这部分数据复制到新的地方再进行修改或添加,而其他操作仍然访问原数据区数据,通过这项技术节约了镜像的存储空间,加快了系统启动时间

    所有镜像层和容器层都保存在宿主机的文件系统/var/lib/docker/中,由存储驱动进行管理

    在拉取ubutnu的时候可以看到有如下输出,表明该版本Ubuntu由五个镜像层组成,下载镜像时实际上就是下载了这五个镜像层,这些镜像层都在宿主机的文件系统中,每层都有独立的目录,在Docker 1.10之前的版本中,目录的名字和镜像的UUID相同,而Docker 1.10后则采用了新的存储方式

    可以看到目录名和下载镜像的UUID并不相同

    尽管存储方式不尽相同,但在所有版本的Docker中都可以共享镜像层。在下载镜像时,Docker Daemon会检查景象中的镜像层与宿主机文件系统中的镜像层进行对比,如果存在则不下载,只下载不存在的镜像层

    通过Dockfile创建了一个Changed-ubuntu镜像,通过docker history可以查看镜像的镜像层及镜像大小,可以看到新建的镜像层占用空间为0B,也就是说changed-ubuntu镜像只会占用额外的一丢丢空间而不需要为其他的五个镜像层分配存储空间

    由此可以看出,Docker通过共享技术,非常节约存储空间

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐