Docker运行原理-0X04-Unionfs

Docker运行原理-0X04-Unionfs

Created
Mar 29, 2022 08:34 AM
Tags
容器

Unionfs

全程Union file system(联合文件系统),它的作用是把多个目录联合挂载到一个目录中,并且运行“只读”和”可读写“目录并存。AUFS是一种Union File System,例子如下所示:
先建立两个文件夹,
➜ union-test  tree
.
├── dir-1
│   ├── file-1
│   └── file-2
├── dir-2
│   ├── file-1
│   └── file-3
└── mnt

2 directories, 4 files
接着使用aufs方式进行挂载,联合之后mnt文件夹下有3个文件
➜ union-test  mount -t aufs -o dirs=./dir-1:./dir-2 none ./mnt
➜ union-test  tree
.
├── dir-1
│   ├── file-1
│   └── file-2
├── dir-2
│   ├── file-1
│   └── file-3
└── mnt
    ├── file-1
    ├── file-2
    └── file-3

3 directories, 7 files
此时向“/mnt/file-3”文件中写入东西,观察三个文件夹下的变化:
➜ union-test  echo "test" > ./mnt/file-3 
radish ➜ union-test  tree
.
├── dir-1
│   ├── file-1
│   ├── file-2
│   └── file-3
├── dir-2
│   ├── file-1
│   └── file-3
└── mnt
    ├── file-1
    ├── file-2
    └── file-3

3 directories, 8 files
➜ union-test  cat ./dir-1/file-3 
test
➜ union-test  cat ./dir-2/file-3
➜ union-test
当向”/mnt/file-3“写入数据时,会在”dir-1“中同步file-3的信息。而”dir-2“中则会不变化。

AUFS特性

AUFS中要被union的目录称为Branch,也就是mount命令中dir参数后门的值,Branch会根据union的顺序形成一个stack,一般最上面的是可写的,下面的都是只读的。Branch stack 还是可以进行修改,比如修改顺序,加入新的 branch,或者删除其中的 branch,或者直接修改 branch 的权限。
目录的权限有:
  • rw:可读可写
  • ro:只读。如果dir没有指定权限,那么该权限是默认值。对于该权限的目录,不会收到写操作,也不会收到查找whiteout的操作 (这一句没明白啥意思)
  • rr:real-read-only,该标记表示本身就是只读Branch,不需要再对此目录设置inotify监听文件变化了。
  • wh:whiteout。当union中要删除在read-only目录上的某个文件时,就需要对ro目录里的文件做whiteout。
wh例子:
➜ union-test  mount -t aufs -o dirs=./test=rw:./dir-1=ro:./dir-2=ro none ./mnt
 ➜ union-test  tree
.
├── dir-1
│   ├── file-1
│   └── file-2
├── dir-2
│   ├── file-1
│   └── file-3
├── mnt
│   ├── file-1
│   ├── file-2
│   └── file-3
└── test

4 directories, 7 files
 ➜ union-test  touch ./test/.wh.file-3
 ➜ union-test  tree
.
├── dir-1
│   ├── file-1
│   └── file-2
├── dir-2
│   ├── file-1
│   └── file-3
├── mnt
│   ├── file-1
│   └── file-2
└── test

4 directories, 6 files
当在“./test”目录下创建”.wh.file-3“时,mnt下的file-3文件就会被删除。

overlay

overlay也是一种Union file system,但概念比AUFS简单。overlay由三部分组成:
  • lower-dir :下层目录
  • upper-dir:上层目录
  • merged:合并之后的文件系统的目录
缺点就是overlay受限于只有一个lower-dir,要想实现多层lower-dir,需要将当前lower-dir成为另一个overlay的merged目录,形成嵌套使用。

overlay2

overlay2继承了overlay的特性,在原有的基础上进行完善,overlay2原生的支持多层lower-dir,最多支持128层。
notion image
image-20220325102359310
从上图可以看出,lowerdir和upperdir在进行联合时,会合并文件系统到merged。除此之外,还有一个worddir目录,用来存放挂在后临时文件和间接文件(暂时不明白具体用途在哪里)。
对于upperdir目录,我自己的理解是,当运行一个容器时,会在原来的环境上添加或删除一些文件或目录,这些操作所影响的文件会单独保存在upperdir目录上,不需要保存整个容器的文件系统,当把当前容器状态打包成镜像时,就会在原有的镜像基础上新加一层,也就是把当前upperdir合并到镜像当中,从而不会对原有的image进行更改。

docker 实现

