容器已风靡全球。 无论您在听到“容器”这个词时想到的是 Kubernetes、Docker、CoreOS、Silverblue 还是 Flatpak,现代应用程序为了方便、安全和可扩展性而在容器中运行已是不争的事实。
然而,容器可能很难理解。 在容器中运行意味着什么? 容器中的进程如何与它们运行的计算机的其余部分交互? 开源不喜欢神秘,因此本文解释了容器技术的后端,正如我关于 Flatpak 的文章解释了一个常见的前端一样。
命名空间
命名空间在编程世界中很常见。 如果您身处计算机世界的高度技术领域,那么您可能见过这样的代码
using namespace std;
或者您可能在 XML 中见过这样的代码
<book xmlns="http://docbook.org/ns/docbook" xml:lang="en">
这类短语为源代码文件中稍后使用的命令提供上下文。 例如,C++ 知道程序员在键入 cout 时意味着什么,唯一的原因是 C++ 知道 cout 命名空间是一个有意义的词。
如果这对您来说太技术性而难以想象,您可能会惊讶地发现,我们在现实生活中也每天都在使用命名空间。 我们不称它们为命名空间,但我们一直都在使用这个概念。 例如,在为大型企业(通常称为“企业”)提供服务的 IT 公司中,“我是 Enterprise 的粉丝”这句话有一种含义,但在科幻小说大会上可能有不同的含义。 问题“它运行的是什么引擎?”在车库中有一种含义,在 Web 开发中则有不同的含义。 我们并非总是在随意对话中声明命名空间,因为我们是人类,我们的大脑可以快速适应以确定上下文,但对于计算机而言,命名空间必须显式声明。
对于容器,命名空间定义了进程“感知”周围正在运行的其他内容的边界。
lsns
您可能没有意识到,但您的 Linux 机器会静默维护特定于给定进程的不同命名空间。 通过使用最新版本的 util-linux 软件包,您可以列出机器上现有的命名空间
$ lsns
NS TYPE NPROCS PID USER COMMAND
4026531835 cgroup 85 1571 seth /usr/lib/systemd/systemd --user
4026531836 pid 85 1571 seth /usr/lib/systemd/systemd --user
4026531837 user 80 1571 seth /usr/lib/systemd/systemd --user
4026532601 user 1 6266 seth /usr/lib64/firefox/firefox [...]
4026532928 net 1 7164 seth /usr/lib64/firefox/firefox [...]
[...]
如果您的 util-linux 版本未提供 lsns 命令,您可以在 /proc 中查看命名空间条目
$ ls /proc/*/ns
1571
6266
7164
[...]
$ ls /proc/6266/ns
ipc net pid user uts [...]
在您的 Linux 机器上运行的每个进程都使用进程 ID (PID) 进行枚举。 每个 PID 都分配有一个命名空间。 同一命名空间中的 PID 可以相互访问,因为它们被编程为在给定的命名空间内运行。 默认情况下,不同命名空间中的 PID 无法相互交互,因为它们在不同的上下文或命名空间中运行。 这就是为什么在一个命名空间下“容器”中运行的进程无法访问其容器外部的信息或在不同容器内部运行的信息。
创建新的命名空间
处理容器的软件的一个常见功能是自动命名空间管理。 启动新的容器化应用程序或环境的人工管理员不必使用 lsns 来检查存在哪些命名空间,然后手动创建一个新的命名空间; 使用 PID 命名空间的软件借助 Linux 内核自动执行此操作。 但是,您可以手动模拟该过程,以更好地了解幕后发生的事情。
首先,您需要确定计算机上未运行的进程。 在此示例中,我将使用 Z shell (Zsh),因为我在我的机器上运行 Bash shell。 如果您在计算机上运行 Zsh,请使用 Bash 或 tcsh 或您当前未运行的其他 shell。 目标是找到您可以证明未运行的程序。 您可以使用 pidof 命令证明某程序未运行,该命令查询您的系统以发现您命名的任何应用程序的 PID
$ pidof zsh
$ sudo pidof zsh
只要没有返回 PID,您查询的应用程序就没有运行。
Unshare
unshare 命令在与其父进程不共享的命名空间中运行程序。 有许多类型的命名空间可用,因此请阅读 unshare 手册页以获取所有可用选项。
要为您的测试命令创建新的命名空间
$ sudo unshare --fork --pid --mount-proc zsh
%
由于 Zsh 是一个交互式 shell,因此它会在启动时方便地将您带入其命名空间。 并非所有进程都这样做,因为某些进程在后台运行,让您在其本机命名空间中的提示符下。 只要您保持在 Zsh 会话中,您就可以通过查看新派生进程的 PID 来看到您已离开通常的命名空间
% pidof zsh
pid 1
如果您对 Linux 进程 ID 有所了解,那么您就会知道 PID 1 始终是保留的,主要是由于引导过程的性质,用于初始化应用程序(Slackware、Devuan 之外的大多数发行版以及 Arch 的某些自定义安装上的 systemd)。 Zsh 或任何不是引导初始化应用程序的应用程序几乎不可能成为 PID 1(因为没有 init 系统,计算机将不知道如何启动)。 然而,就您的 shell 在此演示中所知,Zsh 占据了 PID 1 插槽。
尽管您的 shell 现在告诉您的内容,但系统上的 PID 1 未被替换。 在计算机上打开第二个终端或终端选项卡,然后查看 PID 1
$ ps 1
init
然后找到 Zsh 的 PID
$ pidof zsh
7723
正如您所看到的,您的“主机”系统看到了全局,并了解 Zsh 实际上是作为一些编号较高的 PID 运行的(除非巧合,否则它可能不是您计算机上的 7723)。 Zsh 认为自己是 PID 1 只是因为它的范围仅限于(或包含在)其命名空间内。 一旦您将进程派生到其自己的命名空间中,其子进程将从 1 开始编号,但仅在该命名空间内。
命名空间以及 cgroups 等其他技术构成了容器化的基础。 了解命名空间存在于主机环境的更广泛命名空间的上下文中(在此演示中,它是您的计算机,但在现实世界中,主机通常是服务器或混合云)可以帮助您理解容器化应用程序的行为方式和原因。 例如,运行 Wordpress 博客的容器“不”知道它不是在容器中运行; 它知道它可以访问内核和一些 RAM 以及您提供的任何配置文件,但它可能无法访问您的主目录或您未明确授予其访问权限的任何目录。 此外,该博客软件中的失控进程不会影响系统上的任何其他进程,因为就它所知,PID“树”仅追溯到 1,而 1 是它正在运行的容器。
容器是强大的 Linux 功能,并且每天都变得越来越流行。 现在您已经了解了它们的工作原理,请尝试探索 Kubernetes、Silverblue 或 Flatpak 等容器技术,看看您可以使用容器化应用程序做什么。 容器是 Linux,所以启动它们,仔细检查它们,并在实践中学习。
6 条评论