大家好,我是张锦涛。上周我交流群里有朋友问到Overlay2,所以在这篇文章中介绍一下。本节为大家介绍Docker目前推荐的存储驱动Overlay2。在开始之前,可以执行如下命令查看Docker正在使用的存储驱动:(萌萌哒)?~dockerinfo--format'{{.Driver}}'overlay2如果看到的结果也是overlay2,说明您的Docker已经在使用overlay2存储驱动程序。我个人工作站使用的是btrfs,因为btrfs从Fedora33开始就是Fedora的默认文件系统。但是服务器都是overlay2。您可能还会看到其他不同的结果,可以在启动docker守护进程时通过--storage-driver参数指定,或者通过/etc/docker/daemon.json文件中的storage-driver字段进行配置。目前,对于最新版本的Docker,你有以下几种存储驱动可供选择:overlay2fuse-overlayfsbtrfszfsaufsoverlaydevicemappervfs但它们对你使用的文件系统的要求不同,实现方式也不同。我将以本节重点介绍的overlay2存储驱动程序为例。它需要你使用Linux4.x以上的内核版本,或者RHEL/CentOS的3.10.0-514以上的内核(旧版本存在一些兼容性问题,我在上一篇文章中提到过)。同时支持你使用ext4文件系统,或者添加ftype=1的xfs文件系统。文件系统的相关信息可以通过dockerinfo获取。#省略了一些输出(MoeLove)?~dockerinfoStorageDriver:overlay2BackingFilesystem:extfsSupportsd_type:trueNativeOverlayDiff:true存储驱动的作用虽然我们已经讲过如何设置和查看当前使用的存储driver,但是还没有介绍为什么一定要用storagedriver,它有什么作用。还记得我在上篇文章《万字长文:彻底搞懂容器镜像构建》介绍过的Docker是如何存储镜像相关内容的吗?如果忘记了,可以回去复习。Docker将容器镜像分层存储,每一层都包含一个Dockerfile指令。这些层在磁盘上的存储方式,以及在启动容器时如何组织这些层并提供一个可写层,是存储驱动的主要功能。另外需要注意的是,不同的存储驱动有不同的实现和不同的性能,使用不同的存储驱动也会造成不同的磁盘空间占用。同时:由于它们的实现不同,当你修改存储驱动时,可能看不到原来的镜像、容器等,这是正常的,不用担心,切换回来后就可以看到了原来的司机。OverlayFS了解了前面的背景知识后,你也可以看到我刚才列出的存储驱动有两个可用,overlay和overlay2。其实overlay2是overlay的升级版,这两个存储驱动使用的是OverlayFS。overlay驱动于2014年8月首次进入Docker,而overlay2于2016年6月合并并首次出现在Docker1.12中。它的创建是为了解决覆盖存储驱动程序可能用完早期inode的问题。在简单介绍完overlay和overlay2之后,我们将把注意力放回到OverlayFS上。让我们启动一个容器,并以它为切入点来了解OverlayFS。注:以下内容使用Linux5.4内核和Docker20.10.21,不同环境下结果可能略有不同。#检查没有正在运行的容器和覆盖挂载(MoeLove)?~mount|grepoverlay(MoeLove)?~dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES#启动容器(MoeLove)?~dockerrun--rm-dalpinesleep99999caa9517ce0d799602735a30aaaaf123c07e07ff6e44c5a4b07e776af85780abe(MoeLove)?~dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMEScaa9517ce0d7alpine"sleep99999"23secondsagoUp22secondshopeful_dubinsky#检查overlay挂载(MoeLove)?~mount|grepoverlayoverlayon/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/合并类型覆盖(rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/5OO3RLRXHJPEH3IFEXNCTO4PY5:/overlay/UZVA/ZldockerTN2BNTKCZ7T6HUWU,upperdir=/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/diff,workdir=/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/work)可以看到,在启动容器后,系统上多了一个OverlayFS(overlay)的挂载注意看其中的几个内容:挂载点在:/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/merged(MoeLove)?~sudols/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/mergedbindevetchomeThecontentoflibmediamntopt??procrootrunsbinsrvsystmpusrvarlooksveryfamiliar,itisthecontentintherootdirectoryofthecontainerwestarted.为了验证这个说法,我在容器中写了一个新的文件:(MoeLove)?~dockerexec-it$(dockerps-ql)sh/#echo'HelloDocker'>moelove-info再次查看这个挂载点的内容:(MoElove)?lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/merged/moelove-infoHelloDocker可以看到刚才写入的内容已经在这个挂载点的目录下了。lowerdir:这是我们挂载时指定的目录。这个lowerdir包含两个目录,它们使用内核对OverlayFS多层特性的支持。分别查看一下内容:(萌爱)?~sudols-a/var/lib/docker/overlay2/l/5OO3RLRXHJPEH3IFEXNCTO4PY5...dev.dockerenv等(MoeLove)?~sudols-a/var/lib/docker/overlay2/l/UVA7IR67ZZTN2BNTKCZ7T6HUWU。..bindevetchomelibmediamntoptprocrootrunsbinsrvsystmpusrvarboth目录,是不是很眼熟?是的,这就是我们启动的容器根目录中的大部分内容。为什么说大部分内容呢?当我们看内容的时候,你也会发现他们的内容并不完整。比如我们刚刚写的moelove-info文件,或者我们查??看etc目录下的文件,你也会发现它只是常规系统/etc目录下的一部分内容。(MoeLove)?~sudols/var/lib/docker/overlay2/l/5OO3RLRXHJPEH3IFEXNCTO4PY5/etchostnamehostsmtabresolv.conf(MoeLove)?~sudols/var/lib/docker/overlay2/l/UVA7IR67ZZTN2BNTKCZ7T6HUWU-etcalpinereleasefstabinit.dmodprobe.dmtabpasswdprotocolsshellsudhcpd.confapkgroupinittabmodulesnetworkperiodicsecuritysslconf.dhostnameissuemodules-load.doptprofileservicessysctl.confsyscrontabshostslogrotate.dmotdos-releaseprofile.dupperdir是另一个重要的目录,我们看看里面的内容(MoeLove)?~sudols-a/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/diff..这个目录包含moelove-info根目录下你刚刚创建的moelove-info文件。同时,它还包含一个根目录,也就是我们默认使用的root用户的家目录。如果你去查看内容,你还会发现我们刚才执行的命令的历史记录。workdir目录和upperdir在同一个父目录中。查看其内容,发现只有一个工作目录(MoeLove)?~sudols-a/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/work。..work看完上面的介绍,你一定发现了它们之间的一些联系。在此之前,我们在看一个额外的目录,它是upperdir、workdir和挂载点的共同父目录:(萌萌哒)?~sudols/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09dbdifflinklowermergedwork你会发现我们刚刚才看过其中的diff,merged和work目录的内容了,现在看下中的内容吧:(MoeLove)?~sudocat/var/lib/docker/overlay2/f4356a8f14342008fc298bf3d313b863d10f30ef447a3b2f51ea9ece0dec09db/lowerl/5OO3RLRXHJPEH3IFEXNCTO4PY5:l/UVA7IR67ZZTN2BNTKCZ7T6HUWU我们发现lower文件的内容是两个用:.至此,我们可以得出以下结论:lower为基础层,可以包含多个lowerdir;diff为可写层,即挂载时的upperdir,容器内所有变化的文件都存放在这一层;merged是最终的merge结果,也就是容器呈现给我们的结果;Overlay2介绍了Docker启动容器后挂载的OverlayFS,Overlay2的工作流程想必大家已经很清楚了。将镜像的每一层作为底层基础层,同时添加diff的可写层。通过OverlayFS的工作机制,最终将合并后的文件作为容器中的一个文件目录展示给用户。大家可能会有疑问,如果只是这么一个简单的组织,会不会有什么限制?答案是肯定的,当然有限制,我们可以看看Overlay2的代码//daemon/graphdriver/overlay2/overlay.go#L442func(d*Driver)getLower(parentstring)(string,error){//省略部分Contentiflen(lowers)>maxDepth{return"",errors.New("maxdepthexceeded")}}可以看到它对lower的深度有一个硬编码限制,而当前的硬编码limit是128,如果你在使用过程中遇到这个错误,说明你已经超过了最大深度限制,你需要想办法降低level。作为本节的总结,我为您介绍了OverlayFS和Overlay2存储驱动程序。通过实际启动容器生成的相关目录来介绍overlay2的工作流程。这样理解一定更容易理解。欢迎订阅我的文章公众号【MoeLove】
