Docker存储技术浅析

Docker镜像由一些松耦合的只读镜像层组成,故要求镜像存储必须支持分层存储的特性。在Linux上支持每种镜像存储驱动都有本身的镜像分层、镜像层共享以及写时复制(CoW)技术的具体实现。本文只讨论Docker本地存储技术,不讨论存储卷。node

Docker存储基础技术

镜像分层

全部的Docker镜像都起始于一个基础镜像层,当进行修改或增长新的内容时,就会在当前镜像层之上,建立新的镜像层。默认Docker镜像由多个只读层镜像叠加而成,启动容器后,Docker会加载只读镜像层,并再顶部添加一个读写层,并经过写时复制的方式,来写读写层linux

镜像层共享

多个镜像之间能够而且确实会共享镜像层,这样能够有效节省空间 并提高性能。例如多个centos7的镜像能够共享centos7的基础image,由于基础image你们都同样docker

写时复制CoW

CoW就是copy-on-write,表示只在须要写时才去复制,这个是针对已有文件的修改场景。好比基于一个image启动多个Container,若是为每一个Container都去分配一个image同样的文件系统,那么将会占用大量的磁盘空间。而CoW技术可让全部的容器共享image的文件系统,全部数据都从image中读取,只有当要对文件进行写操做时,才从image里把要写的文件复制到本身的文件系统进行修改。因此不管有多少个容器共享同一个image,所作的写操做都是对从image中复制到本身的文件系统中的复本上进行,并不会修改image的源文件,且多个容器操做同一个文件,会在每一个容器的文件系统里生成一个复本,每一个容器修改的都是本身的复本,相互隔离,相互不影响。使用CoW能够有效的提升磁盘的利用率。ubuntu

本地存储驱动

每一个Docker容器都有本身的非持久化存储,即容器的本地存储,默认状况这是容器所有文件和文件系统保存的地方,故本地存储要知足镜像存储的技术特性。本地存储自动建立,从属于容器,生命周期与容器相同,这意味着删除容器也会删除所有非持久化数据。centos

devicemapper驱动

在linux2.6内核版本中并入内核,devicemapper将全部的镜像和容器存储在本身的虚拟块设备上,devicemapper工做在块层次上而不是文件层次上。在Docker 17.06以及更高的版本中能够配置direct-lvm 做为存储驱动,这种模式下经过使用基于裸块设备 (Raw Block Device)的LVM精简池(LVM thin pool)来获取更好的性能。app

overlay驱动

overlay
如上图所示,Overlay在主机上用到2个目录,这2个目录被当作是overlay的层。upperdir为容器层、lowerdir为镜像层使用联合挂载技术将它们挂载在同一目录(merged)下,提供统一视图。ide

overlay只使用2层,意味着多层镜像不会被实现为多个OverlayFS层。每一个镜像被实现为本身的目录,这个目录在路径/var/lib/docker/overlay下。硬连接被用来索引和低层共享的数据,节省了空间,可是会有inode占用过多的问题。性能

当建立一个容器时,overlay驱动链接表明镜像层顶层的目录(只读)和一个表明容器层的新目录(读写)。ui

overlay2驱动

docker的overlay2驱动须要在kernel 3.10.0-514以上和Docker 17.06以上版本支持。而centos/redhat7系统默认内核为3.10.*。overlay2能够直接形成muitiple lower layers(最大128层)不用像overlay同样要经过硬连接的方式共享数据。
在overlay2每层的内容都是不同的,diff是文件系统的统一挂载点,link文件描述的是该层的标识符,lower文件描述了层与层之间的组织关系,overlay2是将底层多个lowerdir和upperdir和workdir联合挂载,造成最终的merged挂载点。centos7

以下为ubuntu:18.04的镜像

/var/lib/docker/overlay2/
|-- 6e599f35a3366d95287390fc00183a80bfc9a34492aaf9694836ec7b5722d0b6
|   |-- diff
|   |-- link
|   |-- lower
|   |-- merged
|   `-- work
|-- 6f66846fe6a29834193bf36380eb1664947f21435edd8ce8fbc9b79dea92c51b
|   |-- diff
|   |-- link
|   |-- lower
|   |-- merged
|   `-- work
|-- a1869d02b056d66742372c4b6d31d64f98b477590cdd76a842a653424f46710b
|   |-- diff
|   |-- link
|   |-- lower
|   |-- merged
|   `-- work
|-- backingFsBlockDev
|-- da05539957d703ae81cf279c76059c947c96bd1f91fd24691b9e7630dcb13ff7
|   |-- diff
|   `-- link
`-- l
    |-- 2LYVTSJQWPVOB46BCA74GFHJ7T -> ../da05539957d703ae81cf279c76059c947c96bd1f91fd24691b9e7630dcb13ff7/diff
    |-- HEXAQW5O23XL6HHRVOMKWGAI4Z -> ../6f66846fe6a29834193bf36380eb1664947f21435edd8ce8fbc9b79dea92c51b/diff
    |-- KXIOHK4OI6PPPFZ56I3ZORNHS7 -> ../6e599f35a3366d95287390fc00183a80bfc9a34492aaf9694836ec7b5722d0b6/diff
    `-- PHQQ6RCEXR6BLZJGKBP6GYS55Q -> ../a1869d02b056d66742372c4b6d31d64f98b477590cdd76a842a653424f46710b/diff

