跳转至

Lab 4

容器是近年来非常热门的一个概念。它通过操作系统内核提供的隔离技术,实现轻量级的虚拟化环境。目前,它在软件的开发、部署等方面有着非常广泛的应用。

最常见的几种容器,如 Docker,都充分利用了多种 Linux 的特性,因此毫不夸张地说,容器是属于 Linux 的。本实验中我们将动手编写一个容器实现,理解 Linux 中的容器技术。

本实验由于与操作系统深度结合,因此无法在 vlab 平台上进行,请同学们在自己的虚拟机上完成。以下资源可以使用:

准备工作

你需要一个 C 编译器和最基本的 C 语言库,相信你已经在前几个实验中使用熟练了,因此略过。

Linux 容器的典型实现使用了至少五种特性:命名空间(namespaces)pivot_root能力(capabilities)SecComp(Secure Computing)Cgroup(Control Groups)

其中,命名空间使用 clone(2) 和 unshare(2) 的 flags 来完成,cgroup 使用文件系统的 API(即通过创建删除目录和读写文件的方式与 cgroup 系统交互),而 seccomp 和 capabilities 使用原生的系统调用(该系统调用非常复杂,因此我们使用相关库来完成这两项功能)。

在阅读本次实验文档前,你需要理解如 fork(2) 这样的表达方式。你可以参考 Linux 用户协会的 Linux 101 讲义附录中的相关章节来进行初步的了解。

一个重要的提示是,实验二中学习使用的 strace 工具在本实验中非常有帮助。

实验内容

在你准备好 rootfs 之后,可以编译运行该页面底部的示例程序体验“光杆容器”。下面的各项任务是对其进行修改升级(你也可以选择自己从头编写一个)。由于下面几个项目中有互相依赖的任务,或者是进行功能上的限制,因此请尽量按顺序完成。

实验要求

请按照以下目录结构组织你的 GitHub 仓库:

(Git)                     // Git 仓库目录
├── lab4                  // 实验四根目录
│   ├── *.c               // 你的容器源代码
│   ├── *.cpp             // 你的容器源代码
│   ├── *.go              // 你的容器源代码
│   ├── *.rs              // 你的容器源代码
│   ├── Makefile          // (可选)你提供的 Makefile
│   └── README.md         // 简要的说明
├── .gitignore            // 这两个文件在实验一的文档里说过了
└── README.md

本次实验满分为 10 分,你需要完成:

  • 使用 Linux 命名空间隔离子进程的特性(不要求隔离网络命名空间、用户命名空间与时间命名空间)(1 分)
  • 正确挂载容器内的 /proc, /sys, /tmp, /dev,并在 /dev 下创建必要的节点(3 分)
  • 使用 pivot_root(2) 替代 chroot(2) 完成容器内根文件系统的切换,并从主机上隐藏容器的挂载点(4 分)
  • 为容器中的进程移除一些能力(drop capabilities)(2 分)
  • 使用 SecComp 限制容器中能够进行的系统调用(2 分)
  • 使用 cgroup 限制容器中的系统资源使用,并在容器中按要求挂载三个 cgroup 控制器(2 分)
  • 完成思考题(2 分)

本实验可以使用 C / C++ / Go / Rust 语言完成,推荐使用 C 或 C++ 语言。使用其他编程语言前请询问助教。方便起见,你的程序应当支持下面的命令行参数:

./lab4 <rootfs> <command> [args...]

argv[1] 表示 rootfs 的路径(没有 / 结尾),argv[2] 开始表示作为容器中的 PID 1 运行的程序及参数,可以假设 argc ≥ 3(在 argc < 3 时你可以选择直接报错,但请尽量避免异常退出,如数组越界等)。

除了运行容器中的第一个程序(即容器中的 PID 1,应由命令行给出)之外,你的程序不应该调用其他程序来完成任何功能。你可以将「调用其他程序」理解为「除了运行目标命令之外的 execve 系统调用」。请注意,一些库函数(如 system(3) 和 popen(3) 等)都会调用额外的程序,这些调用过程通常包含了 fork+execve 的系统调用。

与实验二和三相似,本实验不要求实验报告,但是推荐简单描述你的实现,并对你使用的「奇技淫巧」简单介绍,以免对助教造成误会等。

本次实验满分为 10 分,但根据以上内容,实际可获得的分数上限为 16 分,超出 10 分的部分将减半计入实验总分。

例如,若你根据以上标准获得了 16 分,那么本次实验计入总分的分数为 13 分,这将等价于本课程总评成绩的 6.5 分。

思考题

  1. 用于限制进程能够进行的系统调用的 seccomp 模块实际使用的系统调用是哪个?用于控制进程能力的 capabilities 实际使用的系统调用是哪个?尝试说明为什么本文最上面认为「该系统调用非常复杂」。
  2. 当你用 cgroup 限制了容器中的 CPU 与内存等资源后,容器中的所有进程都不能够超额使用资源,但是诸如 htop 等「任务管理器」类的工具仍然会显示主机上的全部 CPU 和内存(尽管无法使用)。查找资料,说明原因,尝试提出一种解决方案,使任务管理器一类的程序能够正确显示被限制后的可用 CPU 和内存(不要求实现)。