查看ubuntu:16.04的信息docker inspect ubuntu:16.04
"GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9/diff:/var/lib/docker/overlay2/0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1/diff:/var/lib/docker/overlay2/1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e/diff",
                "MergedDir": "/var/lib/docker/overlay2/ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0/merged",
                "UpperDir": "/var/lib/docker/overlay2/ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0/diff",
                "WorkDir": "/var/lib/docker/overlay2/ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:be96a3f634de79f523f07c7e4e0216c28af45eb5776e7a6238a2392f71e01069",
                "sha256:df54c846128da3c71cc11b2150a3df39ec86fb170e299765daf6bb016a0705c2",
                "sha256:47ef83afae74745639f6738a05fe5320fcfca9e6c7765fba4f25e270bc0df9dc",
                "sha256:1251204ef8fc20da275e09f6e3ab9205421d4ff34732f2d50a1d3e86d2995edd"
            ]
        },
从结果可以看,该镜像是由四层镜像层组成,每一层都是ubuntu:16.04系统的一部分,本地文件在“/var/lib/docker/overlay2/”,“l”目录下存储着各个层的diff软连接,每个镜像层的文件具体放在“diff”目录下,
➜  overlay2 docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
58690f9b18fc: Pull complete 
b51569e7c507: Pull complete 
da8ef40b9eca: Pull complete 
fb15d46c38dc: Pull complete 
Digest: sha256:0f71fa8d4d2d4292c3c617fda2b36f6dabe5c8b6e34c3dc5b0d17d4e704bd39c
Status: Downloaded newer image for ubuntu:16.04
docker.io/library/ubuntu:16.04
➜  overlay2 ls -al
总用量 28
drwx--x---  7 root root 4096 329 14:41 .
drwx--x--- 13 root root 4096 329 14:23 ..
drwx--x---  4 root root 4096 329 14:41 0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1
drwx--x---  3 root root 4096 329 14:41 1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e
drwx--x---  4 root root 4096 329 14:41 ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0
drwx--x---  4 root root 4096 329 14:41 e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9
drwx------  2 root root 4096 329 14:41 l
➜  overlay2 ls -al l
总用量 24
drwx------ 2 root root 4096 329 14:41 .
drwx--x--- 7 root root 4096 329 14:41 ..
lrwxrwxrwx 1 root root   72 329 14:41 DKGMRHQG2NPFILQ6WL4HUSASVD -> ../e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9/diff
lrwxrwxrwx 1 root root   72 329 14:41 DS3G7MGYZGUSF7ZE3ROLDQGDNY -> ../1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e/diff
lrwxrwxrwx 1 root root   72 329 14:41 K2LYWD5D2U2TR6ATHGV3AK2COP -> ../0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1/diff
lrwxrwxrwx 1 root root   72 329 14:41 WQLHKIC2YT2JC4LMDQC6FT6PIK -> ../ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0/diff

➜  overlay2 tree -d -L 3  
.
├── 0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1
│   ├── diff
│   │   ├── etc
│   │   ├── sbin
│   │   ├── usr
│   │   └── var
│   └── work
├── 1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e
│   └── diff
│       ├── bin
│       ├── boot
│       ├── dev
│       ├── etc
│       ├── home
│       ├── lib
│       ├── lib64
│       ├── media
│       ├── mnt
│       ├── opt
│       ├── proc
│       ├── root
│       ├── run
│       ├── sbin
│       ├── srv
│       ├── sys
│       ├── tmp
│       ├── usr
│       └── var
├── ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0
│   ├── diff
│   │   └── run
│   └── work
├── e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9
│   ├── diff
│   │   └── var
│   └── work
└── l
    ├── DKGMRHQG2NPFILQ6WL4HUSASVD -> ../e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9/diff
    ├── DS3G7MGYZGUSF7ZE3ROLDQGDNY -> ../1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e/diff
    ├── K2LYWD5D2U2TR6ATHGV3AK2COP -> ../0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1/diff
    └── WQLHKIC2YT2JC4LMDQC6FT6PIK -> ../ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0/diff

