rootfs

本贴最后更新于 205 天前,其中的信息可能已经时移俗易

1. rootfs

rootfs​: 进程运行后的根目录,又叫根文件系统。

操作系统 rootfs 包含了操作系统运行所需要的文件和目录。

对于镜像而言,应用以及它运行所需的所有依赖都被打包到了 rootfs。

容器进程运行后的根目录,就叫做 rootfs。这个根文件系统,一般来说包括下面这些文件/bin,/etc,/proc 等
image

运行容器时执行的/bin/bash 其实是容器的 rootfs 中/bin 目录下的 bash 可执行文件,和宿主机的 rootfs 中/bin 目录下的 bash 文件没关系。

2. chroot

chroot(change root)​: 改变进程的根目录,使它不能访问该目录之外的其他文件。而容器实际上就是一个特殊的进程,所以我们就可以使用 chroot 限制容器的文件访问,使得每个容器都有一个自己的文件系统。

chroot [OPTION] NEWROOT [COMMAND [ARG]...]

  • NEWROOT:表示切换到的新的 root 目录

  • COMMAND:表示切换 root 目录后执行的命令

现在写一个 go 项目输出/目录下的所有文件

func main(){ fds,err := os.ReadDir("/") if err != nil{ panic(err) } for _,fd:=range fds{ if fd.IsDir(){ fmt.Print(fd.Name()+" ") } } fmt.Println() } // 打包 go build -o server main.go //将server复制到/opt/chroot下 cp server /opt/chroot (base) root@ubuntu:/opt/chroot# ls -la total 2040 drwxr-xr-x 2 root root 4096 Aug 3 15:43 . drwxr-xr-x 6 root root 4096 Aug 3 15:43 .. -rwxr-xr-x 1 root root 2080126 Aug 3 15:37 server //执行server,将显示系统本身根目录下的目录 (base) root@ubuntu:/opt/chroot# ./server app boot dev etc home lost+found media mnt opt proc project root run snap srv sys tmp usr var

创建一个目录 go-server,作为 server 命令的新的 root 目录,并在 go-server 目录下创建几个目录以供 server 命令使用。

(base) root@ubuntu:/opt/chroot# mkdir -p go-server/{test1,test2,test3,test4} (base) root@ubuntu:/opt/chroot# ls go-server/ test1 test2 test3 test4

将 go-server 目录作为 server 命令来执行。

// 将server命令移到go-server的目录或者子目录下 (base) root@ubuntu:/opt/chroot# mv server go-server/ // 查看目录结构 (base) root@ubuntu:/opt/chroot# tree go-server/ go-server/ ├── server ├── test1 ├── test2 ├── test3 └── test4 // 先单独执行一下 server 命令,会输出系统根目录下的目录 (base) root@ubuntu:/opt/chroot# ./go-server/server app boot dev etc home lost+found media mnt opt proc project root run snap srv sys tmp usr var // 添加新的根目录/go-server,执行 (base) root@ubuntu:/opt/chroot# chroot go-server/ /server test1 test2 test3 test4 //更换根目录,再次执行 (base) root@ubuntu:/opt# ls -la chroot/ total 16 drwxr-xr-x 4 root root 4096 Aug 3 16:06 . drwxr-xr-x 6 root root 4096 Aug 3 15:43 .. drwxr-xr-x 6 root root 4096 Aug 3 15:53 go-server drwxr-xr-x 5 root root 4096 Aug 3 16:06 ls_test (base) root@ubuntu:/opt# chroot chroot/ /go-server/server go-server ls_test

其实我们还可以给系统本身的命令新的 root 目录,这里以 ls 命令为例

