共计 10677 个字符,预计需要花费 27 分钟才能阅读完成。
简介
OverlayFS 是一种和 AUFS 很类似的文件系统,与 AUFS 相比,OverlayFS 有以下特性:
1) 更简单地设计;
2) 从 3.18 开始,就进入了 Linux 内核主线;
3) 可能更快一些。
因此,OverlayFS 在 Docker 社区关注度提高很快,被很多人认为是 AUFS 的继承者。就像宣称的一样,OverlayFS 还很年轻。所以,在生成环境使用它时,还是需要更加当心。
Docker 的 overlay 存储驱动利用了很多 OverlayFS 特性来构建和管理镜像与容器的磁盘结构。
自从 Docker1.12 起,Docker 也支持 overlay2 存储驱动,相比于 overlay 来说,overlay2 在 inode 优化上更加高效。但 overlay2 驱动只兼容 Linux kernel4.0 以上的版本。
注意:自从 OverlayFS 加入 kernel 主线后,它在 kernel 模块中的名称就被从 overlayfs 改为 overlay 了。但是为了在本文中区别,我们使用 OverlayFS 代表整个文件系统,而 overlay/overlay2 表示 Docker 的存储驱动。
overlay 和 overlay2
OverlayFS(overlay)的镜像分层与共享
OverlayFS 使用两个目录,把一个目录置放于另一个之上,并且对外提供单个统一的视角。这两个目录通常被称作层,这个分层的技术被称作 union mount。术语上,下层的目录叫做 lowerdir,上层的叫做 upperdir。对外展示的统一视图称作 merged。
下图展示了 Docker 镜像和 Docker 容器是如何分层的。镜像层就是 lowerdir,容器层是 upperdir。暴露在外的统一视图就是所谓的 merged。
注意镜像层和容器层是如何处理相同的文件的:容器层(upperdir)的文件是显性的,会隐藏镜像层(lowerdir)相同文件的存在。容器映射(merged)显示出统一的视图。
overlay 驱动只能工作在两层之上。也就是说多层镜像不能用多层 OverlayFS 实现。替代的,每个镜像层在 /var/lib/docker/overlay 中用自己的目录来实现,使用硬链接这种有效利用空间的方法,来引用底层分享的数据。注意:Docker1.10 之后,镜像层 ID 和 /var/lib/docker 中的目录名不再一一对应。
创建一个容器,overlay 驱动联合镜像层和一个新目录给容器。镜像顶层是 overlay 中的只读 lowerdir,容器的新目录是可写的 upperdir。
overlay 中镜像和容器的磁盘结构
下面的 docker pull 命令展示了 Docker host 下载一个由 5 层组成的镜像。
$ sudo docker pull Ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
5ba4f30e5bea: Pull complete
9d7d19c9dc56: Pull complete
ac6ad7efd0f9: Pull complete
e7491a747824: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:46fb5d001b88ad904c5c732b086b596b92cfb4a4840a3abd0e35dbb6870585e4
Status: Downloaded newer image for ubuntu:latest
上图的输出结果显示 pull 了 5 个目录包含了 5 个镜像层,每一层在 /var/lib/docker/overlay/ 下都有自己的目录。还是再次提醒下,如你所见,Docker1.10 之后,镜像层和目录名不再对应。
$ ls -l /var/lib/docker/overlay/
total 20
drwx------ 3 root root 4096 Jun 20 16:11 38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8
drwx------ 3 root root 4096 Jun 20 16:11 55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
drwx------ 3 root root 4096 Jun 20 16:11 824c8a961a4f5e8fe4f4243dab57c5be798e7fd195f6d88ab06aea92ba931654
drwx------ 3 root root 4096 Jun 20 16:11 ad0fe55125ebf599da124da175174a4b8c1878afe6907bf7c78570341f308461
drwx------ 3 root root 4096 Jun 20 16:11 edab9b5e5bf73f2997524eebeac1de4cf9c8b904fa8ad3ec43b3504196aa3801
镜像层目录中,共享的数据使用的是硬链接,他们的 inode 号相同。这样做有效地利用了磁盘。
$ ls -i /var/lib/docker/overlay/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
19793696 /var/lib/docker/overlay/38f3ed2eac129654acef11c32670b534670c3a06e483fce313d72e3e0a15baa8/root/bin/ls
$ ls -i /var/lib/docker/overlay/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
19793696 /var/lib/docker/overlay/55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358/root/bin/ls
容器也在 /var/lib/docker/overlay/ 下。使用 ls - l 命令查看容器目录,会发现以下文件和目录。
$ ls -l /var/lib/docker/overlay/<directory-of-running-container>
total 16
-rw-r--r-- 1 root root 64 Jun 20 16:39 lower-id
drwxr-xr-x 1 root root 4096 Jun 20 16:39 merged
drwxr-xr-x 4 root root 4096 Jun 20 16:39 upper
drwx------ 3 root root 4096 Jun 20 16:39 work
这四个文件系统对象都是 OverlayFS 的组件。lower-id 文件包含了容器的镜像层最顶层的 ID。
$ cat /var/lib/docker/overlay/ec444863a55a9f1ca2df72223d459c5d940a721b2288ff86a3f27be28b53be6c/lower-id
55f1e14c361b90570df46371b20ce6d480c434981cbda5fd68c6ff61aa0a5358
upper 目录是容器的可读写层。任何对容器的改变都写在这个目录中。
merged 目录就是容器的 mount point,这就是暴露的镜像(lowerdir)和容器(upperdir)的统一视图。任何对容器的改变也影响这个目录。
work 目录是 OverlayFS 功能需要的,会被如 copy_up 之类的操作使用。
可以通过 mount 命令来核实上面的描述是否正确。
$ mount | grep overlay
overlay on /var/lib/docker/overlay/ec444863a55a.../merged
type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay/55f1e14c361b.../root,
upperdir=/var/lib/docker/overlay/ec444863a55a.../upper,
workdir=/var/lib/docker/overlay/ec444863a55a.../work)
OverlayFS(overlay2)的镜像分层与共享
overlay 驱动只工作在一个 lower OverlayFS 层之上,因此需要硬链接来实现多层镜像,但 overlay2 驱动原生地支持多层 lower OverlayFS 镜像(最多 128 层)。
因此 overlay2 驱动在合层相关的命令(如 build 和 commit)中提供了更好的性能,与 overlay 驱动对比,消耗了更少的 inode。
overlay2 中镜像和容器的磁盘结构
docker pull ubuntu 下载了包含 5 层的镜像,可以看到在 /var/lib/docker/overlay2 中,有 6 个目录。
$ ls -l /var/lib/docker/overlay2
total 24
drwx------ 5 root root 4096 Jun 20 07:36 223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
drwx------ 3 root root 4096 Jun 20 07:36 3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b
drwx------ 5 root root 4096 Jun 20 07:36 4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1
drwx------ 5 root root 4096 Jun 20 07:36 e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5
drwx------ 5 root root 4096 Jun 20 07:36 eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed
drwx------ 2 root root 4096 Jun 20 07:36 l
l 目录包含了很多软连接,使用短名称指向了其他层。短名称用于避免 mount 参数时达到页面大小的限制。
$ ls -l /var/lib/docker/overlay2/l
total 20
lrwxrwxrwx 1 root root 72 Jun 20 07:36 6Y5IM2XC7TSNIJZZFLJCS6I4I4 -> ../3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 B3WWEFKBG3PLLV737KZFIASSW7 -> ../4e9fa83caff3e8f4cc83693fa407a4a9fac9573deaf481506c102d484dd1e6a1/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 JEYMODZYFCZFYSDABYXD5MF6YO -> ../eca1e4e1694283e001f200a667bb3cb40853cf2d1b12c29feda7422fed78afed/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 NFYKDW6APBCCUCTOUSYDH4DXAT -> ../223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff
lrwxrwxrwx 1 root root 72 Jun 20 07:36 UL2MW33MSE3Q5VYIKBRN4ZAGQP -> ../e8876a226237217ec61c4baf238a32992291d059fdac95ed6303bdff3f59cff5/diff
在最低层中,有个 link 文件,包含了前面提到的这个层对应的短名称;还有个 diff 目录,包含了这个镜像的内容。
$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/
diff link
$ cat /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/link
6Y5IM2XC7TSNIJZZFLJCS6I4I4
$ ls /var/lib/docker/overlay2/3a36935c9df35472229c57f4a27105a136f5e4dbef0f87905b2e506e494e348b/diff
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
第二底层中,lower 文件指出了该层的组成。该目录还有 diff、merged 和 work 目录。
$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7
diff link lower merged work
$ cat /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/lower
l/6Y5IM2XC7TSNIJZZFLJCS6I4I4
$ ls /var/lib/docker/overlay2/223c2864175491657d238e2664251df13b63adb8d050924fd1bfcdb278b866f7/diff/
etc sbin usr var
运行容器包含的目录同样有着类似的文件和目录。注意在 lower 文件中,使用: 符号来分割不同的底层,并且顺序是从高层到底层。
$ ls -l /var/lib/docker/overlay/<directory-of-running-container>
$ cat /var/lib/docker/overlay/<directory-of-running-container>/lower
l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4
mount 的结果如下:
$ mount | grep overlay
overlay on /var/lib/docker/overlay2/9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/merged
type overlay (rw,relatime,
lowerdir=l/DJA75GUWHWG7EWICFYX54FIOVT:l/B3WWEFKBG3PLLV737KZFIASSW7:l/JEYMODZYFCZFYSDABYXD5MF6YO:l/UL2MW33MSE3Q5VYIKBRN4ZAGQP:l/NFYKDW6APBCCUCTOUSYDH4DXAT:l/6Y5IM2XC7TSNIJZZFLJCS6I4I4,
upperdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/diff,
workdir=9186877cdf386d0a3b016149cf30c208f326dca307529e646afce5b3f83f5304/work)
容器使用 overlay 读写
有三种场景,容器会通过 overlay 只读访问文件。
容器层不存在的文件 。如果容器只读打开一个文件,但该容器不在容器层(upperdir),就要从镜像层(lowerdir)中读取。这会引起很小的性能损耗。
只存在于容器层的文件 。如果容器只读权限打开一个文件,并且容器只存在于容器层(upperdir)而不是镜像层(lowerdir),那么直接从镜像层读取文件,无额外性能损耗。
文件同时存在于容器层和镜像层 。那么会读取容器层的文件,因为容器层(upperdir)隐藏了镜像层(lowerdir)的同名文件。因此,也没有额外的性能损耗。
有以下场景容器修改文件。
第一次写一个文件 。容器第一次写一个已经存在的文件,容器层不存在这个文件。overlay/overlay2 驱动执行 copy-up 操作,将文件从镜像层拷贝到容器层。然后容器修改容器层新拷贝的文件。
然而,OverlayFS 工作在文件级别而不是块级别。也就是说所有的 OverlayFS 的 copy-up 操作都会拷贝整个文件,即使文件非常大但却只修改了一小部分,这在容器写性能上有着显著的影响。不过,有两个方面值得注意:
▷ copy-up 操作只发生在第一次写文件时。后续的对同一个文件的写操作都是直接针对拷贝到容器层的那个新文件。
▷ OverlayFS 只工作在两层中。这比 AUFS 要在多层镜像中查找时性能要好。
删除文件和目录 。删除文件时,容器会在镜像层创建一个 whiteout 文件,而镜像层的文件并没有删除。但是,whiteout 文件会隐藏它。
容器中删除一个目录,容器层会创建一个不透明目录。这和 whiteout 文件隐藏镜像层的文件类似。
重命名目录。只有在源路径和目的路径都在顶层容器层时,才允许执行 rename 操作。否则,会返回 EXDEV。
因此,你的应用需要能够处理 EXDEV,并且回滚操作,执行替代的“拷贝和删除”策略。
在 Docker 中配置 overlay/overlay2 存储驱动
为了给 Docker 配置 overlay 存储驱动,你的 Docker host 必须运行在 Linux kernel3.18 版本之上,而且加载了 overlay 内核驱动。对于 overlay2 驱动,kernel 版本必须在 4.0 或以上。OverlayFS 可以运行在大多数 Linux 文件系统之上。不过,现在最建议在生产环境中使用 ext4。
下面的步骤讲述了如何在 Docker host 中配置使用 OverlayFS。
注意:在开始配置之前,如果你已经在使用 Docker daemon,并且有一些想保留的镜像,简易你 push 它们到 Docker hub 中。
1) 如果 Docker daemon 正在运行,需要先停止其运行。
$ systemctl stop docker.service
2) 检查 kernel 版本,确认 overlay 的内核模块是否加载。
$ uname -r
4.4.0-64-generic
$ lsmod | grep overlay
overlay
# 如果上面命令没有输出,说明驱动没有加载,可以如下操作
$ modprobe overlay
3) 使用 overlay/overlay2 存储驱动来启动 Docker daemon。
$ dockerd --storage-driver=overlay2 &
[1] 29403
root@ip-10-0-0-174:/home/ubuntu# INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)
INFO[0000] Option DefaultDriver: bridge
INFO[0000] Option DefaultNetwork: bridge
<output truncated>
此外,你还可以在 Docker 的配置文件中添加 –storage-driver=overlay 的标志到 DOCKER_OPTS 中,这样就可以持久化配置,不再需要启动 daemon 时手动指定 –storage-driver 标志了。比如,可以将配置持久化到配置文件 /etc/default/docker 中,将下面内容加入该文件中。
DOCKER_OPTS="--storage-driver=overlay2"
4) 检查 daemon 是否已经使用了 overlay/overlay2 存储驱动。
$ docker info
Containers: 0
Images: 0
Storage Driver: overlay2
Backing Filesystem: extfs
<output truncated>
注意输出结果显示后端文件系统使用的是 extfs。虽然支持多种文件系统,但是生产环境中还是建议使用 extfs(尤其 ext4)。
OverlayFS 和 Docker 性能
一般来说,overlay/overlay2 驱动更快一些,几乎肯定比 aufs 和 devicemapper 更快,在某些情况下,可能比 btrfs 也更快。即便如此,在使用 overlay/overlay2 存储驱动时,还是需要注意以下一些方面:
- Page Caching,页缓存。OverlayFS 支持页缓存共享,也就是说如果多个容器访问同一个文件,可以共享一个或多个页缓存选项。这使得 overlay/overlay2 驱动高效地利用了内存,是 PaaS 平台或者其他高密度场景的一个很好地选项。
- copy_up。和 AuFS 一样,在容器第一次修改文件时,OverlayFS 都需要执行 copy-up 操作,这会给写操作带来一些延迟——尤其这个要拷贝的文件很大时。不过,一旦文件已经执行了这个向上拷贝的操作后,所有后续对这个文件的操作都只针对这份容器层的新拷贝而已。
OverlayFS 的 copy_up 操作比 AuFS 的 copy_up 操作要快。因为 AUFS 支持比 OverlayFS 支持更多的层数,如果需要在多层查找文件时,就可能导致比较大的延迟。 - Inode limits。使用 overlay 存储驱动可能导致过多的 inode 消耗,尤其是 Docker host 上镜像和容器的数目增长时。大量的镜像,或者很多容器启停,,会迅速消耗掉该 Docker host 上的 inode。overlay2 存储驱动不存在这个问题。
不幸的是,只能在文件系统创建时指定 inode 的个数。因此,可以考虑将 /var/lib/docker 放在一个单独的设备文件系统中,或者在创建文件系统时指定 inode 个数。
以下一些方法可以提供 OverlayFS 的性能。 - 使用 Solid State Devices(SSD)。
-
使用数据卷。数据卷能提高更好的性能,因为其绕过存储驱动,不会引起超配、copy-on-write 可能会导致的隐患。
OverlayFS 兼容性
有以下两点 OverlayFS 和其他文件系统不太兼容:
- open(2)。OverlayFS 支持吃 POSIX 标准的一个子集。以 copy-up 操作为例,加入你的应用调用了 fd1=open(“foo”, O_RDONLY) 和 fd2=open(“foo”, O_RDWR),你的应用期望 fd1 和 fd2 指向同一个文件,然而,因为 copy-up 操作,会导致指向不同的文件。
-
rename(2),这个和前面提到 AuFS 一致。
小结
overlay/overlay2 存储驱动已经成为了 Docker 存储驱动的首选,并且性能优于 AuFS 和 devicemapper。不过,他们也带来了一些与其他文件系统的不兼容性,如对 open 和 rename 操作的支持。另外,overlay 与 overlay2 相比,overlay2 支持了多层镜像,优化了 inode 使用。然而,使用这两种驱动时,需要注意你的 Docker host 的 kernel 版本。
更多 Docker 相关教程见以下内容:
Docker 安装应用(CentOS 6.5_x64) http://www.linuxidc.com/Linux/2014-07/104595.htm
Ubuntu 14.04 安装 Docker http://www.linuxidc.com/linux/2014-08/105656.htm
Ubuntu 使用 VNC 运行基于 Docker 的桌面系统 http://www.linuxidc.com/Linux/2015-08/121170.htm
阿里云 CentOS 6.5 模板上安装 Docker http://www.linuxidc.com/Linux/2014-11/109107.htm
Ubuntu 15.04 下安装 Docker http://www.linuxidc.com/Linux/2015-07/120444.htm
在 Ubuntu Trusty 14.04 (LTS) (64-bit)安装 Docker http://www.linuxidc.com/Linux/2014-10/108184.htm
在 Ubuntu 15.04 上如何安装 Docker 及基本用法 http://www.linuxidc.com/Linux/2015-09/122885.htm
Ubuntu 16.04 上 Docker 使用手记 http://www.linuxidc.com/Linux/2016-12/138490.htm
Docker 的详细介绍:请点这里
Docker 的下载地址:请点这里
本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-03/141579.htm