容器就是一个特殊的进程,想要进入容器,先要找到容器的进程
# docker inspect --format '{{ .State.Pid }}' c054b1ef5034
5962
找到进程对应的namespace
# cd /proc/5962/ns # ls -l lrwxrwxrwx 1 root root 0 4月 5 12:18 cgroup -> 'cgroup:[4026531835]' lrwxrwxrwx 1 root root 0 4月 5 12:18 ipc -> 'ipc:[4026532766]' lrwxrwxrwx 1 root root 0 4月 5 12:18 mnt -> 'mnt:[4026532764]' lrwxrwxrwx 1 root root 0 4月 5 12:15 net -> 'net:[4026532769]' lrwxrwxrwx 1 root root 0 4月 5 12:18 pid -> 'pid:[4026532767]' lrwxrwxrwx 1 root root 0 4月 5 12:18 pid_for_children -> 'pid:[4026532767]' lrwxrwxrwx 1 root root 0 4月 5 12:18 time -> 'time:[4026531834]' lrwxrwxrwx 1 root root 0 4月 5 12:18 time_for_children -> 'time:[4026531834]' lrwxrwxrwx 1 root root 0 4月 5 12:18 user -> 'user:[4026531837]' lrwxrwxrwx 1 root root 0 4月 5 12:18 uts -> 'uts:[4026532765]'
所谓进入容器(docker exec),就是在新建一个进程的时候使用容器的namespace,这个过程是使用setns()这个linux系统调用完成的。
#define _GNU_SOURCE #include <fcntl.h> #include <sched.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE);} while (0) int main(int argc, char *argv[]) { int fd; fd = open(argv[1], O_RDONLY); if (setns(fd, 0) == -1) { errExit("setns"); } execvp(argv[2], &argv[2]); errExit("execvp"); }
这段代码是摘抄自极客时间张磊老师《深入剖析Kubernetes》,代码作用在argv[2]进程中加入argv[1]的namespace。
# gcc -o set_ns set_ns.c # ./set_ns /proc/5962/ns/net /bin/bash # echo $$ 6132
新进程为6132,容器进程为5962,两个进程使用的是同一个网络namespace。
# ls -l /proc/5962/ns/net lrwxrwxrwx 1 root root 0 4月 5 12:15 /proc/5962/ns/net -> 'net:[4026532769]' # ls -l /proc/6132/ns/net lrwxrwxrwx 1 root root 0 4月 5 12:31 /proc/6132/ns/net -> 'net:[4026532769]'
在6132这个终端下查看网络,6号网卡@if7
# echo $$ 6132 # ip a6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
在宿主机上查看容器网络,7号网卡@if6
# ip a 7: vethff5a318@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether ae:34:ac:46:40:25 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::ac34:acff:fe46:4025/64 scope link valid_lft forever preferred_lft forever
与docker命令行--net方式获得结果一致
# docker run -it --net container:c054b1ef5034 busybox ip a 6: eth0@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever