虚拟化要解决的核心问题:资源隔离与资源限制。
docker通过Namespace实现资源隔离,CGroup实现资源限制,UnionFS实现存储层的读写控制。
这几种机制都是操作系统级支持的,docker利用它们实现虚拟化过程。
1. Namespace命名空间
linux的6种命名空间:
PID Namespace | 进程隔离 | 不同Namespace中的进程可以有相同的 PID(容器的 init 进程 pid 是 1)。 |
Net Namespace | 网络隔离 | 每个Namespace都有自己的网络设备、IP 地址、路由表和防火墙规则 |
Mnt Namespace | 文件系统隔离 | 不同的Namespace有不同的挂载点,容器内看到的是自己独立的文件系统 |
IPC Namespace | 隔离进程间通信 | 每个Namespace管理自己的IPC资源,包括消息队列、信号量和共享内存,不会相互干扰 |
UTS Namespace | 隔离主机名和域名 | 每个命名空间都有自己的主机名 |
User Namespace | 隔离用户和用户组 | 每个命名空间都有自己的用户和组 |
如何查看一个进程的命名空间id:
// 打印当前进程pid
echo $$
// 查看进程树
pstree -p 8061
// 根据进程pid查看namespace
ls -l /proc/8061/ns
// 以下是输出
lrwxrwxrwx 1 root root 0 Jun 24 12:51 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Jun 24 12:51 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Jun 24 12:51 net -> net:[4026531968]
lrwxrwxrwx 1 root root 0 Jun 24 12:51 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Jun 24 12:51 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jun 24 12:51 uts -> uts:[4026531838]
docker启动一个容器时,会调用Linux Kernel Namespace的接口,设置几种命名空间(默认6全都设置)。容器被创建出了新的命名空间,有别于宿主机的命名空间,以此达到资源的有效隔离。
linux kernel的系统函数:
// child_func 传入子进程运行的程序主函数
// child_stack 传入子进程使用的栈空间
// flags 表示使用哪些 CLONE_* 标志位
// args 用于传入用户参数
int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg);
// 比如要实现进程独立的UTS空间:
int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWUTS | SIGCHLD , NULL);
2. CGroup资源限制
要解决的问题:如容器在执行cpu密集型任务时,为了不影响其他容器的性能,出现多个容器抢占资源的问题,需要对多个容器的资源使用进行限制(cpu/内存/磁盘)。
Linux Control Group是Linux内核的一个功能,可以实现:
-
Resource limitation: 限制资源
-
Prioritization: 优先级控制,如CPU利用和磁盘IO吞吐
-
Accounting : 一些审计或一些统计,主要是为了计费
-
Control: 挂起进程,恢复执行进程
// 进入cpu group目录
cd /sys/fs/cgroup/cpu
// 创建一个测试目录,这里创建后会自动在目录下生成一些文件
mkdir test_limit
cd test_limit
// 接下来执行一个程序,让cpu跑到100%, 并查看这个进程的pid
go run /xxx/test.go
// 限制test_limit这个进程组的cpu总利用率为20%
// 这里20000指的就是20%
echo 20000 > /sys/fs/cgroup/cpu/test_limit/cpu.cfs_quota_us
// 向任务控制列表中添加需要被限制的pid
echo 12345 >> /sys/fs/cgroup/cpu/test_limit/tasks
// 可以top查看cpu利用率是否降到20%
docker底层也是利用上述原理实现的资源限制,cpu、内存、磁盘IO、网络带宽都可以限制。
3. UnionFS联合文件系统
解决的问题:对多个容器来说,如果他们共用一个image,是不需要把每个image都cp一份的,全量的文件系统拷贝会对磁盘造成巨大压力,需要做image的复用,提高磁盘的利用率。
Docker镜像是一种分层存储结构,Dockerfile中每条指令会生成一层,镜像是一层一层堆叠起来的,镜像中的这些层都是只读的。
当启动容器时,会在这些层的基础上创建一个容器层,用于保存容器内部做的所有更改。
写时复制(COW, Copy on Write):当对文件有写操作时,才会从只读的镜像层复制文件到容器层,进行修改。所以无论有多少个容器共享一个image,都是在自己容器内的副本上操作的,不会修改image源文件,也不会相互影响。
UnionFS 是一种为 Linux 操作系统设计的,用于把多个文件系统联合到同一个挂载点的文件系统服务, 将不同文件夹中的层联合(Union)到了同一个文件夹中,整个联合的过程被称为联合挂载(Union Mount)。
Docker的存储驱动是基于UnionFS的overlay2,实现上述联合文件系统的管理。
![](https://img-blog.csdnimg.cn/direct/36c2c61f0474457f91539dec2abff26a.png)