rootfs

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 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    927 引用 • 937 回帖

相关帖子

回帖

欢迎来到这里!

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

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