Docker vs Hypersior
之前提到过 hypersior 技术:允许多个操作系统共享一个 CPU(多核 CPU 的情况可以是多个 CPU),用以协调多个虚拟机
hypervisor 的每个虚拟机都是一个完整的操作系统,而 docker 采用了“容器”技术,不同容器之间共用一个底层的操作系统:
- hypervisor 采用的是 硬件资源虚拟化 的方法将硬件资源分配给不同的操作系统使用
- docker 采用的则是 操作系统虚拟化 的方式实现了对程序运行环境和访问资源在操作系统内部的隔离
docker 底层依赖于 Linux 中的 Namespace,CGroups 和 Union File System(在 window docker 其实是跑了一个 Linux 虚拟机,然后在虚拟机中使用 docker)
Namespace
Namespace 名为命名空间,限制了 Linux 进程的可访问资源
改变一个 Namespace 中的系统资源只会影响当前 Namespace 里的进程,对其他 Namespace 中的进程没有影响,这就为容器技术提供了条件
Linux 一共实现了6种不同类型的 Namespace:
UTS Namespace:主要用来隔离 hostname(主机名) 和 domainname(域名) 两个系统标识
1 | ➜ 桌面 hostname |
IPC Namespace:用于隔离 System V IPC 和 POSIX message queues
Mount Namespace:用来隔离各个进程看到的挂载点视图
1 | ➜ 桌面 ipcs -q /* 消息队列里有一个msg */ |
PID Namespace:用来隔离进程 ID,同样一个进程在不同的 PID Namespace 里可以拥有不同的 PID
1 | ➜ 桌面 ps -ef /* 查看所有进程的PID */ |
- 如果只是创建 PID Namespace,不能保证只看到 Namespace 中的进程
- 因为类似
ps
这类系统工具读取的是proc
文件系统,proc
文件系统没有切换的话,虽然有了 PID Namespace,但是不能达到我们在这个 Namespace 中只看到属于自己 Namespace 进程的目的 - 在创建 PID Namespace 的同时,使用
--mount-proc
选项,会创建新的 Mount Namespace,并自动mount
新的proc
文件系统 - 这样
ps
就可以看到当前 PID Namespace 里面所有的进程了
User Namespace:主要是隔离用户的用户组 ID
1 | ➜ 桌面 unshare -r --user /bin/bash /* 创建User Namespace */ |
1 | ➜ 桌面 ps -ef | grep 5366 | grep -v grep /* 普通权限 */ |
- 进程 5366 在容器(user namespace)外属于一个普通用户,但是在 user namespace 里却属于 root 用户
Net Namespace:用来隔离网络设备,IP地址,端口等
1 | ➜ 桌面 sudo ip link add veth0_11 type veth peer name veth1_11 /* 创建一对网卡,分别命名为veth0_11/veth1_11 */ |
CGroups
CGroups 全称 Control Groups,是 Linux 下用来控制进程对 CPU、内存、块设备 I/O、网络等资源使用限制的机制
通过使用 CGroups,可以实现为进程组设置内存上限、配置文件系统缓存、调节 CPU 使用率和磁盘 IO 吞吐率等功能,以及对进程组进行快照或者重启等功能(具体的资源控制器由不同的子系统 subsystem
完成)
相关的概念如下:
- 一个
subsystem
就是一个内核模块,他被关联到一颗 cgroup 树之后,就会在树的每个节点(进程组)上做具体的操作 - 一个
hierarchy
可以理解为一棵 cgroup 树,树的每个节点就是一个进程组,每棵树都会与零到多个subsystem
关联 - 一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的
subsystem
关联
查看当前系统支持的 subsystem(子模块):
1 | ➜ 桌面 cat /proc/cgroups |
- subsys_name:subsystem 的名称
- hierarchy:subsystem 所关联到的 cgroup 树的 ID
- 如果多个 subsystem 关联到同一颗 cgroup 树,那么他们的这个字段将一样
- 这个字段将为 “0”,将会是下面3种情况:
- 当前 subsystem 没有和任何 cgroup 树绑定
- 当前 subsystem 已经和 cgroup v2 的树绑定
- 当前 subsystem 没有被内核开启
- num_cgroups:subsystem 所关联的 cgroup 树中进程组的个数,也即树上节点的个数
- enabled:“1” 表示开启,“0” 表示没有被开启(可以通过设置内核的启动参数 cgroup_disable 来控制 subsystem 的开启)
Union File System
Union File System,简称 UnionFS,他是一种为 Linux,FreeBSD 和 NetBSD 操作系统设计的,把其他文件系统联合到一个联合挂载点的文件系统服务(它用到了一个重要的资源管理技术,叫写时复制 COW)
Linux 启动会先用只读模式挂载 rootfs,运行完完整性检查之后,再切换成读写模式
Docker deamon 为 container 挂载 rootfs 时,也会先挂载为只读模式,但是与 Linux 做法不同的是:
- 在挂载完只读的 rootfs 之后,Docker deamon 会利用联合挂载技术(Union Mount)
- 在已有的 rootfs 上再挂一个读写层
- container 在运行过程中文件系统发生的变化只会写到读写层,并通过 whiteout 技术隐藏只读层中的旧版本文件
Docker 镜像的设计中,引入了层(layer)的概念:
- 用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs(一个目录)
- 这样应用 A 和应用 B 所在的容器共同引用相同的 Debian 操作系统层,只读层(存放程序的环境),而各自有各自应用程序层,和读写层
Docker 的镜像就采用了 UnionFS 技术,从而实现了分层的镜像
使用 docker inspect
这个命令来查看 ubuntu 这个镜像文件,输出了以下内容:
1 | "GraphDriver": { |
- 这些镜像层都位于
/var/lib/docker/overlay2
目录中(OverlayFS 是 Docker 目前的联合文件系统解决方案)
Docker 默认安装的情况下,会使用 /var/lib/docker/
目录作为存储目录,用以存放拉取的镜像和创建的容器等
1 | ➜ 桌面 sudo ls /var/lib/docker/ |
- 而
/var/lib/docker/overlay2
通常用于存放容器虚拟文件系统的相关信息
先看一个案例:
1 | ➜ 桌面 docker start 96e1f1382d93 |
- 开启一个容器,往
/home/1234
写入 “hello word”
1 | ➜ 桌面 sudo ls /var/lib/docker/overlay2/a11758995ecf6105ad01ed03f9e8f4304d4685f58eae032dff5e29ec4b55cf8e |
- 查看
/var/lib/docker/overlay2
中的数据,发现/home/1234
被写入其中 - 由此可以发现,docker 容器的读写层其实是挂载到
/var/lib/docker/overlay2
中的 - 对一个容器的修改只会影响其读写层,而这些变化也直接体现在
/var/lib/docker/overlay2
中
1 | ➜ 桌面 sudo rm /var/lib/docker/overlay2/a11758995ecf6105ad01ed03f9e8f4304d4685f58eae032dff5e29ec4b55cf8e/diff/home/1234 |
- 若删除
/home/1234
后重新进入容器,会发现/home/1234
依然存在 - 这时因为文件
/home/1234
仍被加载在内存中
1 | ➜ 桌面 docker stop $(docker ps -q) |
- 若此时关闭容器,重新打开并进入后会发现
/home/1234
消失
docker 处理异构操作系统
对于虚拟化程序来说,处理异构二进制代码的能力是必不可少的,Qemu 就内置了一个翻译器,专门用于把异构的二进制代码给翻译为本架构能够理解的形式
而 docker 也是借用了 Qemu 的翻译器(qumu-user-static)来处理异构程序,使用方法如下:
- 下载翻译器:Releases · multiarch/qemu-user-static (github.com)
- 注册平台的解释器:
1 | docker pull multiarch/qemu-user-static:register |
- PS:每次重启机器,需重新注册
- 注册成功后,可以使用如下命令查询 aarch64 对应的解释器:(其他架构同理)
1 | cat /proc/sys/fs/binfmt_misc/qemu-aarch64 |
- 撤销平台的解释器:
1 | docker run --rm --privileged multiarch/qemu-user-static:register |