最近在写一个小工具,依赖的库比较多,但是这些依赖都不常用,不想为了开发而装在常用的机器上。想用以前用 chroot 搞的一个开发环境,可惜更新源验证总出问题,搜索了几个方案后还是没解决。我只是想搞个隔离的测试环境,不需要限制资源,docker 又庞大配置又麻烦(其实根本原因是我讨厌 golang),又不想重新配置一份环境,于是搜了下 docker 相关的底层技术,最后发现 overlayfs+chroot 就能完美解决我的需求,在这里记录一下,内核版本是 5.6。
overlayfs 介绍
顾名思义,overlayfs 就是把多个目录组合在一起形成一个新的文件系统,在 2014 年被合并进 kernel v3.18。先来看下使用时的命令行参数:
mount -t overlay overlay -o lowerdir=<lowerdir1>:<lowerdir2>:<...>,upperdir=<upperdir>,workdir=<workdir> <mountpoint>
大概介绍下这里的三个参数:
lowerdir
:只读目录,多个目录用 “:” 分隔。upperdir
:可读可写目录。如果upperdir
和lowerdir
中有同名的文件,会优先展示upperdir
中的文件(换言之就是lowerdir
中的文件被屏蔽了)。workdir
:必须是空目录,作用未知。
例如下面的例子:
mkdir low1 low2 low3 upper work overlaydir
touch low1/1.txt low2/2.txt low3/3.txt
mount -t overlay overlay -o lowerdir=./low1:./low2:./low3,upperdir=./upper,workdir=./work overlaydir
执行完上面的命令后:
# ls overlaydir
1.txt 2.txt 3.txt
可以看到三个 lower 目录下的内容被合并到 overlaydir
中。如果这时在 overlaydir
中创建一个新文件:
touch overlaydir/4.txt
用 ls
查看三个 lower 目录都没有改变,但是在 upper
中出现了新建的文件。
接着修改下 3.txt
的内容:
echo "hello, world!" > overlaydir/3.txt
cat overlaydir/3.txt
可以发现的确写入了内容,但是 cat low3/3.txt
可以发现这个文件还是空的,而 upper
目录下多了个 3.txt
,里面的内容正是我们刚才写入的内容。
在 upper
中新建一个文件(根据 参考资料 [1],在目录被挂载的时候修改被挂载的目录可能导致不可预知的问题,这里只是做个实验):
touch upper/5.txt
新建的文件也出现在了 overlaydir
中。接着往新建的文件中写入内容:
echo "ouonline" > overlaydir/5.txt
通过查看可以发现在 upper/5.txt
中保存了我们写入的内容。
overlayfs 的简单用法就这样,知道这些已经可以很好地满足我的需求了,实现细节就没有去深究。
overlayfs+chroot
具体到我的需求,就是先把宿主的环境挂载为只读(不污染宿主环境):
# 创建工作用的目录
mkdir upper work overlaydir
# 把根目录挂载为只读,overlaydir 是最终的挂载点
mount -t overlay overlay -o lowerdir=/,upperdir=./upper,workdir=./work overlaydir
这里我直接挂载了根目录(我机器上就两个分区,一个根目录一个 /home
)。这里虽然直接把根目录挂载了,但是进入 overlaydir/home
是看不到 /home
目录下的内容的。经过测试,使用上面的选项是看不到挂载点下挂载的其它分区的,如果需要共享的话可以单独挂载到别的目录或者拷贝到 upper
目录等方法。如果根目录下的 /var
或者 /usr
等挂载在其它分区的话可能还要拷贝到 upper
才能用。
准备好环境后就切换到 overlaydir
:
chroot overlaydir /bin/bash
# 这里是 chroot 到 overlaydir 之后的一些环境初始化
mount -t devpts devpts /dev/pts
mount -t proc proc /proc
mount -t sysfs sysfs /sys
然后像宿主一样正常使用就可以了。做完实验后
umount /sys
umount /proc
umount /dev/pts
exit
umount overlaydir
然后把 upper
中的内容都删掉,重新挂载后就跟实验前的环境一样了。当然也可以新建一个 upperdir 然后挂载到另一个目录中,就可以同时拥有多个不同的环境了,但是存储空间不会线性增长,因为只有被修改过的内容会保存下来。同时内核和其它基础工具都是用的宿主系统的环境,不需要单独维护。