最近在学习docker原理, 使用setns模拟将新程序追加到已有的pid namespace中, 发现不起作用.
我的步骤:
-
找到容器, 查看它的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]
-
查看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]
-
使用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结果与在模拟中运行的结果一致.
PID namespace 比较特殊..当前调用者所属PID namespace不能被改变
你可以参考下runc的做法,用子进程做这个事.
请参考此链接