跳到主要内容

rootfs 与容器技术

回到我们在 Linux 的环境,运行我们的“容器”:

/root/test/container

我们是不是可以像虚拟机中的操作系统一样,在容器中“为非作歹”了呢?

那!当!然!不!行!啦!

1. 为什么需要 rootfs

先忽略 Cgroup 机制。在容器中探索时,不知大家是否发现,/bin,/etc,/var ... 等等目录下的内容,它们与宿主机的目录完全一致。你在这些目录里的更改一样会影响到宿主机。聪明的读者一定发现了,我们创建进程的时候不是指定开启 Mount Namespace 了吗,为什么不生效呢?

即使我们启用了 Mount Namespace,但它需要手动指定挂载时才能生效,我们在代码中指定容器进程中的 /tmp 目录使用空的内存盘挂载,所以我们在容器中访问到的 /tmp 目录是空的,与宿主机不一致的。

至于其他的目录,因为我们也没有指定挂载点变更,它们会直接使用宿主机的目录。但理想状态下,我们会希望,进入在一个新的容器后,它的所有文件都应该尽可能与宿主机隔离,我们才能放心使用它。

实现起来也不算复杂,我们知道 Linux 的目录结构都是从 / 开始的,如果我们制作一份 / 目录,然后在容器进程启动的时候将它切换挂载到容器的 /,就可以解决这个问题。而像这种完整包含根目录的拷贝,我们称呼它:rootfs。

2. 构建 rootfs

构建一个真正可用的 rootfs 的过程其实非常繁琐,我们不需要了解它的细节,我们仅仅需要使用它基本的功能理解 rootfs 即可。

这里我们使用的构建命令如下:

cd /root/test/
mkdir rootfs
# 为rootfs提供初始目录结构和核心依赖库
sudo rpm -ivh --nodeps --root /home/root/test/rootfs https://mirrors.aliyun.com/centos/8/BaseOS/x86_64/os/Packages/centos-release-8.1-1.1911.0.9.el8.x86_64.rpm
# 为rootfs安装vim 工具和gcc编译环境
sudo dnf --installroot=/root/test/rootfs install vim gcc --nogpgcheck

3. 使用 rootfs

在 Linux 环境的 /root/test/ 目录下,已经有一份打包好的 rootfs,我们直接使用它即可。

我们来探查一下它的目录结构:

tree -L 1

发现它与宿主机的/目录的结构一致, 因为它就是我们容器将要使用的/

下面我们让我们的容器使用这个 rootfs,编写一个 helloworld 程序

Tip: 下面我们会使用到 chroot, chroot 是在 unix 系统的一个操作,针对正在运作的软件行程和它的子进程,改变它外显的根目录。一个运行在这个环境下,经由 chroot 设置根目录的程序,它不能够对这个指定根目录之外的文件进行访问动作,不能读取,也不能更改它的内容。Namespace Mount 事实上就是受到 chroot 的启发改进而来的。

cd /root/test/

## 运行容器环境
./container

## chroot切换到rootfs
chroot /root/rootfs /bin/bash

# 进入容器中的home目录,编写代码 helloworld.c并编译执行
cd /home
vim helloworld.c

#include <stdio.h>
int main()
{
// printf() 中字符串需要引号
printf("Hello, World!");
return 0;
}

保存后编译运行

cc helloworld.c -o helloworld
./helloworld

我们将看到终端中显示:

hello, world!

这样我们获得了一个文件系统目录与宿主机隔离的环境,在这里,我们可以放心大胆地操作,不用担心影响到宿主机的环境。

至此,我们获取了一个可以使用 C 语言编译运行应用的容器。

4. 拓展思考

如果 多个项目同时使用这个容器,他们之间如何隔离文件系统呢?

直接拷贝或者重新构建多个 rootfs 似乎可行,但这浪费了空间

如果 容器使用过程中,不小心误删了某个 rootfs 中的关键文件,导致容器被损坏, 怎么办?

让 rootfs 的关键文件设定为只读的状态,似乎可以解决这个问题。

如果我们想与其他人分享我们的容器和容器内的应用,该怎么办呢?

平台相同的情况下,我们把 rootfs 和容器进程文件拷贝一份副本给他们就行了。传输方式?U 盘,网盘,通讯软件好像都可以。

如果…?

5. 小结

本节我们学习了容器场景下的 rootfs 需求的认知和构建使用。需要注意的是,rootfs只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核,因此在容器中运行的内核与宿主机一致,与内核相关的模块是无法隔离的。

结合前两节中 Namespace 和 CGroup 的内容,我们对容器技术的核心概念和原理应该具有了充分的理解。但技术在产品化的过程中,比如拓展思考的部分,仅仅脑补就会遇到无数的问题,我们自己的解决方案似乎问题也很大,所以闭门造车是不行的,容器技术的核心部分将讲到这里,下面的章节就让主角 Docker 正式登场。