Golang挂载命名空间:进程退出后未清除挂载卷?



下面的代码,我想如果我用syscall启动一个进程。CLONE_NEWNS,当进程退出时,命名空间中的每个挂载选项都将被清除。

但事实并非如此?

package main
import (
"fmt"
"os"
"os/exec"
"syscall"
)
var command string = "/usr/bin/bash"
func container_command() {
fmt.Printf("starting container command %sn", command)
cmd := exec.Command(command)
cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWPID |
syscall.CLONE_NEWNS,
}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
fmt.Println("error", err)
os.Exit(1)
}
}
func main() {
fmt.Printf("starting current process %dn", os.Getpid())
container_command()
fmt.Printf("command endedn")
}

运行这个并挂载一个目录,程序退出后这个目录仍然退出。

[root@localhost go]# go run namespace-1.go
starting current process 7558
starting container command /usr/bin/bash
[root@ns-process go]# mount --bind /home /mnt
[root@ns-process go]# ls /mnt
vagrant
[root@ns-process go]# exit
exit
command ended
[root@localhost go]# ls /mnt
vagrant
[root@localhost go]#

如果这是所需的行为,那么 proc 如何在容器实现中挂载?因为如果我在命名空间中挂载 proc,我将得到

[root@ns-process go]# mount -t proc /proc
[root@ns-process go]# exit
exit
command ended
[root@localhost go]# mount
mount: failed to read mtab: No such file or directory
[root@localhost go]#

必须重新安装 proc 才能将其取回。

更新: 在 C 中做同样的事情也会给出相同的结果,我认为这应该是一种预期的行为。

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};
int container_main(void* arg)
{
printf("Container [%5d] - inside the container!n", getpid());
sethostname("container",10);
system("mount -t proc proc /proc");
execv(container_args[0], container_args);
printf("Something's wrong!n");
return 1;
}
int main()
{
printf("start a container!n");
int container_pid = clone(container_main, container_stack+STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);
printf("container ended!n");
return 0;
}

命令输出:

[root@localhost ~]# gcc a.c
[root@localhost ~]# ./a.out
start a container!
Container [    1] - inside the container!
[root@container ~]# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:57 pts/0    00:00:00 /bin/bash
root        17     1  0 08:57 pts/0    00:00:00 ps -ef
[root@container ~]# exit
exit
container stopped!
[root@localhost ~]# ps -ef
Error, do this: mount -t proc proc /proc
[root@localhost ~]# cat a.c

这是由于挂载事件在命名空间之间传播而发生的。装入点的传播类型为MS_SHARED

MS_SHARED:此挂载点与属于其"对等组"的其他挂载点共享挂载和卸载事件。在此挂载点下添加或删除挂载点时,此更改将传播到对等组,以便挂载或卸载也将在每个对等挂载点下进行。传播也以相反的方向发生,因此对等装载上的装载和卸载事件也将传播到此装入点。

来源 - https://lwn.net/Articles/689856/

/proc/self/mountinfo中的shared:N标记表示挂载正在与对等组共享传播事件:

$ sudo go run namespace-1.go
[root@localhost]# mount --bind /home/andrii/test /mnt
# The propagation type is MS_SHARED
[root@localhost]# grep '/mnt' /proc/self/mountinfo
264 175 254:0 /home/andrii/test /mnt rw,noatime shared:1 - ext4 
/dev/mapper/cryptroot rw,data=ordered
[root@localhost]# exit
$ ls /mnt
test_file

在大多数 Linux 发行版上,默认传播类型为MS_SHARED,由systemd设置。请参阅man 7 mount_namespaces中的NOTES

尽管事实上默认传播类型为 新的 在许多情况下,装入点MS_PRIVATE,MS_SHARED通常更多 有用。因此, systemd(1( 会自动重新挂载所有挂载 点作为系统启动时的MS_SHARED。因此,在大多数现代系统上, 默认传播类型实际上是MS_SHARED。

如果需要完全隔离的命名空间,可以通过以下方式将所有挂载点设为私有:

$ sudo go run namespace-1.go
[root@localhost]# mount --make-rprivate /
[root@localhost]# mount --bind /home/andrii/test /mnt
# The propagation type is MS_PRIVATE now
[root@localhost]# grep '/mnt' /proc/self/mountinfo
264 175 254:0 /home/andrii/test /mnt rw,noatime - ext4 
/dev/mapper/cryptroot rw,data=ordered
[root@localhost]# exit
$ ls /mnt

相关内容

  • 没有找到相关文章

最新更新