// 创建所需的目录 (base) root@ubuntu:/opt/chroot# mkdir -p ls_test/{bin,lib,lib64} // 将ls命令复制到ls_test/bin目录中 (base) root@ubuntu:/opt/chroot# cp /bin/ls ls_test/bin/ // 查看ls的所有依赖 (base) root@ubuntu:/opt/chroot# ldd /bin/ls linux-vdso.so.1 (0x00007ffe396bc000) libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fc398b87000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc398995000) libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007fc398904000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc3988fe000) /lib64/ld-linux-x86-64.so.2 (0x00007fc398be1000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc3988db000) // 将所有依赖也放入到对应的目录中 (base) root@ubuntu:/opt/chroot# cp /lib/x86_64-linux-gnu/libselinux.so.1 ls_test/lib (base) root@ubuntu:/opt/chroot# cp /lib/x86_64-linux-gnu/libc.so.6 ls_test/lib (base) root@ubuntu:/opt/chroot# cp /lib/x86_64-linux-gnu/libpcre2-8.so.0 ls_test/lib (base) root@ubuntu:/opt/chroot# cp /lib/x86_64-linux-gnu/libdl.so.2 ls_test/lib (base) root@ubuntu:/opt/chroot# cp /lib64/ld-linux-x86-64.so.2 ls_test/lib64/ (base) root@ubuntu:/opt/chroot# cp /lib/x86_64-linux-gnu/libpthread.so.0 ls_test/lib

给 ls 命令添加新的 root 目录

// 先单独执行一下命令,显示的是我们系统根目录下的文件 (base) root@ubuntu:/opt/chroot# ./ls_test/bin/ls / app lib mnt sbin usr wget-log.12 wget-log.18 wget-log.5 bin lib32 opt snap var wget-log.13 wget-log.19 wget-log.6 boot lib64 proc srv wget-log wget-log.14 wget-log.2 wget-log.7 dev libx32 project swap.img wget-log.1 wget-log.15 wget-log.20 wget-log.8 etc lost+found root sys wget-log.10 wget-log.16 wget-log.3 wget-log.9 home media run tmp wget-log.11 wget-log.17 wget-log.4 //给ls命令添加新的root目录 (base) root@ubuntu:/opt/chroot# chroot ./ls_test/ /bin/ls / bin lib lib64

现在我们使用一下 docker 执行容器时,经常用到的/bin/bash 命令

// 创建一个新的root目录供bash命令使用 (base) root@ubuntu:/opt/chroot# mkdir new_rootfs // 将上面的ls命令都复制到新的目录下 (base) root@ubuntu:/opt/chroot# cp -R ls_test/* new_rootfs/ */ // 将 bash 命令复制到 new_rootfs/bin/目录下 (base) root@ubuntu:/opt/chroot# cp /bin/bash new_rootfs/bin/ // 查看bash命令依赖的库 (base) root@ubuntu:/opt/chroot# ldd /bin/bash linux-vdso.so.1 (0x00007ffc9d9b8000) libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007f62405ed000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f62405e7000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f62403f5000) /lib64/ld-linux-x86-64.so.2 (0x00007f6240753000) // 将依赖库都复制到目录下 (base) root@ubuntu:/opt/chroot# cp /lib/x86_64-linux-gnu/libtinfo.so.6 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 new_rootfs/lib/ (base) root@ubuntu:/opt/chroot# cp /lib64/ld-linux-x86-64.so.2 new_rootfs/lib64/ // 目录结构 (base) root@ubuntu:/opt/chroot# tree new_rootfs/ new_rootfs/ ├── bin │ ├── bash │ └── ls ├── lib │ ├── libc.so.6 │ ├── libdl.so.2 │ ├── libpcre2-8.so.0 │ ├── libpthread.so.0 │ ├── libselinux.so.1 │ └── libtinfo.so.6 └── lib64 └── ld-linux-x86-64.so.2 3 directories, 9 files

给 bash 命令新的 root 目录