overlay相比overlay2要更加占用inode

一、overlay只支持两层lowerdir和upperdir,而且只支持一个lowerdir,因此若是你的容器镜像有多层的话,层与层以前的数据共享是经过硬连接来实现的,咱们也知道硬连接自己是同一个inode但不一样的文件名而已,但为何仍是会大量消耗inode这里简单作的实验
咱们在一台配置了storage-driver的机器上PULL ubuntu:18.04镜像

[root@master dir]# docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
32802c0cfa4d: Pull complete 
da1315cffa03: Pull complete 
fa83472a3562: Pull complete 
f85999a86bef: Pull complete 
Digest: sha256:48eb09a5c832172357f172593ce5e98e027814f758f6aeaef59a7fd658e50d49
Status: Downloaded newer image for ubuntu:18.04

整个镜像是一个四层镜像层组成的镜像,在/var/lib/docker/overlay/下每层对应一个目录,经过tree命令能够看见每一个目录内只包含一个root文件夹

tree -L 2 /var/lib/docker/overlay/
/var/lib/docker/overlay/
|-- 0a9cd41c44a802a325bfc5bfdda757bced8eaf69a8d6004e5d6fcb730591ab31
|   `-- root
|-- 1f9c95c6642a98930a14d914ac9bcfe9535b5a604dc27851cd76266326c76ed7
|   `-- root
|-- 2d79f688e1bf505ef20100f7450ad7e5ea550500bd07a17b7b9b08513fc96988
|   `-- root
`-- e8b1fcddec600628a75964619da3d0de7310fcbd6350b0c07ddf038d71c25d8b
    `-- root
8 directories, 0 files

这个root目录内包含的是该层独有的文件和根lowdir共享的数据硬连接,这里看见共享的数据他们自己都是经过硬连接方式链接起来的。

[root@master overlay]# ls  -i /var/lib/docker/overlay/0a9cd41c44a802a325bfc5bfdda757bced8eaf69a8d6004e5d6fcb730591ab31/root/bin/ls
70832997 /var/lib/docker/overlay/0a9cd41c44a802a325bfc5bfdda757bced8eaf69a8d6004e5d6fcb730591ab31/root/bin/ls
[root@master overlay]# ls  -i /var/lib/docker/overlay/1f9c95c6642a98930a14d914ac9bcfe9535b5a604dc27851cd76266326c76ed7/root/bin/ls
70832997 /var/lib/docker/overlay/1f9c95c6642a98930a14d914ac9bcfe9535b5a604dc27851cd76266326c76ed7/root/bin/ls
[root@master overlay]# ls  -i /var/lib/docker/overlay/2d79f688e1bf505ef20100f7450ad7e5ea550500bd07a17b7b9b08513fc96988/root/bin/ls
70832997 /var/lib/docker/overlay/2d79f688e1bf505ef20100f7450ad7e5ea550500bd07a17b7b9b08513fc96988/root/bin/ls
[root@master overlay]# ls  -i /var/lib/docker/overlay/e8b1fcddec600628a75964619da3d0de7310fcbd6350b0c07ddf038d71c25d8b/root/bin/ls
70832997 /var/lib/docker/overlay/e8b1fcddec600628a75964619da3d0de7310fcbd6350b0c07ddf038d71c25d8b/root/bin/ls

但为何overlay仍是会占用大量inode呢?根本缘由在于那些文件夹,每层的root目录内存放的都是完整的rootfs文件夹,但它们都是新建出来
的,它们inode都不同,因此在overlay下一个容器镜像层数越多,占用的inode就越多
1

四,参考资料
https://docs.docker.com/storage/storagedriver/overlayfs-driver/
https://docs.docker.com/storage/storagedriver/select-storage-driver/#supported-backing-filesystems
https://www.bladewan.com/2018/01/25/docker_storage_driver/

相关文章
相关标签/搜索