Docker 底层 runC 的 namespace 实现简单原理

参考以下文章
Linux下使用gcc编译和运行C程序的简单操作 - 2020-12-10 15:39:05
Docker基础技术:Linux Namespace(上) | | 酷 壳 - CoolShell 2020-12-11 15:03:17
Docker基础技术:Linux Namespace(下) | | 酷 壳 - CoolShell 2020-12-11 15:03:26
Docker基础技术:Linux CGroup | | 酷 壳 - CoolShell 2020-12-11 15:03:31
Linux Network Namespace(网络名称空间)介绍、应用 及 详细的互通案例_2020-12-10 15:30:00
docker 背后的核心技术 namespace(命名空间) - 2020-12-10 15:44:51
Docker 书 入门实战DockOne_Docker入门实战全文阅读_百度阅读 2020-12-09 14:40:43
docker 命令底层原理,10张图带你深入理解Docker容器和镜像 - DockOne.io 2020-12-09 15:21:27

gcc 编译说明

1
2
3
gcc xxx.c -o abc #===> 编译 xxx.c 文件,-o 指定编译后的文件名称为 abc(-o 默认为 a.out)

./abc #===> 执行编译后的文件

UTS-(UNIX Time Sharing, UNIX分时操作系统,做到 hostname,domainname 隔离)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
------------------------cat main.c
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024*1024)

static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};

int container_main(void* args){//容器入口
printf("Container - inside the container!!\n");
sethostname("ContainerHostname", 17);//设置 hostname,17表示截取ContainerHostname的长度
/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */
execv(container_args[0], container_args);//进入 /bin/bash
return 1;
}

int main(){//主方法(程序入口)
printf("Parent - start a container!\n");
/* 调用clone函数,传一个函数,定义栈的大小 */
int container_pid = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);//等待子进程结束
printf("Parent - container stopped!\n");
return 0;
}
------------------------hostname #===> 主机 hostname
localhost.localdomain
------------------------gcc main.c -o main ; ./main #===> 编译并执行 c 程序,进入容器
Parent - start a container!
Container - inside the container!!
------------------------hostname #===> 容器 hostname
ContainerHostname
------------------------exit #===> 退出容器 bash
exit
Parent - container stopped!
------------------------hostname #===> 回到主机 bash
localhost.localdomain

IPC-(Inter-Process Communications 进程间通信)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
------------------------cat main.c
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024*1024)

static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};

int container_main(void* args){//容器入口
printf("Container - inside the container!!\n");
sethostname("ContainerHostname", 17);//设置 hostname,17表示截取ContainerHostname的长度
/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */
execv(container_args[0], container_args);//进入 /bin/bash
return 1;
}

int main(){//主方法(程序入口)
printf("Parent - start a container!\n");
/* 调用clone函数,传一个函数,定义栈的大小 */
int container_pid = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);//等待子进程结束
printf("Parent - container stopped!\n");
return 0;
}
------------------------ipcmk -Q
消息队列 id:0
------------------------ipcmk -Q
消息队列 id:1
------------------------ipcmk -Q
消息队列 id:2
------------------------ipcmk -Q #===> 主机 消息队列,每输入一次,id +1
消息队列 id:3
------------------------gcc main.c -o main ; ./main
Parent - start a container!
Container - inside the container!!
------------------------hostname #===> 仍然可以用 hostname 隔离
ContainerHostname
------------------------ipcmk -Q
消息队列 id:0
------------------------
------------------------ipcmk -Q #===> 隔离了
消息队列 id:1
------------------------exit #===> 退出容器
exit
Parent - container stopped!
------------------------

PID-(PID 设为 1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
------------------------cat main.c
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024*1024)

static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};

int container_main(void* args){//容器入口
/* 查看子进程的PID,我们可以看到其输出子进程的 pid 为 1 */
printf("Container [%5d] - inside the container!!\n", getpid());
sethostname("ContainerHostname", 17);//设置 hostname,17表示截取ContainerHostname的长度
/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */
execv(container_args[0], container_args);//进入 /bin/bash
return 1;
}

