Docker是如何实现容器隔离的

本文详细介绍了Docker如何利用Linux内核的Namespace技术实现容器的隔离,包括PID、NET、IPC、UTS、Mount和User六个方面的隔离。通过实例展示了如何创建和管理网络命名空间,以及解决容器中1号进程不能启动其他进程的问题。最后,讨论了pid隔离后的两个常见问题及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Docker如何实现隔离
Linxu内核实现Namespace的主要目的是为了实现轻量化的虚拟化,就是为了支持容器
查看隔离
Docker每一个容器中有独立的IP、端口、路由,共有六项隔离
我们通过一个简单的Apache来查看Docker有哪六项隔离
1 [root@localhost ~]# yum -y install httpd
2 [root@localhost ~]# systemctl start httpd
3 [root@localhost ~]# netstat -anpt | grep 80
4 tcp6 0 0 :::80 ::😗 LISTEN 68305/httpd
可以看到关于80端口的pid是68305,这是重点,接下来就要去看68305这个pid中有哪些东西
这写pid的相关目录都在/proc目录中,也理解为在内存中运行的内容
1 [root@localhost ~]# ls /proc
2 1 10524 10676 1286 289 484 67448 861 cgroups modules
3 10 10529 10701 1289 29 485 67589 862 cmdline mounts
4 10042 10535 10727 1293 290 49 67590 863 consoles mpt
5 101 10552 10730 13 314 50 67591 864 cpuinfo mtrr
6 10112 10562 10735 1301 316 51 67628 865 crypto net
7 10117 10570 10742 1307 317 53 677 867 devices pagetypeinfo
8 10120 10581 10745 1308 318 569 68058 868 diskstats partitions
9 …
所有正在运行的进程id号都会在这里以目录的形式体现,同样刚才启动的80端口的pid68305也在这里,如果要看该程序占用多大,查看目录相关pid目录的大小即可
其中的ns目录就是namespace的缩写
1 [root@localhost ~]# cd /proc/68305/ns/
2 [root@localhost ns]# ll
3 total 0
4 lrwxrwxrwx. 1 root root 0 Mar 25 09:06 ipc -> ipc:[4026531839]
5 lrwxrwxrwx. 1 root root 0 Mar 25 09:06 mnt -> mnt:[4026532505]
6 lrwxrwxrwx. 1 root root 0 Mar 25 09:06 net -> net:[4026531956]
7 lrwxrwxrwx. 1 root root 0 Mar 25 09:06 pid -> pid:[4026531836]
8 lrwxrwxrwx. 1 root root 0 Mar 25 09:06 user -> user:[4026531837]
9 lrwxrwxrwx. 1 root root 0 Mar 25 09:06 uts -> uts:[4026531838]
ns目录中的六项内容,就是namespace的隔离数据包,每个程序的空间都要保持唯一性,否则就会产生冲突
我们通过另一个随机程序的隔离机制来查看,当两个程序的每一项后面的数字相同时,表示存在冲突,也就是他们在同一个空间内,但是观察发现,只有mnt是不冲突的
1 [root@localhost ns]# ll …/…/20/ns/
2 total 0
3 lrwxrwxrwx. 1 root root 0 Mar 25 09:09 ipc -> ipc:[4026531839]
4 lrwxrwxrwx. 1 root root 0 Mar 25 09:09 mnt -> mnt:[4026531840]
5 lrwxrwxrwx. 1 root root 0 Mar 25 09:09 net -> net:[4026531956]
6 lrwxrwxrwx. 1 root root 0 Mar 25 09:09 pid -> pid:[4026531836]
7 lrwxrwxrwx. 1 root root 0 Mar 25 09:09 user -> user:[4026531837]
8 lrwxrwxrwx. 1 root root 0 Mar 25 09:09 uts -> uts:[4026531838]
mnt:是挂载点和文件系统的隔离,也就是程序所在的根目录不同,也就是在不同的mnt命名空间
六项隔离
Linux的namespace是在内核版本3.8以后引进的,如果内核是之前的也就代表不支持虚拟化
编写一个C语言的脚本来启动一个进程模拟命名空间
1 [root@localhost ~]# vim example.c
2 # 添加
3 #define _GNU_SOURCE
4 #include <sys/types.h>
5 #include <sys/wait.h>
6 #include <stdio.h>
7 #include <sched.h>
8 #include <signal.h>
9 #include <unistd.h>
10
11 #define STACK_SIZE (1024 * 1024)
12
13 static char child_stack[STACK_SIZE];
14 char* const child_args[] = {
15 “/bin/bash”,
16 NULL
17 };
18
19 int child_main(void* args) {
20 printf(“在子进程中!\n”);
21 execv(child_args[0], child_args);
22 return 1;
23 }
24
25 int main() {
26 printf(“程序开始: \n”);
27 int child_pid = clone(child_main, child_stack + STACK_SIZE, SIGCHLD, NULL);
28 waitpid(child_pid, NULL, 0);
29 printf(“已退出\n”);
30 return 0;
31 }
编译该脚本
1 gcc -Wall example.c -o test.o
将编译后的脚本程序运行起来
1 ./test.o
验证该进程是否好用
可以看到运行脚本后多了一个终端test.o,执行ps命令之后,查看到执行的ps是在test.o的bash环境里面执行的,其实就是我们在c脚本程序中让他开启了一个子进程bash
当exit退出test.o,再次执行时,发现只有一个bash了,也就是我们最开始使用的环境
1 [root@localhost ~]# ./test.o
2 程序开始:
3 在子进程中!
4 [root@localhost ~]# ps
5 PID TTY TIME CMD
6 68102 pts/3 00:00:01 bash
7 68651 pts/3 00:00:00 test.o
8 68652 pts/3 00:00:00 bash
9 68695 pts/3 00:00:00 ps
10 [root@localhost ~]# exit
11 exit
12 已退出
13 [root@localhost ~]# ps
14 PID TTY TIME CMD
15 68102 pts/3 00:00:01 bash
16 68697 pts/3 00:00:00 ps
这个程序中的子进程bash和原始bash是对称空间的关系
UTS:主机和域名
提供了主机名和域名的隔离,每个容器都有自己的主机名和域名,可以理解为一个独立的节点,并不是一个进程
修改c脚本来实现UTS的隔离,在脚本中我们子进程的bash设置了一个主机名为Changed Name,希望在执行程序进入子进程后,它的主机名是我们设置的这个,又添加了CLONE_NEWUTS,用来克隆一个当前空间来启动一个UTS空间
1 [root@localhost ~]# vim example.c
2 #define _GNU_SOURCE
3 #include <sys/types.h>
4 #include <sys/wait.h>
5 #include <stdio.h>
6 #include <sched.h>
7 #include <signal.h>
8 #include <unistd.h>
9
10 #define STACK_SIZE (1024 * 1024)
11
12 static char child_stack[STACK_SIZE];
13 char* const child_args[] = {
14 “/bin/bash”,
15 NULL
16 };
17
18 int child_main(void* args) {
19 printf(“在子进程中!\n”);
20 sethostname(“Changed Name”, 12);
21 execv(child_args[0], child_args);
22 return 1;
23 }
24
25 int main() {
26 printf(“程序开始: \n”);
27 int child_pid = clone(child_main, child_stack + STACK_SIZE, CLONE_NEWUTS | SIGCHLD, NULL);
28 waitpid(child_pid, NULL, 0);
29 printf(“已退出\n”);
30 return 0;
31 }
编译修改后的脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值