docker背后的存储之aufs(一): Overview

| 分类 docker  | 标签 docker  linux  storage 

概述

docker将网络,存储,监控以及cgroup/namespace和docker本身抽象隔离开来,主要的driver有这么几种:

  • execdriver — lxc libcontianer 实现隔离(cgroup + namespace)
    基于cgroup, namespace将image运行起来,隔离多个进程,这方面包括uts, process, memory,network等等。docker支持lib container和lxc两种方式实现。
  • graphdriver — 实现image和container的存储 (rootfs) graph和aufs(device mapper)目录
  • networkdriver — docker vm的网络设置 (bridge+iptables) (bridge + mac switch) (ipvlan)

Docker image是一个抽象的概念,表示的是一个容器运行的rootfs环境。非常类似Cloud foundry里面的buildpack概念。
Dockerfile是docker的一种语法,用来build生成镜像。
Docker registry是image的管理。
docker image最为主要的概念是分层管理,Copy on Write,可以做到最小化image,而且可复用。

docker image分成两部分来管理

  • metadata 存储在/var/lib/docker/graph/{imageId},其中每个imageID代表一个layer, 是每次dockerfile里面动作的一个结果,例如ADD, RUN等。该文件目录下主要有两个文件, json存储了该image的metadata,例如创建时间, 对应的parent image, image产生的命令等等。另一个文件layersize是一个描述了的diff包的大小。
  • image tar diff。存储在/var/lib/docker/aufs目录。

aufs的目录主要分成三种 diff,layers和mnt。下面一一解释

/var/lib/docker/aufs/diff/{imageId}:  
/var/lib/docker/aufs/diff/{containerId}:  
/var/lib/docker/aufs/diff/{containerId-init}:  

存储每次该imageId指定的image相比其parent image的文件delta(diff)。例如ADD的一个文件, COPY的一个文件, RUN会改变的目录及文件等等。 另外也会存储每次产生container时需要的两层文件,{containerId}和{containerId-init}。这两层文件的作用后面会解释。

/var/lib/docker/aufs/layers/{imageId}:  
/var/lib/docker/aufs/layers/{containerId}:  
/var/lib/docker/aufs/layers/{containerId-init}:  

存储了当前image的parent image chain,便于快速构建image tree。当然,也存储了每次产生container时需要的两层文件所对应的文件层次,其实 {containerId}的parent就是{containerId-init}。我们可以先看一个image的例子。

root@vagrant-ubuntu-trusty-64:/var/lib/docker/aufs/layers# cat 60c018693d4c850166810f7f8104fa1b310bbad355eb1d43cbca02a31a0db9a8
18392d3874fbb2fcbfa861a81d5b6ea32c81c3293e9c0cda62143b599f186c4a
05a707a5c88538f5695ce679e1b61331b43a24faf3a755b12ff7873fd4e62003
8a09e63f6f5cca6b2b88f2a50f6a5c5399002f8394bf6dd44243ebec7277e632
...


/var/lib/docker/aufs/mnt/{containerId}:
/var/lib/docker/aufs/mnt/{containerId-init}:

存储了aufs将layed image经过union以后形成的最终可以读写的rootfs。

下面结合数据结构研究一下几个docker命令的实现

docker history [IMAGE]

根据image的repository:tag得到其image id; 一种是可以根据image id找到其/var/lib/docker/graph/{imageId}目录,找到其json数据,从而知道其parent ID; 循环 parentID until last 另一种是通过/var/lib/docker/aufs/layers/{imageId}找到其image tree.

这里tagStore 存储在/var/lib/docker/repositories-{graphDriver}

Repository map[string][]string
"ubuntu":
{"14.04":"d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6",
"14.04.2":"d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6",
"latest":"d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6",
"trusty":"d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6",
"trusty-20150630":"d2a0ecffe6fa4ef3de9646a75cc629bbd9da7eead7f767cb810f9808d6b3ecb6"}

docker create and run

首先,创建containerId-init的layer, diff。并mount到/mnt/{containerId}-init

/var/lib/docker/aufs/diff/{containerId}-init  (RW)
/var/lib/docker/aufs/diff/{baseImageId}   (RO)
...
————>
/var/lib/docker/aufs/mnt/{containerId}-init

等价于下面的mount命令

mount -t aufs
br:/var/lib/docker/aufs/diff/{containerId}-init=rw
:/var/lib/docker/aufs/diff/{baseImageId}=ro+wh,xino=/dev/shm/aufs.xino
:/var/lib/docker/aufs/diff/{parentofBaseImageId}=ro+wh ...
/var/lib/docker/aufs/mnt/{conatainer}-init