(base) root@ubuntu:/opt/chroot# chroot new_rootfs/ /bin/bash bash-5.0# ls -la ./* */ //目录结构 ./bin: total 1304 drwxr-xr-x 2 0 0 4096 Aug 3 16:44 . drwxr-xr-x 5 0 0 4096 Aug 3 16:44 .. -rwxr-xr-x 1 0 0 1183448 Aug 3 16:44 bash -rwxr-xr-x 1 0 0 142144 Aug 3 16:44 ls ./lib: total 3092 drwxr-xr-x 2 0 0 4096 Aug 3 16:46 . drwxr-xr-x 5 0 0 4096 Aug 3 16:44 .. -rwxr-xr-x 1 0 0 2029592 Aug 3 16:46 libc.so.6 -rw-r--r-- 1 0 0 18848 Aug 3 16:46 libdl.so.2 -rw-r--r-- 1 0 0 588488 Aug 3 16:44 libpcre2-8.so.0 -rwxr-xr-x 1 0 0 157224 Aug 3 16:44 libpthread.so.0 -rw-r--r-- 1 0 0 163200 Aug 3 16:44 libselinux.so.1 -rw-r--r-- 1 0 0 192032 Aug 3 16:46 libtinfo.so.6 ./lib64: total 196 drwxr-xr-x 2 0 0 4096 Aug 3 16:44 . drwxr-xr-x 5 0 0 4096 Aug 3 16:44 .. -rwxr-xr-x 1 0 0 191504 Aug 3 16:47 ld-linux-x86-64.so.2 // 使用 cat 命令试一下 bash-5.0# cat bin/ls bash: cat: command not found //发现并不行,这是因为我们在这个新的rootfs中只添加了ls命令 // 但是我们还可以使用bash的内置命令,`cd`、`echo`、`export` 等 bash-5.0# cd lib bash-5.0# ls libc.so.6 libdl.so.2 libpcre2-8.so.0 libpthread.so.0 libselinux.so.1 libtinfo.so.6 bash-5.0# echo helloworld helloworld // 当使用cd / 时也会到我们新的root目录下

其实到这里我们就大概明白 docker 是怎么调用 /bin/bash 命令了,应该就是 docker exec .. /bin/bash 时会使用 chroot 去为 docker 的进程中的/bin/bash 开一个新的 root 目录。

3. busybox

busybox​: 是一个集成了一百多个最常用 linux 命令和工具的软件,甚至还集成了一个 http 服务器和一个 telnet 服务器,但是占用储存很小。

像上面出现的问题,因为没有加入 cat 命令,然后 cat 就无法使用,那我们需要将所有的基础命令都一个一个加进来的话,一是很麻烦,二是占用内存很大,所以可以使用 busybox 实现 linux 基础命令的使用

获取 busybox 的方法:

  1. 官网下载 busybox 的包,然后手动进行编译。
  2. docker 运行 busybox 容器,并将 busybox 中的根目录复制出来。

我这里使用第二种:

// 创建busybox目录 (base) root@ubuntu:/opt/chroot# mkdir busybox/ // 启动busybox容器 (base) root@ubuntu:/opt/chroot# docker run -d --rm busybox sh -c "sleep 1000" a3b7bb10695d3263fdc919745949537c02e62b1e5b4dfd91306869acecbd05d1 // 将容器中的根目录复制出来 (base) root@ubuntu:/opt/chroot# docker cp sad_maxwell:/bin /opt/chroot/busybox/ Successfully copied 1.47MB to /opt/chroot/busybox/

我们想在对比一下所占储存的大小

// 系统目录:bin目录占用393M,lib目录占用1.8G (base) root@ubuntu:/opt/chroot/busybox# du -sh /usr/* */ 393M /usr/bin 4.0K /usr/games 23M /usr/include 1.8G /usr/lib 4.0K /usr/lib32 4.0K /usr/lib64 140M /usr/libexec 4.0K /usr/libx32 254M /usr/local 33M /usr/sbin 225M /usr/share 264M /usr/src // busybox目录:就只占用了1.2M (base) root@ubuntu:/opt/chroot# du -sh busybox/* */ 1.2M busybox/bin // 当然系统中肯定有很多其他的命令,但是我们使用容器时,用到的命令,一般就是最基础的那些linux命令,所以这也是为什么容器中都会使用busybox中的命令

使用 busybox 添加一个创建一个新的 rootfs。

