golang mount namespace: смонтированный том не очищается после выхода из процесса?

код ниже, я думал, что если я запущу процесс с 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 %s\n", 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 %d\n", os.Getpid())
        container_command()
        fmt.Printf("command ended\n")

}

запустите это и смонтируйте каталог, этот каталог по-прежнему закрывается после выхода из программы.

[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 внутри пространства имен, я получу

[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

person woosley. xu    schedule 08.08.2017    source источник


Ответы (1)


Это происходит из-за распространения событий монтирования между пространствами имен. Тип распространения вашей точки монтирования — MS_SHARED.

MS_SHARED: Эта точка монтирования разделяет события монтирования и размонтирования с другими точками монтирования, которые являются членами ее "равноправной группы". Когда точка монтирования добавляется или удаляется в этой точке монтирования, это изменение распространяется на группу одноранговых узлов, так что монтирование или размонтирование также происходит в каждой из одноранговых точек монтирования. Распространение также происходит в обратном направлении, так что события монтирования и размонтирования на одноранговом монтировании также будут распространяться на эту точку монтирования.

Источник — https://lwn.net/Articles/689856/

Тег shared:N в /proc/self/mountinfo указывает, что монтирование делится событиями распространения с группой одноранговых узлов:

$ 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. См. NOTES в man 7 mount_namespaces:

Несмотря на то, что тип распространения по умолчанию для новых точек монтирования во многих случаях — 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
person Andrii L.    schedule 14.08.2017
comment
Спасибо. Для записи, чтобы достичь mount --make-private / в golang, флаг монтирования syscall.MS_PRIVATE|syscall.MS_REC - person woosley. xu; 18.08.2017