setns 设置pid namespace不起作用

最近在学习docker原理, 使用setns模拟将新程序追加到已有的pid namespace中, 发现不起作用.

我的步骤:

  1. 找到容器, 查看它的ns

    $ docker inspect 7caa18cb2657|grep -i pid
                "Pid": 13059,
                "PidMode": "",
                "PidsLimit": 0,
    $ sudo ls -l /proc/13059/ns
    [sudo] chen 的密码:
    总用量 0
    lrwxrwxrwx 1 root root 0 5月  28 09:15 cgroup -> cgroup:[4026531835]
    lrwxrwxrwx 1 root root 0 5月  28 09:15 ipc -> ipc:[4026532571]
    lrwxrwxrwx 1 root root 0 5月  28 09:15 mnt -> mnt:[4026532569]
    lrwxrwxrwx 1 root root 0 5月  28 09:14 net -> net:[4026532574]
    lrwxrwxrwx 1 root root 0 5月  28 09:15 pid -> pid:[4026532572]
    lrwxrwxrwx 1 root root 0 5月  28 09:15 pid_for_children -> pid:[4026532572]
    lrwxrwxrwx 1 root root 0 5月  28 09:15 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 5月  28 09:15 uts -> uts:[4026532570]
  2. 查看docker exec的效果

    $ sudo ps -ef |grep /bin/sh
    ...
    chen     12941 11444  0 09:14 pts/2    00:00:00 docker run -it alpine /bin/sh
    root     13059 13033  0 09:14 pts/0    00:00:00 /bin/sh
    chen     14750 13127  0 09:23 pts/3    00:00:00 docker exec -it 7caa18cb2657 /bin/sh
    root     14839 13033  0 09:23 pts/1    00:00:00 /bin/sh
    ...
    $ sudo ls -l /proc/14839/ns
    总用量 0
    lrwxrwxrwx 1 root root 0 5月  28 09:25 cgroup -> cgroup:[4026531835]
    lrwxrwxrwx 1 root root 0 5月  28 09:25 ipc -> ipc:[4026532571]
    lrwxrwxrwx 1 root root 0 5月  28 09:25 mnt -> mnt:[4026532569]
    lrwxrwxrwx 1 root root 0 5月  28 09:25 net -> net:[4026532574]
    lrwxrwxrwx 1 root root 0 5月  28 09:25 pid -> pid:[4026532572] // 和容器的pid namespace一致
    lrwxrwxrwx 1 root root 0 5月  28 09:25 pid_for_children -> pid:[4026532572]
    lrwxrwxrwx 1 root root 0 5月  28 09:25 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 5月  28 09:25 uts -> uts:[4026532570]
  3. 使用setns模拟docker exec
    使用的setns代码:

    #define _GNU_SOURCE
    #include <fcntl.h>
    #include <sched.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>
     
    // 它需要两个参数: argv[1]是当前进程要加入的 Namespace 文件的路径, 而argv[1]是将要在这个 Namespace 里运行的进程
    // 这段代码的的核心操作是通过 open() 系统调用打开了指定的 Namespace 文件,并把这个文件的描述符 fd 交给 setns() 使用. 
    // 在 setns() 执行后,当前进程就加入了这个文件对应的 Linux Namespace 中.
    int main(int argc, char *argv[]) {
        int fd;
        
        fprintf(stdout, "argv1: %s, argv2\n", argv[1],argv[2]);
    
        fd = open(argv[1], O_RDONLY);
        if (setns(fd, 0) == -1) {
            fprintf(stderr, "setns failed: %s\n", strerror(errno));
            return -1;
        }
        
        if (execvp(argv[2], &argv[2]) != 0 ) {
            fprintf(stderr, "failed to execvp argments %s\n", strerror(errno));
            return -1;
          }
    
          printf("all done!\n");
          return 0;
    }

    执行查看效果:

    $ gcc -o setns setns.c
    $ sudo ./setns /proc/13059/ns/pid /bin/bash
    [sudo] chen 的密码:
    argv1: /proc/13059/ns/pid, argv2:/bin/bash
    root@chen-pc:/home/chen/test#

    在另一个终端查看效果:

    $ sudo ps -ef |grep bin/bash
    ...
    root     16210 13790  0 09:40 pts/6    00:00:00 sudo ./setns /proc/13059/ns/pid /bin/bash
    root     16240 16210  0 09:40 pts/6    00:00:00 /bin/bash
    ...
    $ sudo ls -l /proc/16240/ns
    总用量 0
    lrwxrwxrwx 1 root root 0 5月  28 09:43 cgroup -> cgroup:[4026531835]
    lrwxrwxrwx 1 root root 0 5月  28 09:43 ipc -> ipc:[4026531839]
    lrwxrwxrwx 1 root root 0 5月  28 09:43 mnt -> mnt:[4026531840]
    lrwxrwxrwx 1 root root 0 5月  28 09:43 net -> net:[4026531993]
    lrwxrwxrwx 1 root root 0 5月  28 09:43 pid -> pid:[4026531836] # 和容器的pid namespace一致
    lrwxrwxrwx 1 root root 0 5月  28 09:43 pid_for_children -> pid:[4026532572]
    lrwxrwxrwx 1 root root 0 5月  28 09:43 user -> user:[4026531837]
    lrwxrwxrwx 1 root root 0 5月  28 09:43 uts -> uts:[4026531838]

最后发现setns.c模拟结果和容器的pid namespace不一致, 请问这是怎么回事?

我也用sudo ./setns /proc/13059/ns/net /bin/bash模拟过, 发现容器里的ifconfig结果与在模拟中运行的结果一致.
阅读 3k
1 个回答

PID namespace 比较特殊..当前调用者所属PID namespace不能被改变

你可以参考下runc的做法,用子进程做这个事.

请参考此链接

推荐问题
宣传栏