overlay

716 阅读12分钟

OverlayFS

概览

overlayFS是一种新型联合文件系统(union filesystem),它允许用户将一个文件系统与另一个文件系统重叠(overlay),在上层的文件系统中记录更改,而下层得文件系统保持不变。

Overlay主要使用4类目录完成工作,被联合挂载的两个目录lower和upper,作为统一视图联合挂载点得merged目录,还有作为辅助功能的work目录。作为upper和lower被联合挂载得统一视图。当同一路径下得两个文件分别存在两个目录中时,位于上层的目录upper中得文件会屏蔽位于下层lower中得文件,如果是同路径的文件夹,那么下层目录中的文件和文件夹会被合并到上层。在对可读写的OverlayFS挂载目录中的文件进行读写删等操作过程与挂载两层的aufs(下层是只读层,上层是可读可写层)是类似的,需要注意的一点是,第一次(涉及到修改lower中的数据)以write方式打开一个位于下层目录的文件时OverlayFS会执行一个copy_up将文件从下层复制到上层,与aufs不同的是,这个copy_up的实现不符合POSIX标准。

代码实战:

一开始overlay路径下为空

[root@luyuni-aliyun ~]# tree -L 2 /var/lib/docker/overlay/
/var/lib/docker/overlay/

0 directories, 0 files

把ubuntu镜像pull下来,我们发现这个镜像有4层

[root@luyuni-aliyun ~]# docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
692c352adcf2: Pull complete
97058a342707: Pull complete
2821b8e766f4: Pull complete
4e643cc37772: Pull complete
Digest: sha256:55cd38b70425947db71112eb5dddfa3aa3e3ce307754a3df2269069d2278ce47
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

去overlay目录下面看一下

[root@luyuni-aliyun ~]# tree -L 2 /var/lib/docker/overlay/
/var/lib/docker/overlay/
├── 862976c524a76652a52d8966cf8b4d77ac3cf8ef17f7129d8cfb3685a89b4009
│   └── root
├── 886eb412f86d1926714db6031f17791d4c66cb2db0156b115a867378093d48ae
│   └── root
├── cf9d7c3cfb138d043defcb902c2fa3bc29f371701ade715974b983958ded4f93
│   └── root
└── e814ca9180804d1aee91358457f7ef835b20a722c45115503314e8048a778ff9
    └── root

我们可以发现每一层都有一个对应的目录,包含了该层镜像的内容,通过tree命令发现,每个镜像层下面之包含一个root目录(因为docker1.10开始,使用了基于内容的寻址,因此目录名和镜像层的id不一致)

[root@luyuni-aliyun ~]# ls -i /var/lib/docker/overlay/862976c524a76652a52d8966cf8b4d77ac3cf8ef17f7129d8cfb3685a89b4009/root/bin/ls
1573201 /var/lib/docker/overlay/862976c524a76652a52d8966cf8b4d77ac3cf8ef17f7129d8cfb3685a89b4009/root/bin/ls


[root@luyuni-aliyun ~]# ls -i /var/lib/docker/overlay/886eb412f86d1926714db6031f17791d4c66cb2db0156b115a867378093d48ae/root/bin/ls
1573201 /var/lib/docker/overlay/886eb412f86d1926714db6031f17791d4c66cb2db0156b115a867378093d48ae/root/bin/ls

每一层都包含了该层独有的文件以及和其低层共享的数据的硬链接,如ls命令,每一层都使用了一个硬链接来指向实际ls命令的inode号。

docker run 一把刚刚拉下来的镜像

[root@luyuni-aliyun ~]# docker run -itd ubuntu /bin/bash
206cdf00e86a0879d8b6de3979ea51eacafb7bb84e53c3da51acf5b74965c108

docker ps看一下有没有把容器起起来

