本文由作者邹珍珍授权网易云社区发布。
本文主要介绍Docker的存储管理。Docker拥有镜像分层,写时复制机制以及内容寻址存储等特征,为了支持这些特征,Docker设计了一套镜像元数据管理机制来管理镜像元数据。另外,为了能够让Docker容器适应不同平台不同应用场景对存储的要求,Docker提供了各种基于不同文件系统实现的存储驱动来管理实际的镜像文件。下面就详细介绍镜像元数据管理和存储驱动。
另外,本文最后简单介绍了Docker数据卷以及数据卷的基本操作。
一、Docker镜像元数据管理
Docker镜像在设计上将镜像元数据和镜像文件的存储完全分隔。在管理镜像层元数据时,采用repository,image,layer三层次。Docker分层存储镜像,repository和image这两类元数据时没有物理上的镜像文件与之对应,只有layer这种元数据时有的。(通俗讲,我们拉取的镜像就是分层存的镜像文件,每个镜像层可以是不同的文件系统。前面的三类元数据就是些镜像的信息和镜像层之间的关系说明。)
1. Repository元数据
在本地的持久化文件存放在/var/lib/docker/image/{graphdriver} /repositories.json
*注意:显示了所有repository的镜像名,每个repository下的版本镜像名字:tag以及镜像ID(采用SHA256算法)。上图可见,我的云主机上graphdriver是devicemapper,只有三个镜像。本机上的graphdriver存储驱动可以在/var/lib/docker/下查看
2. Image元数据
在本地的持久化文件存放在/var/lib/docker/image/{graphdriver} /imagedb/content/sha256/{imageid}
*注意:刚刚在Repository的文件中只看到了三个镜像,但在这里确看到了六个,这是因为前面的zzz/Ubuntu:1.0是我用dockerfile自己构建的镜像,在构建镜像的过程中的形成的中间层镜像也会被保存下来。使用docker run这个中间层镜像id是可以运行一个容器,从而查看每一步构建镜像后的实际状态,提供调试构建过程的能力。
另外,image元数据中包括了镜像架构,操作系统,默认配置,构建该镜像的容器ID以及rootfs等等。内容较多就不截图了。
3. Layer元数据
只读层的存储路径/var/lib/docker/image/{graphdriver} /layerdb/sha256/{chainID}
*注意:理所当然layer的元数据比image多,因为一个image对应多个镜像层layer。上图是只读层,每个只读层主要存索引该镜像层的chainID、该镜像层的校验码diffID,父镜像层parent,已经存储驱动存储该镜像层文件的cacheID,镜像层大小size。
可读写层(容器层)的存储路径/var/lib/docker/image/{graphdriver} /layerdb/mounts/{containerid}
二、存储驱动GraphDriver
Docker引擎1.12版本中,GraphDriver有以下几种:vfs、aufs、overlay、overlay2、btrfs、zfs、devicemapper和windows
A.GraphDriver必须执行什么操作?也就是它的功能是什么?
GraphDriver主要定义了ProtoDriver和Driver接口。主要实现功能(总共有12个方法,以下没有全部介绍):
(1)提供(计算)差别和改动相关的方法:都是比较指定镜像层与它的父镜像的差异,根据不同的方法返回不同的信息,比如Diff()将改动的文件打包;Chnages()是返回差异列表等等。
(2)Create( )创建镜像层:镜像包含多个分层(layer),这些分层有存在父级子级的关系。利用graphdriver驱动,采用最适合该文件系统实现中类似联合和写时复制(union+CoW-like)的叠层技术,来保存这些分层和它们之间的关系。要处理这些分层镜像的创建和解开(un-tar)操作,以及将镜像解开的内容放到创建的位置,会用到graphdriver的Create和ApplyDiff接口;
(3)remove( )删除镜像层:当镜像从本地缓存删除的时候需要执行相反的操作,“分层仓库”会调用graphdriver的Remove接口来将分层的内容从系统中删除;
(4)Get( )返回指定ID层的挂载点的绝对路径:容器需要运行时,在容器启动之前这些必须被组装成可运行的根文件系统。graphdriver的Get方法会被调用并带上一个特定的标识符,此时根据graphdriver特定文件系统的实现,需要根据父级连接关系遍历并且使用该文件系统提供的相应技术来将分层堆叠成一个单独的挂载点,并创建可写的上层或者顶部分层来满足容器更改文件系统的需要。
(5)put( )释放一个层使用的资源:告知graphdriver,某挂载的资源没有用了,并在绝大多数的场景下卸载相关的层。
B. 为什么一个Image运行多个Container?以AUFS存储驱动为例进行说明。
AUFS核心概念:将多个目录合并成一个虚拟文件系统,成员目录称为虚拟文件系统的一个分支(branch)。
*注意:上图把 /tmp, /var, /opt三个目录联合挂着到 /aufs目录下,则 /aufs目录可见 /tmp, /var, /opt目录下的所有文件。而每个成员目录,则称为虚拟文件系统的一个branch。
每个branch可以指定 readwrite/whiteout‐able/readonly权限,只读(ro),读写(rw),写隐藏(wo)。一般情况下,aufs只有最上层的branch具有读写权限,其余branch均为只读权限。
Docker镜像(Image)是由一个或多个AUFS branch组成,并且所有的branch均为只读权限。简单来说,AUFS所有robranch按照一定顺序堆积构成Docker Image镜像。
在运行容器的时候,创建一个AUFS branch位于image层之上,具有rw权限,并把这些branch联合挂载到一个挂载点下。这就是Docker能够一个镜像运行多个容器的原理所在。
*注意:Image有3个目录位于 /var/lib/docker/aufs/diff/文件夹内,当基于该Image创建容器时,创建一个容器运行目录,同样位于/var/lib/docker/aufs/diff/目录下,并使用aufs联合挂载Image目录和Container目录到 /var/lib/docker/aufs/mnt/目录下。创建多个容器时,只需创建多个容器运行目录,使用aufs把容器运行目录挂载在Image目录之上,即实现一个Image运行多个Container。
C.容器文件读写与删除如何实现?
当容器需要修改一个文件,而该文件位于低层branch时,顶层branch会直接复制低层branch的文件至顶层再进行修改,而低层的文件不变,这种方式即是CoW技术(写复制),AUFS默认支持Cow技术, 从而保证镜像层数据的完整性和复用性
当容器删除一个低层branch文件时,只是在顶层branch对该文件进行重命名并隐藏,实际并未删除文件,只是不可见,这种方式即AUFS的whiteout(写隐藏)。
-----------------------------------------------------------分割线-----------------------------------------------------------
Docker数据卷
在默认情况下,当用户退出容器而容器中又没有非守护进程在运行时,容器会进入关闭状态,同时,数据的修改会保留在最上层的可写文件系统内。当用户需要重新开启一个容器时,是无法访问原来所做的修改的,而是恢复到镜像的初始化状态。为了解决数据持久化的问题,Docker提供了数据卷。
关于数据卷的操作比较好理解,以下是关于数据卷一些基本操作的链接:
http://www.heblug.org/chinese_docker/userguide/dockervolumes.html
免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐
更多网易技术、产品、运营经验分享请访问网易云社区。
相关文章:
【推荐】 用双十一的故事串起碎片的网络协议(下)