41 directories
➜  overlay2
接下来这个镜像来启动一个容器
➜  overlay2 ls -al
总用量 36
drwx--x---  9 root root 4096 329 14:50 .
drwx--x--- 13 root root 4096 329 14:23 ..
drwx--x---  4 root root 4096 329 14:41 0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1
drwx--x---  3 root root 4096 329 14:41 1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e
drwx--x---  5 root root 4096 329 14:50 3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886
drwx--x---  4 root root 4096 329 14:50 3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886-init
drwx--x---  4 root root 4096 329 14:50 ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0
drwx--x---  4 root root 4096 329 14:41 e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9
drwx------  2 root root 4096 329 14:50 l
➜  overlay2 ls -al l 
总用量 32
drwx------ 2 root root 4096 329 14:50 .
drwx--x--- 9 root root 4096 329 14:50 ..
lrwxrwxrwx 1 root root   72 329 14:41 DKGMRHQG2NPFILQ6WL4HUSASVD -> ../e271cc26ba0db66ac1a18fb52ae8fbf3d679170a7b579d56491d4f0975a16fe9/diff
lrwxrwxrwx 1 root root   72 329 14:41 DS3G7MGYZGUSF7ZE3ROLDQGDNY -> ../1441d9c6a2ac10320e2c4b5d60f11c4fbe65ce392517629f92aad8b8e24a4b5e/diff
lrwxrwxrwx 1 root root   72 329 14:50 IORLYF22L4G2VS4NN4J757E7B7 -> ../3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/diff
lrwxrwxrwx 1 root root   72 329 14:41 K2LYWD5D2U2TR6ATHGV3AK2COP -> ../0571fdf8378b1f99d39dde4f47446de3b2503e95c9c1d037fa2d4b0b464036f1/diff
lrwxrwxrwx 1 root root   72 329 14:41 WQLHKIC2YT2JC4LMDQC6FT6PIK -> ../ade3098855566c3b0a58a510b65929313350ccf030fef6d67f67f87ec382a6b0/diff
lrwxrwxrwx 1 root root   77 329 14:50 WR4JHLPOEW6CZ2ZBTPBW23TNSN -> ../3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886-init/diff
可以发现在overlay2目录下多了两个目录,分别是“3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886-init”和“3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886”,以及在“l”目录下多了“IORLYF22L4G2VS4NN4J757E7B7 -> ../3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/diff”和“WR4JHLPOEW6CZ2ZBTPBW23TNSN -> ../3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886-init/diff”
进入容器中执行mount查看挂载情况
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/WR4JHLPOEW6CZ2ZBTPBW23TNSN:/var/lib/docker/overlay2/l/WQLHKIC2YT2JC4LMDQC6FT6PIK:/var/lib/docker/overlay2/l/DKGMRHQG2NPFILQ6WL4HUSASVD:/var/lib/docker/overlay2/l/K2LYWD5D2U2TR6ATHGV3AK2COP:/var/lib/docker/overlay2/l/DS3G7MGYZGUSF7ZE3ROLDQGDNY,upperdir=/var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/diff,workdir=/var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/work)
拆分:
  • lowerdir
    • ​ /var/lib/docker/overlay2/l/WR4JHLPOEW6CZ2ZBTPBW23TNSN:
    • ​ /var/lib/docker/overlay2/l/WQLHKIC2YT2JC4LMDQC6FT6PIK:
    • ​ /var/lib/docker/overlay2/l/DKGMRHQG2NPFILQ6WL4HUSASVD:
    • ​ /var/lib/docker/overlay2/l/K2LYWD5D2U2TR6ATHGV3AK2COP:
    • ​ /var/lib/docker/overlay2/l/DS3G7MGYZGUSF7ZE3ROLDQGDNY,
  • upperdir
    • ​ /var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/diff,
  • workdir
    • ​ /var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/work)
lowerdir是只读层,upperdir是可读可写层,workdir是 lowerdir 执行 copy_up 操作的中转层
copy_up 操作是指当要修改的文件不存在于 upperdir 而仅存在于 lower 时,要先将数据从 lower 拷贝到 upper 的这个操作
在宿主机上执行mount |grep 'overlay'查看挂载信息
overlay on /var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/WR4JHLPOEW6CZ2ZBTPBW23TNSN:/var/lib/docker/overlay2/l/WQLHKIC2YT2JC4LMDQC6FT6PIK:/var/lib/docker/overlay2/l/DKGMRHQG2NPFILQ6WL4HUSASVD:/var/lib/docker/overlay2/l/K2LYWD5D2U2TR6ATHGV3AK2COP:/var/lib/docker/overlay2/l/DS3G7MGYZGUSF7ZE3ROLDQGDNY,upperdir=/var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/diff,workdir=/var/lib/docker/overlay2/3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/work)
可以看出,lowerdir、upperdir、workdir通过overlay的方式挂载到“3200cbddd054891fe5592b93d135f24567d13ec98a4cf30d71072c22fe38b886/merged”目录上
容器整体文件结构如下图所示:
notion image
image-20220329160045833

分层结构

一个容器分为“可读可写层”、“Init层”、“只读层”。分别对应着上图的绿色、黄色、红色

只读层

只读层是容器的镜像文件系统。

Init 层

Init 层是 Docker 项目单独生成的一个内部层,专门用来存放 /etc/hosts、/etc/resolv.conf等信息。
需要这样一层的原因是,这些文件本来属于只读的 Ubuntu 镜像的一部分,但是用户往往需要在启动容器时写入一些指定的值比如 hostname,所以就需要在可读写层对它们进行修改。但是这些修改往往只对当前的容器有效,我们并不希望执行 docker commit时,把这些信息连同可读写层一起提交掉。

可读可写层

在没有写入文件之前,这个目录是空的。而一旦在容器里做了写操作,修改产生的内容就会以增量的方式出现在这个层中。

参考

https://dockone.io/article/1765
https://staight.github.io/2019/10/04/%E5%AE%B9%E5%99%A8%E5%AE%9E%E7%8E%B0-overlay2/