int main(){//主方法(程序入口)
printf("Parent [%5d] - start a container!\n", getpid());
/* 调用clone函数,传一个函数,定义栈的大小 */
int container_pid = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);//等待子进程结束
printf("Parent - container stopped!\n");
return 0;
}
------------------------gcc main.c -o main ; ./main
Parent [16266] - start a container!
Container [ 1] - inside the container!!
------------------------kill -9 16266 #===> 隔离了,不能删除主机的 PID 号
bash: kill: (16266) - 没有那个进程
------------------------echo $$ #===> 当前 PID = 1
1
------------------------exit #===> 退出容器
exit
Parent - container stopped!

NS-(历史第一个 NameSpace,简写 NS。Mount 挂载点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
------------------------cat main.c
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>

/* 定义一个给 clone 用的栈,栈大小1M */
#define STACK_SIZE (1024*1024)

static char container_stack[STACK_SIZE];
char* const container_args[] = {
"/bin/bash",
NULL
};

int container_main(void* args){//容器入口
/* 查看子进程的PID,我们可以看到其输出子进程的 pid 为 1 */
printf("Container [%5d] - inside the container!!\n", getpid());
sethostname("ContainerHostname", 17);//设置 hostname,17表示截取ContainerHostname的长度
/* 重新mount proc文件系统到 /proc下 */
system("mount -t proc proc /proc");
/* 直接执行一个shell,以便我们观察这个进程空间里的资源是否被隔离了 */
execv(container_args[0], container_args);//进入 /bin/bash
return 1;
}

int main(){//主方法(程序入口)
printf("Parent [%5d] - start a container!\n", getpid());
/* 调用clone函数,传一个函数,定义栈的大小 */
int container_pid = clone(container_main, container_stack + STACK_SIZE,
CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);//等待子进程结束
printf("Parent - container stopped!\n");
return 0;
}
------------------------ps aux | head #===> 主机信息
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.5 0.2 128296 6936 ? Ss 19:47 0:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0.0 0.0 0 0 ? S 19:47 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 19:47 0:00 [kworker/0:0]
root 4 0.0 0.0 0 0 ? S< 19:47 0:00 [kworker/0:0H]
root 5 0.0 0.0 0 0 ? S 19:47 0:00 [kworker/u256:0]
root 6 0.0 0.0 0 0 ? S 19:47 0:00 [ksoftirqd/0]
root 7 0.0 0.0 0 0 ? S 19:47 0:00 [migration/0]
root 8 0.0 0.0 0 0 ? S 19:47 0:00 [rcu_bh]
root 9 0.1 0.0 0 0 ? R 19:47 0:00 [rcu_sched]
------------------------gcc main.c -o main ; ./main
Parent [ 2635] - start a container!
Container [ 1] - inside the container!!
------------------------ps aux #===> 容器信息,进行隔离
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 1.0 0.1 116976 3332 pts/0 S 19:59 0:00 /bin/bash
root 32 0.0 0.0 155448 1848 pts/0 R+ 19:59 0:00 ps aux
------------------------exit
exit
Parent - container stopped!
------------------------ps aux #===> 主机需重新挂载,才可以使用命令(因为隔离了)
Error, do this: mount -t proc proc /proc
------------------------mount -t proc proc /proc
------------------------ps aux | head
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.5 0.2 128296 6936 ? Ss 19:47 0:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
root 2 0.0 0.0 0 0 ? S 19:47 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? S 19:47 0:00 [kworker/0:0]
root 4 0.0 0.0 0 0 ? S< 19:47 0:00 [kworker/0:0H]
root 5 0.0 0.0 0 0 ? S 19:47 0:00 [kworker/u256:0]
root 6 0.0 0.0 0 0 ? S 19:47 0:00 [ksoftirqd/0]
root 7 0.0 0.0 0 0 ? S 19:47 0:00 [migration/0]
root 8 0.0 0.0 0 0 ? S 19:47 0:00 [rcu_bh]
root 9 0.1 0.0 0 0 ? R 19:47 0:00 [rcu_sched]
------------------------

NET-(Network Namespace 网络隔离)

使用 veth pair 来实现 虚拟代理,与主机通信。
省略,可参考此文顶部的文章