安装

安装

我试图了解 Linux 容器的内部工作原理。我学到的一件事是,您可以通过向 shell 传递特殊标志来将其容器化。标志中的一个用于 PID。

然而,这并没有给ps命令带来预期的效果,因为它仍然显示来自主机和容器的所有进程。这可能是因为从目录ps读取。/proc

我见过的显示仅在容器上运行的进程的方法之一ps是通过 chroot 到假根文件系统(仅包含操作系统的目录/实用程序),然后将/proc主机的目录挂载到假fs的proc目录。

我只是不明白为什么这实际上有效。为什么将/proc目录挂载到另一个挂载点会使其表现得像容器化一样。

为什么像 Docker 这样的容器需要一个假根文件系统才能正常工作?

我错过了什么吗?

我描述的技术来自这个视频在 DockerCon 上。

我已将链接设置为该方法完成的确切时间。

答案1

假设您正在谈论该unshare命令,解决方案是使用该--mount-proc选项,该选项还会取消共享挂载命名空间并/proc在其中挂载一个新的命名空间,该命名空间将引用新的 pid 命名空间。请参阅pid_namespaces(7)手册页:

/proc 和 PID 命名空间

/proc 文件系统(在 /proc/[pid] 目录中)仅显示在执行挂载的进程的 PID 命名空间中可见的进程,即使从其他命名空间中的进程查看 /proc 文件系统也是如此。

创建新的 PID 命名空间后,子级更改其根目录并在 /proc 挂载新的 procfs 实例非常有用,以便 ps(1) 等工具正常工作。

如果通过在 clone(2) 或 unshare(2) 的 flags 参数中包含 CLONE_NEWNS 同时创建新的挂载命名空间,则无需更改根目录:新的 procfs 实例可以直接挂载在 /proc 上。

在 shell 中,挂载 /proc 的命令是:

$ mount -t proc proc /proc

在路径 /proc/self 上调用 readlink(2) 会生成 procfs 挂载的 PID 命名空间(即挂载 procfs 的进程的 PID 命名空间)中调用者的进程 ID。当进程想要在其他命名空间中发现其 PID 时,这对于自省目的非常有用。

$ sudo unshare -p   -f ps -o pid,ppid,pidns,mntns,comm
  PID  PPID      PIDNS      MNTNS COMMAND
27462 24107 4026531836 4026531840 sudo
27463 27462 4026531836 4026531840 unshare
27464 27463 4026532863 4026531840 ps
$ sudo unshare -p --mount-proc  -f ps -o pid,ppid,pidns,mntns,comm
  PID  PPID      PIDNS      MNTNS COMMAND
    1     0 4026532864 4026532863 ps

unshare和是包装和系统调用nsenter的低级实用程序,用于像 docker 一样访问命名空间。unshare(2)setns(2)

你可以strace让他们看看会发生什么。在第二个中:

  1. 取消共享 mnt 和 pid 命名空间:

    5281  unshare(CLONE_NEWNS|CLONE_NEWPID) = 0
    
  2. 分叉一个孩子(因为-f

    5281  clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f6b0af4a7d0) = 5282
    

    孩子继承了未共享的命名空间

  3. 在新的 mnt 命名空间中禁用挂载传播,以便挂载既不会在父命名空间中传播,也不会在子命名空间中传播:

    5282  mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) = 0
    5282  mount("none", "/proc", NULL, MS_REC|MS_PRIVATE, NULL) = 0
    
  4. 为内部新的 pid 命名空间挂载一个新的 proc /proc(因为这是ps期望找到它的地方,也是我们创建 mnt 命名空间的原因)。另一种选择是使用一些绑定安装和 chroot.您还可以将 pid 命名空间procfs 挂载到父 mnt 命名空间中,但这会造成严重破坏。

    5282  mount("proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL) = 0
    
  5. ps在该命名空间中执行

    5282  execve("/bin/ps", ["ps", "-o", "pid,ppid,pidns,mntns,comm"], 0x7fff5a325dd8 /* 73 vars */) = 0
    

答案2

我刚看了视频。她将类型 proc 安装为 /proc,并且它仅显示尽可能多的内容(它位于不同的进程命名空间中)。当您可以看到主机时/proc,进程命名空间中存在(正如您所说的)泄漏。

您正在混合一些东西: 有各种名称空间。视频显示了主机名命名空间(她没有关注太多)、进程命名空间和文件系统命名空间(通过 chroot)。您正在混合文件系统和进程命名空间。可能是因为ps使用了文件系统 ( /proc),但您希望它使用进程命名空间。 proc 使用进程命名空间,因此当创建新的 proc 时,它位于新的命名空间中。

安装

请注意,安装不是重新安装(请注意缺少重新安装选项)。

你能有几个 proc 吗?尝试一下。是的,但是在不同的地方,ps只会使用/proc.您甚至可以用静态文件替换它,以欺骗ps.然而,只能存在一个/proc,无论是文件、目录还是特殊文件(除非您执行 chroot)

内核是否写入所有的procs

不,内核不会同时写入两个地方(没有什么可写入的(这不是磁盘空间)。当您从 proc 读取时,内核会生成适当的数据,(这取决于您所在的命名空间)您一次只能读取一个,因此一次只能创建一个,并且只读取需要的数量(仅一个文件)。

是否有任何文档说明 Linux 中哪些命名空间对应于哪些文件系统?

这就像询问名称空间ls位于哪个名称空间一样。它不是,但它受到名称空间的影响。因此 proc 只能了解当前进程命名空间内的进程。 proc 的其他部分可能会受到其他名称空间类型的影响(取决于它们的功能)。

相关内容