【Docker 基础知识】存储驱动overlay和overlay2

OverlayFS是一个类似于AUFS 的现代联合文件系统,更快实现简单。 

OverlayFS是内核提供的文件系统,overlay和overlay2是docker的存储驱动

设置存储驱动方法

{
  "storage-driver": "overlay",
  "storage-opts": [
    "xxxxxxxx"
  ]
}

 

1 overlay介绍

  1.1 overlay原理

      OverlayFS将单个Linux主机上的两个目录合并成一个目录。这些目录被称为层,统一过程被称为联合挂载。OverlayFS底层目录称为lowerdir, 高层目录称为upperdir。合并统一视图称为merged。当需要修改一个文件时,使用CoW将文件从只读的Lower复制到可写的Upper进行修改,结果也保存在Upper层。在Docker中,底下的只读层就是image,可写层就是Container

       下图分层图,镜像层是lowdir,容器层是upperdir,统一的视图层是merged层

  1.2 overlay镜像结构

      在路径/var/lib/docker/overlay/下,每个镜像层都有一个对应的目录,包含了该层镜像的内容,centos 7.2镜像

只有一层

       通过tree命令,每个镜像层只包含一个root目录。 (docker1.10+使用基于内容的寻址,因此目录名和镜像层id不同名)

   镜像层目录中,共享的数据使用的是硬链接,他们的inode号相同。这样做有效地利用了磁盘。

  1.3 overlay容器结构

       创建容器会在已有的镜像层上创建了一层容器层,容器层在路径/var/lib/docker/overlay下也存在对应的目录(e76cd47*目录)

【Docker 基础知识】存储驱动overlay和overlay2_第1张图片

      文件 lower-id 用来记录下层的镜像层,upper 包含了容器层的内容。创建容器时将lower-id指向的镜像层目录以及upper目录联合挂载到merged目录。work 用来完成如 copy-on_write 的操作

【Docker 基础知识】存储驱动overlay和overlay2_第2张图片

 

  1.4 文件操作

     读操作

  • 如果文件在容器层不存在,则从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.

 

2 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/22126

LINUX KERNERL 4.0 release说明:
https://kernelnewbies.org/Linux_4.0

   

  2.1 overlay2 镜像结构

      只有一个centos7.2镜像,该镜像只有一层镜像

【Docker 基础知识】存储驱动overlay和overlay2_第3张图片

    l目录包含了很多软连接,使用短名称指向了其他层(短名称用于避免mount参数时达到页面大小的限制)

 

  2.2 overlay2 容器结构

      启动一个容器,也是载/var/lib/docker/overlay2目录下生成一层容器层,目录包括diff,link,lower,merged,work

      diff记录每一层自己内容的数据,link记录该层链接目录(实际是l目录下到层的链接),比如在容器中创建目录或在diff新增该目录

【Docker 基础知识】存储驱动overlay和overlay2_第4张图片

      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)

 

3. overlay和overlay2的区别

    本质区别是镜像层之间共享数据的方法不同

  • overlay共享数据方式是通过硬连接,只挂载一层,其他层通过最高层通过硬连接形式共享(增加了磁盘inode的负担)
  • 而overlay2是通过每层的 lower文件

 

   为什么overlay相比overlay2要更加占用inode?

      overlay只支持两层lowerdir和upperdir,并且只支持一个lowerdir,所以如果你的容器镜像有多层的话,层与层之前的数据共享是通过硬链接来实现的,我们也知道硬链接本身是同一个inode但不同的文件名而已,但为什么还是会大量消耗inode这里简单做的实验
我们在一台配置了storage-driver的机器上PULL ubuntu:18.04镜像

 

 

4. 手动操作例子

  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覆盖了

【Docker 基础知识】存储驱动overlay和overlay2_第5张图片

   overlayfs中upper目录,一个或多个lower目录,挂载后都呈现在merged目录中。使用merged目录时,文件有可能是upper目录,也有可能是任何一层lower目录的,如何显示正确的文件呢?

    ovl_dir_open 函数 (fs/overlayfs/readdir.c)

  •  struct dentry的d_fsdata存放了对应文件的upper和lower信息,保存文件的路径
  • ovl_path_open最终调用vfs_open可以得到文件的真实路径
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/

你可能感兴趣的:(Docker)