从aufs文件系统到docker镜像

概要

1 aufs文件下系统是什么?有什么特性?

2 aufs文件系统与docker镜像的关系。

3 dockfile如何工作?

4 docker镜像储存到哪里?储存的什么?

5 docker镜像的储存过程简介。
  1. aufs文件下系统是什么?有什么特性?

    aufs文件系统全名为advanced multi-layered unification filesystem.
    它能够将不同类型的文件系统透明地层叠在一起,实现一个高效的分层文件系统。简言之,它能将不同的目录挂在到统一目录下。

    我们从几个简单的实验窥探一下aufs文件系统的挂载特性。

    实验系统环境

    OS : Ubuntu 14.04.2 LTS (GNU/Linux 3.16.0-30-generic x86_64)

    docker : client/server 1.12.1

    挂载命令:mount -t aufs -o br=path/to/dir/A:path/to/dir/A none path/to/dir/uniondir

    mount命令中,-t表示目标文件系统类型,-o表示挂载参数, none表示挂载的不是设备文件。这些都是mount命令的参数。br是aufs的参数,表示分支,多个分支之间用冒号分隔。

    挂载实验
    ```
    mkdir a b uniondir
    echo 1 > a/1.txt
    echo 2 > b/2.txt
    tree
    #├── a
    #│   └── 1.txt
    #├── b
    #│   └── 2.txt
    #└── uniondir
    sudo mount -t aufs -o br=/home/zyf/aufstest/a:/home/zyf/aufstest/b none /home/zyf/aufstest/uniondir
    tree
    #├── a
    #│   └── 1.txt
    #├── b
    #│   └── 2.txt
    #└── uniondir
    #    ├── 1.txt
    #    └── 2.txt
    ```
    由此可见我们将a目录的1.txt和b目录的2.txt挂载到了uniondir下。
    在挂在时候,我们指定a目录在前,所以a目录是逻辑的上层。br参数没有加读写权限之前,逻辑上层(a目录)为读写权限。其余层(b目录)为制度权限。所以如果我们在uniondir创建3.txt,此文件会创建在a目录中。
    ```
    cd uniondir
    vim 3.txt
    cd ../ & tree
    #├── a
    #│   ├── 1.txt
    #│   └── 3.txt
    #├── b
    #│   └── 2.txt
    #└── uniondir
    #    ├── 1.txt
    #    ├── 2.txt
    #    └── 3.txt
    ```
    同样的,我们如果在一开始在a目录中创建2.txt。在挂在时候会优先挂载2.txt。
    因为读写权限的不同,修改1.txt和2.txt内容对a、b的影响也不同:
    ```
    echo 'add sth' >> /root/test/uniondir/1.txt
    echo 'add sth' >> /root/test/uniondir/2.txt
    cd ../ & tree
    #├── a
    #│   ├── 1.txt
    #|   |   2.txt
    #│   └── 3.txt
    #├── b
    #│   └── 2.txt
    #└── uniondir
    #    ├── 1.txt
    #    ├── 2.txt
    #    └── 3.txt        
    ```
    可见,对可读写分支(目录a),修改直接作用到分支上;
    而对于只读分支(目录b),修改操作会触发一次写时复制(COW, Copy On Write)
    即先将待修改文件从目录b复制到目录a,然后在目录a修改相应文件。
    注意写时复制机制可能在修改大文件时带来性能损耗,虽然每个文件最多只会复制一次。
    
  2. aufs文件系统与docker镜像的关系。

    docker镜像在构建的时候利用了aufs文件系统的挂载原理,对文件系统进行构建。

    首先,存在一个 docker 容器在启动时其内部进程可见的文件系统视角,或者称为docker 容器的根目录rootfs。目录下含有 docker 容器所需要的系统文件、工具、容器文件等。(只读属性)

    接着,在特定的镜像中存在每一个镜像独有的文件系统(读写属性)

    docker这时候利用aufs进行两者挂载。形成一个镜像。可以由示意图表示。


    docker 镜像复用的增量特性

    docker 众所周知是一种很省资源的容器。这一特性很大程度得益于aufs系统特性使得docker镜像进行复用。

    从aufs文件系统到docker镜像_第1张图片

    从上图中我们可以看到,无论是unbuntu这种OS系统,还是mysql,mongo这种具体应用。docker利用的aufs文件系统在文件系统挂载时的(COW机制)对镜像进行复用。

    所以我们假设 docker build 构建出来的镜像名分别为 image 1 和 image 2,由于两个 Dockerfile 均基于ubuntu:14.04,因此,image 1 和 image 2 这两个镜像均复用了镜像 ubuntu:14.04。 假设 RUN apt-get update 修改的文件系统内容为 20 MB,最终本地三个镜像的大小关系应该如下 :

    ubuntu:14.04200 MB
    image 1200 MB(ubuntu:14.04 的大小)+ 20 MB = 220 MB
    image 2200 MB(ubuntu:14.04 的大小)+ 100 MB = 300 MB

    如果仅仅是单纯的累加三个镜像的大小,那结果应该是:200 + 220 + 300 = 720 MB,但是由于镜像复用的存在,实际占用的磁盘空间大小是:200 + 20 + 100 + 320 MB,足足节省了 400 MB 的磁盘空间。在此,足以证明镜像复用的巨大好处。

  3. dockfile如何工作?

    既然说到了image,那不妨再谈一下dockerfile。具体是如何工作的。

    Dockerfile 由多条指令构成,随着深入研究 Dockerfile 与镜像的关系,会发现,Dockerfile 中的每一条指令都会对应于 Docker 镜像中的一层。

    如下 Dockerfile 为例:

    FROM ubuntu:14.04  
    ADD run.sh /  
    VOLUME /data  
    CMD ["./run.sh"]  

    通过 docker build 以上 Dockerfile 的时候,会在 ubuntu:14.04 镜像基础上,添加三层独立的镜像,依次对应于三条不同的命令。镜像示意图如下:
    从aufs文件系统到docker镜像_第2张图片
    有了 Dockerfile 与镜像关系的初步认识之后,我们再进一步联系到每一层镜像的大小。

    在层级化管理的 Docker 镜像中,有不少层大小都为 0。那些镜像层大小不为 0 的情况,归根结底的原因是:构建 Docker 镜像时,对当前的文件系统造成了修改更新。而修改更新的情况主要有两种:

    ADD 或 COPY 命令:ADD 或者 COPY 的作用是在 docker build 构建镜像时向容器中添加内容,只要内容添加成功,当前构建的那层镜像就是添加内容的大小,如以上命令 ADD run.sh /,新构建的那层镜像大小为文件 run.sh 的大小。

    RUN 命令:RUN 命令的作用是在当前空的镜像层内运行一条命令,倘若运行的命令需要更新磁盘文件,那么所有的更新内容都在存储在当前镜像层中。举例说明:RUN echo DaoCloud 命令不涉及文件系统内容的修改,故命令运行完之后当前镜像层的大小为 0;RUN wget http://abc.com/def.tar 命令会将压缩包下载至当前目录下,因此当前这一层镜像的大小为:对文件系统内容的增量修改部分,即 def.tar 文件的大小(在成功执行的情况下)。

  4. docker镜像储存到哪里?储存的什么?

    docker将镜像,存储到了/var/lib/docker/下面。

    Docker 镜像层的内容一般在 Docker 根目录的 aufs 路径下,为 /var/lib/docker/aufs/diff/,具体情况如下:

    root@hmp-pc:/var/lib/docker/aufs/diff# ls | xargs ls
    
    0a838d1341807dc3c97008ffbbc4c97e309f8323b9a6ed44db57b5dba1e37d1c:
    etc
    
    169bc4f555486d96269d377a6c4363fc70b3f280815630123c12fed6cc077200:
    etc  sbin  usr  var
    
    1d4c362445794e698d4b115de91a610894c0038b94df399d7c6ef363ed70550c:
    bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
    
    223ff0b1885840f717348ce856a6569f43476eadcc174ebfd77015159d7eaa78:
    

    另外出了镜像文件之外,还需要对每一个镜像层储存的json文件(一般json储存在了docker/graph目录中,此系统没有找到。)在此系统储存道了/var/lib/docker/image/aufs下的imagedb中

    root@hmp-pc:/var/lib/docker/image/aufs/imagedb/content/sha256# vim 0027e029bd6c37ffc292b52d447d0a44d25a03c88415090a942074cdf228e216 
    
    {"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd":["/bin/bash"]............
  5. docker镜像的储存过程。

    镜像储存过程在docker源码中分成了以下四部。他们分别是:

    1. 验证镜像ID
    2. 创建镜像路径
    3. 储存镜像内容
    4. 注册镜像ID

    在创建镜像路径中使用了mount union的挂载计数。如下图所示
    从aufs文件系统到docker镜像_第3张图片

    在储存镜像内容中,实现的是
    解压镜像内容 layerData 至 diff 目录;收集镜像所占空间大小,并记录;将 jsonData 信息写入临时文件。这三部操作。

    最终镜像储存到了/var/lib/docker/auf中。

参考资料:

关于镜像储存

http://dockone.io/question/70

http://docs.daocloud.io/allen-docker/docker-image-details

关于aufs文件系统

http://www.infoq.com/cn/articles/analysis-of-docker-file-system-aufs-and-devicemapper

http://fengchj.com/?p=2293

关于镜像储存过程与镜像理解

《docker源码分析》

你可能感兴趣的:(团队小项目)