其次准备container top-level 的一些文件,即对/var/lib/docker/aufs/mnt/{conatainer}-init写入一些top-level文件,也就是写到/var/lib/docker/aufs/diff/{containerId}-init中, 这是为了起到保护作用。这些文件主要是/dev/shm, /dev/console, /etc/hostname, /etc/hosts, /etc/resolv.conf等网络相关的配置等。这里要注意的是这里写到/diff/{containerId}-init中的都是空文件,而实际在container里面使用的这些文件或者目录,都会在子进程中重新mount到rootfs中。实际上,子进程不仅mount这些/dev/shm, /dev/console, /etc/hostname, /etc/hosts, /etc/resolv.conf, /proc, /sys, 而且会mount挂载的volumn。

root@vagrant-ubuntu-trusty-64:/var/lib/docker/aufs/diff/7e825e149f15854727c1a77f88d92726458197c11b52d6375664db1e4065e0ae-init/dev# ls -al
total 12
drwxr-xr-x 3 root root 4096 Jul 28 08:06 .
drwxr-xr-x 6 root root 4096 Jul 28 08:06 ..
-rwxr-xr-x 1 root root    0 Jul 28 08:06 console
drwxr-xr-x 2 root root 4096 Jul 28 08:06 shm
root@vagrant-ubuntu-trusty-64:/var/lib/docker/aufs/diff/7767c05d8bbf2214ad94ccb887b723342d7b3f3e63f811fa53f5320b83fcf2b8-init/etc# ll
total 8
drwxr-xr-x 2 root root 4096 Jul 28 08:06 ./
drwxr-xr-x 6 root root 4096 Jul 28 08:06 ../
-rwxr-xr-x 1 root root    0 Jul 28 08:06 hostname*
-rwxr-xr-x 1 root root    0 Jul 28 08:06 hosts*
lrwxrwxrwx 1 root root   12 Jul 28 08:06 mtab -> /proc/mounts
-rwxr-xr-x 1 root root    0 Jul 28 08:06 resolv.conf*

最后创建containerId的layer, diff,并mount到/mnt/{containerId}

/var/lib/docker/aufs/diff/{containerId}   (RW)
/var/lib/docker/aufs/diff/{containerId}-init  (RO)
/var/lib/docker/aufs/diff/{baseImageId}   (RO) ...
————>
/var/lib/docker/aufs/mnt/{containerId}

例如, 一个container

7767c05d8bbf docker.dp/tomcat/ngx4a-2:latest "/bin/sh -c /bin/sta “
root@vagrant-ubuntu-trusty-64:/var/lib/docker/aufs/layers# cat 7767c05d8bbf2214ad94ccb887b723342d7b3f3e63f811fa53f5320b83fcf2b8-init (init image)
60c018693d4c850166810f7f8104fa1b310bbad355eb1d43cbca02a31a0db9a8 (base image)
18392d3874fbb2fcbfa861a81d5b6ea32c81c3293e9c0cda62143b599f186c4a

root@vagrant-ubuntu-trusty-64:/var/lib/docker/aufs/layers# cat 7767c05d8bbf2214ad94ccb887b723342d7b3f3e63f811fa53f5320b83fcf2b8
7767c05d8bbf2214ad94ccb887b723342d7b3f3e63f811fa53f5320b83fcf2b8-init
60c018693d4c850166810f7f8104fa1b310bbad355eb1d43cbca02a31a0db9a8

docker build

首先分析出dockerfile每步需要做的动作,对每一步都会创建临时的一个container 该container的rootfs是来自两部分
1)/var/lib/docker/aufs/diff/{containerId}-init(此为当前container运行的init image,ro: read only), 注意/var/lib/docker/aufs/diff/{containerId}-init的parent image即为当前指定的base image(container从哪个image创建出来)。
2)/var/lib/docker/aufs/diff/{containerId}(rw: readwrite), 而mount的target为/var/lib/docker/aufs/mnt/{containerId},这样对于该container而言,对roots的写全部落入了/var/lib/docker/aufs/diff/{containerId}中。这样就可以得到当前步骤相对于parent image的diff。

/var/lib/docker/aufs/diff/{containerId} (RW)
/var/lib/docker/aufs/diff/{containerId}-init}  (RO)
/var/lib/docker/aufs/diff/{baseImageId}   (RO)
...
————>
/var/lib/docker/aufs/mnt/{containerId}

在该container RUN完之后,立即创建一个新的image对象保存metadata,并写到/var/lib/docker/graph/{imageId}中, 接下来调用driver.Diff(container.ID, initID)获取该container相对于init image的diff数据(tar包),并接着调用 driver.ApplyDiff(imageId, parentId, layerData archive.ArchiveReader)将数据写到 文件系统/var/lib/docker/aufs/diff/{imageId}中

aufs的作用 aufs 可以将每次对layed image所做的更改commit 成一个image(tar),然后可以merge在一起。像这样变成一个rootfs

/var/lib/docker/aufs/diff/{containerId} (RW)
/var/lib/docker/aufs/diff/{containerId}-init}  (RO)
/var/lib/docker/aufs/diff/{baseImageId}   (RO)
...
————>
/var/lib/docker/aufs/mnt/{containerId}

上一篇     下一篇