容器就是一個特殊的進程,想要進入容器,先要找到容器的進程
# 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
