Docker 的原理

进阶内容

此部分涉及较为底层的原理,需要一定的操作系统知识,可以略过。建议根据实际掌握的知识水平选择性浏览。

传统的虚拟机和 Docker 的区别如下图所示:

Docker 容器与宿主机共享操作系统内核,直接运行在宿主操作系统上;而虚拟机通过 Hypervisor 与宿主机共享虚拟化后的硬件资源,运行的是完整的操作系统。

换句话说,Docker 容器实际上就是一些直接运行在宿主机上的程序,只不过它们和其他程序是隔离的。这种隔离有几个方面:

进程隔离。Docker 容器内部拥有独立的进程命名空间,无法看到宿主机上的进程,也无法看到其他容器中的进程。通过进程隔离,Docker 保证了容器一定的安全性。

容器内部的进程是宿主机上的普通进程

与虚拟机不同,容器共享宿主机操作系统内核,因此容器内部的进程实际上是宿主机系统上的普通进程,只不过容器内部只能看到这些进程而已。

可以做一个小实验来验证这一点。我们运行一个 ubuntu 容器,并在后台持续运行 cat

docker run --name test --rm -itd ubuntu cat

然后,分别查看宿主机上和容器中的进程:

ps aux | grep cat
# root        5549  0.0  0.0   2344   764 pts/0    Ss+  17:33   0:00 cat
docker exec test ps aux
# USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
# root           1  0.0  0.0   2344   764 pts/0    Ss+  09:33   0:00 cat
# root           6  0.0  0.0   6420  1596 ?        Rs   09:34   0:00 ps aux

可以看到,cat 在容器内部是进程号为 1 的进程,而这个进程在宿主机上也可见,不过进程号与容器内部不同。

顺便提一下,容器运行时只有 cat 一个进程(ps 进程为打印进程时产生的进程),没有任何其他的进程,这也是 Docker 所谓“轻量级”的表现。

网络隔离。(一般情况下)容器内部具有独立的网络命名空间,容器之间可以通过使用 Docker 创建的网络互相连接。

文件系统隔离。Docker 容器具有自己的 root 文件系统,因此可以实现依赖库等文件与宿主机的完全隔离,易于配置、分发环境。通过挂载数据卷或宿主机目录,Docker 容器可以更加灵活地与外界交互。

物理资源隔离。Docker 可限制容器可访问的物理资源,如内存、CPU 等(虽然本课程并未涉及)。

这几种隔离的实现依赖于 Linux 系统提供的 Namespace、CGroup 等技术,因此目前只有 Linux 系统原生支持 Docker,个人版 Windows 系统 和 macOS 系统上的 Docker 是通过在 Linux 虚拟机上运行实现的。这些技术的细节已远远超出本课程的范围。掌握 Docker 的使用方法,并理解其高效性背后的原理,对于高效地使用 Docker 已经足够了。