1、介绍
OverlayFS是一种目前广泛应用的层次文件系统,openwrt、树莓派、docker等上都有应用。它于2014年被合并到Linux3.18内核中,顾名思义其主要特性就是“覆盖”,OverlayFS将单个Linux主机上的两个目录分层,并将它们显示为单个目录。这些目录称为层,OverlayFS的下层目录为"lowerdir"上层目录为"upperdir",合并目录为"merged",层次结构如图:
简单来说,overlay 文件系统(OverlayFS)是一种联合文件系统,它能把多个目录(层)叠加在一起,对外呈现为一个统一的目录。Docker 正是利用这个特性,实现了镜像的分层存储和容器的写时复制机制。
OverlayFS 的核心概念非常清晰,主要由三个角色组成:
| 术语 | 角色 | 在 Docker 中的对应物 | 主要特性 |
|---|---|---|---|
lowerdir | 只读层 | Docker 镜像层 | 可以被多个容器共享,永不修改。 |
upperdir | 读写层 | 特定于某个容器的可写层 | 容器运行时的所有修改(增、删、改)都发生在这里。 |
merged | 联合视图 | 容器内看到的文件系统 | upperdir和lowerdir合并后的最终视图。 |
工作原理:当你启动一个容器时,Docker 会将镜像的各只读层作为lowerdir,并为该容器创建一个空的upperdir(可写层),然后将它们联合挂载到一个merged目录。容器内所有进程看到的就是这个merged目录。任何对文件的修改,都会以“写时复制”的方式写入到upperdir,而不会改动只读的镜像层。
读文件:优先从
upperdir读,如果文件不在upperdir,则从lowerdir读。修改文件:首次写入时,OverlayFS 会将文件从
lowerdir复制到upperdir,然后再进行修改。这个过程就是著名的copy_up操作。删除文件:由于
lowerdir是只读的,无法真正删除。OverlayFS 会在upperdir中创建一个特殊的"whiteout"文件来“屏蔽”掉lowerdir中的原文件,从merged视图看,这个文件就像消失了一样。
overlay具有:
上下合并、同名覆盖、写时拷贝COW等特点,具体如下:
Lowerdir与Upperdir同名文件在 Merged中显示的只有Upperdir中的同名文件,不同名文件会合并在Merged中显示
修改文件或目录策略:
(1)若文件存在于Upperdir,则直接修改Upperdir中的文件;
(2)若文件只存在于Lowerdir,则先将文件从Lowerdir中拷贝至Upperdir再进行修改;
在Merged层创建文件或目录,会直接在Upperdir层创建
在Merged层删除文件策略:
(1)若文件只存在Upperdir层,则直接在Upprdir层删除文件;
(2)若文件存在Lowerdir层,则在Upperdir层创建一个同名的任何用户都没有任何权限,大小为0的字符设备;
基于以上策略,Lowerdir层内容都不会被修改,使得在OverlayFS中,Lower可以是只读的,而Upper则需要是可读写的文件系统。
2、测试
我们先确认一下当前环境是否在使用overlay2
[root@Anolis2 ~]# docker info | grep "Storage Driver"
Storage Driver: overlay2
Docker 的所有镜像和容器层数据都存储在/var/lib/docker/overlay2/目录下。我们可以进去看看。
[root@Anolis2 ~]# cd /var/lib/docker/overlay2/
[root@Anolis2 overlay2]# ls
0061705cc93a16d0d3acb45e179a34ca776b817a9d774739f9c627fcc9d6381b 8b494bc6f1be68eec06542faee731549cfe23064e0ecf8514343449119aa1d28
00eb0e19b0145619dfaea20d532e7e0030d416e081115f4ef6a737305dff76f5 8c7852b67aadcdd084a1b51612073ab10f7b6b65657c072973a6d8f1f7d8f2d3
03e69362ce81e7fcdc2d0ffe25bfaef82db49074f666e8421632981bbe325902 8cf0ec0bbaaed40a7bcecc59c8d5d50477cc91f42a4fbe457017b84cc1f41385
04f2ef2fed444c87ba43b14ad40ce0aa2d89ecbd69f13af512db9a0225dca935 8d2cd34cbd396f41bd599acaae4940b6c7a39b7f847088f668fb43ba18f17693
你会看到很多由长串随机ID命名的目录和一个特殊的l目录
长ID目录:每个目录代表一个镜像层或容器层。
l目录:包含了指向这些长ID目录的短名称符号链接。这是为了解决在mount命令参数中路径过长的问题
为了更好地理解,我们在/tmp下手动创建一个 OverlayFS 挂载,看看它到底如何工作
1.创建目录
[root@Anolis2 overlay2]# cd /tmp/
[root@Anolis2 tmp]# mkdir overlay-demo && cd overlay-demo
[root@Anolis2 overlay-demo]# mkdir lower1 lower2 upper work merged
2.在两个 lower 目录里创建不同的文件
[root@Anolis2 overlay-demo]# echo "I am from lower1" > lower1/lower1.txt
[root@Anolis2 overlay-demo]# echo "I am from lower2" > lower2/lower2.txt
3.在 lower2 中创建一个和 lower1 同名的文件,用来演示“覆盖”
[root@Anolis2 overlay-demo]# echo "I am the original in lower2" > lower2/conflict.txt
4.使用 mount 命令进行联合挂载
[root@Anolis2 overlay-demo]# mount -t overlay overlay \
> -o lowerdir=./lower2:./lower1,upperdir=./upper,workdir=./work \
> ./merged
5.查看合并后的目录
输出: conflict.txt lower1.txt lower2.txt (三个文件都在)
[root@Anolis2 overlay-demo]# ls ./merged
conflict.txt lower1.txt lower2.txt查看文件内容,验证同名文件的优先级 (lower2 会覆盖 lower1)
[root@Anolis2 overlay-demo]# cat ./merged/conflict.txt
I am the original in lower2
6.在 merged 目录下新建和修改文件
[root@Anolis2 overlay-demo]# rm ./merged/lower1.txt
rm:是否删除普通文件 './merged/lower1.txt'?y
7.查看
[root@Anolis2 overlay-demo]# ls -l ./upper/
总用量 4
c--------- 2 root root 0, 0 3月 17 00:46 lower1.txt
-rw-r--r-- 1 root root 9 3月 17 00:46 new.txt
输出: new.txt lower1.txt[root@Anolis2 overlay-demo]# file ./upper/lower1.txt
./upper/lower1.txt: character special (0/0)
输出: ./upper/lower1.txt: character special (0/0) # 这就是 whiteout 文件
lower1和lower2的内容被合并。当文件在多个
lowerdir中存在时,最左边(优先级最高)的会生效。在
merged中新建和修改的文件,都出现在upper目录中。在
merged中删除一个来自lowerdir的文件,会在upper目录中留下一个 "whiteout" 文件作为标记。
3、容器测试
运行一个容器
[root@Anolis2 ~]# docker run -d --name overlay-test nginx
9ce1941408e1cb53f476916a4725a780794c02c74858ae5d6149b078129826a6
[root@Anolis2 ~]# docker inspect overlay-test | grep -A 10 "GraphDriver"
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/c540679f9649e6e30883a9eec04618700661d7bf5bed6e681a1ae23b7384e0c0-init/diff:/var/lib/docker/overlay2/6d13487122c3d9d703ae2fcc6fe94515c794132a5b852721b0cb5c40fb98ff67/diff:/var/lib/docker/overlay2/8cf0ec0bbaaed40a7bcecc59c8d5d50477cc91f42a4fbe457017b84cc1f41385/diff:/var/lib/docker/overlay2/00eb0e19b0145619dfaea20d532e7e0030d416e081115f4ef6a737305dff76f5/diff:/var/lib/docker/overlay2/faa1271b8ef17994d751582f40a4feec7bca55be6e6938e0ab66cf4e4fbdebf5/diff:/var/lib/docker/overlay2/869ef440029c2065ced2105e51d9a448187f466eb649d2f0a8038a97c0574933/diff:/var/lib/docker/overlay2/97f4a8e3af893b4bfa185e8f9ee66a6da2d83c1cc1b4694fdab73443c8c1ab81/diff:/var/lib/docker/overlay2/17d522a5713fe3d6cb73ef19de4455f6ad08f2ad0e91eec612fcd166a32427a7/diff",
"MergedDir": "/var/lib/docker/overlay2/c540679f9649e6e30883a9eec04618700661d7bf5bed6e681a1ae23b7384e0c0/merged",
"UpperDir": "/var/lib/docker/overlay2/c540679f9649e6e30883a9eec04618700661d7bf5bed6e681a1ae23b7384e0c0/diff",
"WorkDir": "/var/lib/docker/overlay2/c540679f9649e6e30883a9eec04618700661d7bf5bed6e681a1ae23b7384e0c0/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {