一、镜像、容器和存储驱动的关系
前面也已经讲过说,镜像是程序和文件的集合,容器是镜像的运行实例。
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命令,在实际中并不推荐
可以通过Docker 官方的镜像升级工具完成这项工作,这样升级Docker后,Docker Daemon看可以立即响应Docker命令
过程描述:
下载迁移工具的镜像 docker pull docker/v1.10-migrator
启动镜像 docker run –rm -v /var/lib/docker:/var/lib/docker docker/v1.10-migrator
提示:在启动镜像时,需要把宿主机存放镜像的目录挂载到容器中即/var/lib/docker
3. 写时复制策略(Copy On Write)
Docker中的存储驱动用于管理镜像层和容器层,不同的存储驱动采用不同的算法和管理方式,在管理中使用的两大技术是栈层式管理和写时复制
写时复制采用了共享和复制技术,针对相同的数据系统只保留一份,所有操作都访问这一份数据。当有操作需要修改或添加数据时,操作系统会把这部分数据复制到新的地方再进行修改或添加,而其他操作仍然访问原数据区数据,通过这项技术节约了镜像的存储空间,加快了系统启动时间
(1)使用共享技术减少镜像存储空间
所有镜像层和容器层都保存在宿主机的文件系统/var/lib/docker/中,由存储驱动进行管理
在拉取ubutnu的时候可以看到有如下输出,表明该版本Ubuntu由五个镜像层组成,下载镜像时实际上就是下载了这五个镜像层,这些镜像层都在宿主机的文件系统中,每层都有独立的目录,在Docker 1.10之前的版本中,目录的名字和镜像的UUID相同,而Docker 1.10后则采用了新的存储方式
可以看到目录名和下载