[root@luyuni-aliyun ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
206cdf00e86a        ubuntu              "/bin/bash"         4 seconds ago       Up 4 seconds                            dreamy_pascal

看起来没有问题。创建容器是,实际上实在已有的镜像层上创建了一层容器层,容器层在/var/lib/docker/overlay下也存在对应的目录

[root@luyuni-aliyun ~]# tree -L 2 /var/lib/docker/overlay/
/var/lib/docker/overlay/
├── 862976c524a76652a52d8966cf8b4d77ac3cf8ef17f7129d8cfb3685a89b4009
│   └── root
├── 886eb412f86d1926714db6031f17791d4c66cb2db0156b115a867378093d48ae
│   └── root
├── c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547
│   ├── lower-id
│   ├── merged
│   ├── upper
│   └── work
├── c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547-init
│   ├── lower-id
│   ├── upper
│   └── work
├── cf9d7c3cfb138d043defcb902c2fa3bc29f371701ade715974b983958ded4f93
│   └── root
└── e814ca9180804d1aee91358457f7ef835b20a722c45115503314e8048a778ff9
    └── root

15 directories, 2 files

我们可以看到新增了c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547(读写层);c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547-init(初始层)。两个目录。初始层中大多是初始化容器环境时,与容器相关的环境信息,如容器主机名,主机host信息以及域名服务文件等。所有对容器做出的改变都记录在读写层

看一眼容器使用了哪层镜像

[root@luyuni-aliyun ~]# cat /var/lib/docker/overlay/c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547/lower-id
e814ca9180804d1aee91358457f7ef835b20a722c45115503314e8048a778ff9


[root@luyuni-aliyun ~]# cat /var/lib/docker/overlay/c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547-init/lower-id
e814ca9180804d1aee91358457f7ef835b20a722c45115503314e8048a778ff9

在刚刚run起来的容器里创建一个文件

root@206cdf00e86a:/# echo "niyulu" > luyunix
root@206cdf00e86a:/# ls -l
total 52
lrwxrwxrwx  1 root root    7 Jul  3 01:56 bin -> usr/bin
drwxr-xr-x  2 root root 4096 Apr 15 11:09 boot
drwxr-xr-x  5 root root  360 Jul 14 09:40 dev
drwxr-xr-x  1 root root 4096 Jul 14 09:40 etc
drwxr-xr-x  2 root root 4096 Apr 15 11:09 home
lrwxrwxrwx  1 root root    7 Jul  3 01:56 lib -> usr/lib
lrwxrwxrwx  1 root root    9 Jul  3 01:56 lib32 -> usr/lib32
lrwxrwxrwx  1 root root    9 Jul  3 01:56 lib64 -> usr/lib64
lrwxrwxrwx  1 root root   10 Jul  3 01:56 libx32 -> usr/libx32
-rw-r--r--  1 root root    7 Jul 14 10:00 luyunix
drwxr-xr-x  2 root root 4096 Jul  3 01:57 media
drwxr-xr-x  2 root root 4096 Jul  3 01:57 mnt
drwxr-xr-x  2 root root 4096 Jul  3 01:57 opt
dr-xr-xr-x 82 root root    0 Jul 14 09:40 proc
drwx------  2 root root 4096 Jul  3 02:00 root
drwxr-xr-x  5 root root 4096 Jul  6 21:56 run
lrwxrwxrwx  1 root root    8 Jul  3 01:56 sbin -> usr/sbin
drwxr-xr-x  2 root root 4096 Jul  3 01:57 srv
dr-xr-xr-x 13 root root    0 Jul 14 09:40 sys
drwxrwxrwt  2 root root 4096 Jul  3 02:00 tmp
drwxr-xr-x 13 root root 4096 Jul  3 01:57 usr
drwxr-xr-x 11 root root 4096 Jul  3 02:00 var

看看刚才创建的文件去了哪里

[root@luyuni-aliyun ~]# tree -L 3 /var/lib/docker/overlay
/var/lib/docker/overlay
├── 862976c524a76652a52d8966cf8b4d77ac3cf8ef17f7129d8cfb3685a89b4009
│   └── root
│       ├── bin -> usr/bin
│       ├── boot
│       ├── dev
│       ├── etc
│       ├── home
│       ├── lib -> usr/lib
│       ├── lib32 -> usr/lib32
│       ├── lib64 -> usr/lib64
│       ├── libx32 -> usr/libx32
│       ├── media
│       ├── mnt
│       ├── opt
│       ├── proc
│       ├── root
│       ├── run
│       ├── sbin -> usr/sbin
│       ├── srv
│       ├── sys
│       ├── tmp
│       ├── usr
│       └── var
├── 886eb412f86d1926714db6031f17791d4c66cb2db0156b115a867378093d48ae
│   └── root
│       ├── bin -> usr/bin
│       ├── boot
│       ├── dev
│       ├── etc
│       ├── home
│       ├── lib -> usr/lib
│       ├── lib32 -> usr/lib32
│       ├── lib64 -> usr/lib64
│       ├── libx32 -> usr/libx32
│       ├── media
│       ├── mnt
│       ├── opt
│       ├── proc
│       ├── root
│       ├── run
│       ├── sbin -> usr/sbin
│       ├── srv
│       ├── sys
│       ├── tmp
│       ├── usr
│       └── var
├── c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547
│   ├── lower-id
│   ├── merged
│   │   ├── bin -> usr/bin
│   │   ├── boot
│   │   ├── dev
│   │   ├── etc
│   │   ├── home
│   │   ├── lib -> usr/lib
│   │   ├── lib32 -> usr/lib32
│   │   ├── lib64 -> usr/lib64
│   │   ├── libx32 -> usr/libx32
│   │   ├── luyunix
│   │   ├── media
│   │   ├── mnt
│   │   ├── opt
│   │   ├── proc
│   │   ├── root
│   │   ├── run
│   │   ├── sbin -> usr/sbin
│   │   ├── srv
│   │   ├── sys
│   │   ├── tmp
│   │   ├── usr
│   │   └── var
│   ├── upper
│   │   ├── dev
│   │   ├── etc
│   │   └── luyunix
│   └── work
│       └── work
├── c71bf48d2d255833d53ada016cef3ad087e6d33fbc02d67a42ada6ba69f63547-init
│   ├── lower-id
│   ├── upper
│   │   ├── dev
│   │   └── etc
│   └── work
│       └── work
├── cf9d7c3cfb138d043defcb902c2fa3bc29f371701ade715974b983958ded4f93
│   └── root
│       ├── bin -> usr/bin
│       ├── boot
│       ├── dev
│       ├── etc
│       ├── home
│       ├── lib -> usr/lib
│       ├── lib32 -> usr/lib32
│       ├── lib64 -> usr/lib64
│       ├── libx32 -> usr/libx32
│       ├── media
│       ├── mnt
│       ├── opt
│       ├── proc
│       ├── root
│       ├── run
│       ├── sbin -> usr/sbin
│       ├── srv
│       ├── sys
│       ├── tmp
│       ├── usr
│       └── var
└── e814ca9180804d1aee91358457f7ef835b20a722c45115503314e8048a778ff9
    └── root
        ├── bin -> usr/bin
        ├── boot
        ├── dev
        ├── etc
        ├── home
        ├── lib -> usr/lib
        ├── lib32 -> usr/lib32
        ├── lib64 -> usr/lib64
        ├── libx32 -> usr/libx32
        ├── media
        ├── mnt
        ├── opt
        ├── proc
        ├── root
        ├── run
        ├── sbin -> usr/sbin
        ├── srv
        ├── sys
        ├── tmp
        ├── usr
        └── var

126 directories, 4 files

我们发现读写层的upper目录下多出了一个luyunix文件,就是刚才在容器中创建的文件

总结:

Overlay的原理就是将一层目录重叠与另一层目录之上,也就是说OverlayFS文件系统只会涉及两个目录,而Docker镜像却可能有许多层,为了解决这种不对应的情况,overlay存储驱动在存储镜像层的时候,会把父镜像中的内容复制到当前层,然后再写入当前层,为了节省存储空间,在复制的过程中,普通文件是采用硬连接的方式链接到父镜像层对应的文件,其它类型的文件或文件夹则是按照原来的内容重新创建。所以上层镜像层拥有其依赖镜像层的所有文件,而最上面镜像层则拥有了整个镜像的文件系统,这也就是为什么镜像层对应的目录中只有一个root文件夹。(简单的说,上层拥有下层所有文件,对于一样的文件,直接硬链接,不一样的文件复制一份然后做出改变)

对于另一种目录来说,文件lower-id用来索引该容器使用的镜像层,upper目录包含了容器层的内容,每当启动一个容器时,会将lower-id指向的镜像层目录以及upper目录联合挂载到merged目录,因此,容器内的视角就是merged目录下的内容。而work目录则是用来完成如copy-up的操作