OverlayFS是一个类似于AUFS 的现代联合文件系统,更快实现简单。
OverlayFS是内核提供的文件系统,overlay和overlay2是docker的存储驱动
{
"storage-driver": "overlay",
"storage-opts": [
"xxxxxxxx"
]
}
OverlayFS将单个Linux主机上的两个目录合并成一个目录。这些目录被称为层,统一过程被称为联合挂载。OverlayFS底层目录称为lowerdir, 高层目录称为upperdir。合并统一视图称为merged。当需要修改一个文件时,使用CoW将文件从只读的Lower复制到可写的Upper进行修改,结果也保存在Upper层。在Docker中,底下的只读层就是image,可写层就是Container
下图分层图,镜像层是lowdir,容器层是upperdir,统一的视图层是merged层
在路径/var/lib/docker/overlay/下,每个镜像层都有一个对应的目录,包含了该层镜像的内容,centos 7.2镜像
只有一层
通过tree命令,每个镜像层只包含一个root目录。 (docker1.10+使用基于内容的寻址,因此目录名和镜像层id不同名)
镜像层目录中,共享的数据使用的是硬链接,他们的inode号相同。这样做有效地利用了磁盘。
创建容器会在已有的镜像层上创建了一层容器层,容器层在路径/var/lib/docker/overlay下也存在对应的目录(e76cd47*目录)
文件 lower-id 用来记录下层的镜像层,upper 包含了容器层的内容。创建容器时将lower-id指向的镜像层目录以及upper目录联合挂载到merged目录。work 用来完成如 copy-on_write 的操作
读操作
- 如果文件在容器层不存在,则从lowdir中读取
- 只在容器层存在,则直接从容器中读取改文件
- 文件存在容器和镜像层,容器层upperdir会覆盖镜像层lowdir中的文件
修改
首次写入: 在upperdir中不存在,overlay和overlay2执行copy_up操作,把文件从lowdir拷贝到upperdir,由于overlayfs是文件级别的(即使文件只有很少的一点修改,也会产生的copy_up的行为)
copy_up操作只发生在文件首次写入,以后都是只修改副本
overlayfs只适用两层,因此性能很好,查找搜索都更快删除文件和目录: 当文件在容器被删除时,在容器层(upperdir)创建whiteout文件,镜像层的文件是不会被删除的,因为他们是只读的,但without文件 会阻止他们展现,当目录在容器内被删除时,在容器层(upperdir)一个不透明的目录,这个和上面whiteout原理一样,阻止用户继续访问,即便镜像层仍然存在
这个系统调用只在源和目标都在顶层,否则会报 error (“cross-device link not permitted”)
- 页缓存:overlayfs支持页缓存共享,也就是说如果多个容器访问同一个文件,可以共享同一个页缓存。这使得overlay/overlay2驱动高效地利用了内存
- copy_up:aufs和overlayfs,由于第一次写入都会导致copy_up,尤其是大文件,会导致写延迟,以后的写入不会有问题。由于overlayfs层级 比aufs的多,所以ovelayfs的拷贝高于aufs
- inode限制:使用overlay存储驱动可能导致inode过度消耗,特别是当容器和镜像很多的情况下,所以建议使用overlay2.
overlay的改进版,只支持4.0以上内核添加了Multiple lower layers in overlayfs的特性,所以overlay2可以直接造成muitiple lower layers不用像overlay一样要通过硬链接的方式(最大128层) centos的话支持3.10.0-514及以上内核版本也有此特性,所以消耗更少的inode
docker官方overlay2的PR:
https://github.com/moby/moby/pull/22126LINUX KERNERL 4.0 release说明:
https://kernelnewbies.org/Linux_4.0
只有一个centos7.2镜像,该镜像只有一层镜像
l目录包含了很多软连接,使用短名称指向了其他层(短名称用于避免mount参数时达到页面大小的限制)
启动一个容器,也是载/var/lib/docker/overlay2目录下生成一层容器层,目录包括diff,link,lower,merged,work
diff记录每一层自己内容的数据,link记录该层链接目录(实际是l目录下到层的链接),比如在容器中创建目录或在diff新增该目录
mount查看挂载情况如下,通过每层的lower文件如lowerdir参数所示
overlay on /var/lib/docker/overlay2/e1a10e566ec3832013ab219145ec1809aef53b44ce051c2794b949ce89e42005/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/3KNK6CF4RMMSMJUMZUIU3FYN4C:/var/lib/docker/overlay2/l/L6EQRQREBJWPPUITPLTGKSY6SQ,upperdir=/var/lib/docker/overlay2/e1a10e566ec3832013ab219145ec1809aef53b44ce051c2794b949ce89e42005/diff,workdir=/var/lib/docker/overlay2/e1a10e566ec3832013ab219145ec1809aef53b44ce051c2794b949ce89e42005/work)
本质区别是镜像层之间共享数据的方法不同
overlay只支持两层lowerdir和upperdir,并且只支持一个lowerdir,所以如果你的容器镜像有多层的话,层与层之前的数据共享是通过硬链接来实现的,我们也知道硬链接本身是同一个inode但不同的文件名而已,但为什么还是会大量消耗inode这里简单做的实验
我们在一台配置了storage-driver的机器上PULL ubuntu:18.04镜像
a. 挂载,准备目录和文件:
mkdir lower upper work merged
echo “lower.aaaa” > lower/aaaa
echo “lower.bbbb” > lower/bbbb
echo “upper.bbbb” > upper/bbbb
echo “upper.cccc” > upper/cccc
b. 挂载lower和upper目录到merged目录:
mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged
c. merged目录有3个文件,有lower目录的aaaa,upper目录的bbbb和cccc。可以看到upper目录的bbbb把lower目录的bbbb覆盖了
overlayfs中upper目录,一个或多个lower目录,挂载后都呈现在merged目录中。使用merged目录时,文件有可能是upper目录,也有可能是任何一层lower目录的,如何显示正确的文件呢?
ovl_dir_open 函数 (fs/overlayfs/readdir.c)
static int ovl_dir_open(struct inode *inode, struct file *file)
{
struct path realpath;
struct file *realfile;
struct ovl_dir_file *od;
enum ovl_path_type type;
od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL);
if (!od)
return -ENOMEM;
type = ovl_path_real(file->f_path.dentry, &realpath);
realfile = ovl_path_open(&realpath, file->f_flags);
if (IS_ERR(realfile)) {
kfree(od);
return PTR_ERR(realfile);
}
od->realfile = realfile;
od->is_real = !OVL_TYPE_MERGE(type);
od->is_upper = OVL_TYPE_UPPER(type);
file->private_data = od; //ovl_dir_file结构可以通过struct file的private_data找到。
return 0;
}
ovl_iterate函数 fs/overlayfs/readdir.c
upper、lower文件合并同名覆盖
https://docs.docker.com/storage/storagedriver/overlayfs-driver/#how-the-overlay-driver-works
https://docs.docker.com/storage/storagedriver/overlayfs-driver/