(base) root@ubuntu:/opt/chroot# chroot busybox/ /bin/sh / # ls -la total 20 drwxr-xr-x 3 0 0 4096 Aug 3 19:25 . drwxr-xr-x 3 0 0 4096 Aug 3 19:25 .. drwxr-xr-x 2 0 0 12288 Dec 29 2021 bin / # veee /bin/sh: eee: not found / # echo 'hello world' > hello / # ls -la total 24 drwxr-xr-x 3 0 0 4096 Aug 3 19:34 . drwxr-xr-x 3 0 0 4096 Aug 3 19:34 .. drwxr-xr-x 2 0 0 12288 Dec 29 2021 bin -rw-r--r-- 1 0 0 12 Aug 3 19:34 hello / # cat hello hello world / # vi hello / # cat hello hello world hello vi / # exit

在这里我们就可以完整的使用 linux 最基本的命令了。

4. 检测隔离性

将宿主机中的进程挂载到新的 rootfs 中

// 创建一个proc目录 / # mkdir proc // 目录中没有任何文件 / # ls -la proc/ total 8 drwxr-xr-x 2 0 0 4096 Aug 4 08:25 . drwxr-xr-x 4 0 0 4096 Aug 4 08:25 .. // 无法查看任何进程 / # ps -ef PID USER TIME COMMAND // 将系统的进程挂载到新的rootfs下 / # mount -t proc proc /proc/ // 查看 /proc文件夹 是有进程文件的 / # ls /proc/ | wc -l 347 //查看进程 / # ps -ef | tail 479750 0 0:00 tail 1339388 0 0:05 /lib/systemd/systemd --user 1339397 0 0:00 (sd-pam) 1370493 0 0:00 /root/.vscode-server/code-f1b07bd25dfad64b0167beb15359ae573aecd2cc command-shell --cli-data-dir /root/.vscode-server/cli --on-port --require-token 87f4bc72fac6 1370528 0 0:00 /bin/sh 1497443 0 0:00 /root/.vscode-server/code-f1b07bd25dfad64b0167beb15359ae573aecd2cc command-shell --cli-data-dir /root/.vscode-server/cli --on-port --require-token 9e0a5eb6aac7 1497478 0 0:00 /bin/sh 2246221 0 0:02 kubectl exec -it -n jenkins godemo-131-806d4-h13fx-nz0lq -c docker sh 2326527 111 10:54 /usr/bin/fwupdmgr refresh 3931508 0 16:16 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

现在检查一下隔离性

// 在宿主机中启动一个进程 (base) root@ubuntu:/opt/chroot# sleep 1000 & [1] 480012 // 查看 (base) root@ubuntu:/opt/chroot# ps -ef | grep sleep root 479780 476214 0 08:28 ? 00:00:00 sleep 180 root 480012 479830 0 08:31 pts/1 00:00:00 sleep 1000 root 480088 479830 0 08:31 pts/1 00:00:00 grep --color=auto sleep // 进入新的rootfs下,可以看到是有这个进程的 / # ps -ef | grep sleep 480012 0 0:00 sleep 1000 480111 0 0:00 sleep 180 480169 0 0:00 grep sleep //现在将进程kill掉 / # kill -9 480012 // 回到宿主机总查看,sleep 1000 的进程消失 (base) root@ubuntu:/opt/chroot# ps -ef | grep sleep [1]+ Killed sleep 1000 root 480111 476214 0 08:31 ? 00:00:00 sleep 180 root 480277 480273 0 08:33 ? 00:00:00 sleep 1 root 480280 479830 0 08:33 pts/1 00:00:00 grep --color=auto sleep

所以我们看到进程并没有做到隔离,这在容器中是不一样的。

5. 小结

chroot​: 改变进程的根目录,是他不能访问该目录之外的其他目录。

rootfs​: 操作系统或者镜像运行的根目录,可以由 chroot 进行切换。

busybox​: 可以做小的 rootfs。

上面也只是实现了最基本的文件隔离,但是进程这些并没有进行隔离。

  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    951 引用 • 943 回帖

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...