由浅入深 docker 系列:(4) 容器与虚拟机

如果你认真对着前三篇教程做了练习,那么想必现在对 docker 的使用已经比较熟悉,对于不太复杂的需求也能做到胸有成竹,但是,你还是不敢声称熟悉 docker,因为你完全不了解 docker 的内部原理,对于你它完全是个孤立的黑匣子。

那么接下来,我将带领你去认识它,让它不再陌生。

一:Ubuntu 18.04 和 16.04 的内核版本一样么

首先,容器和虚拟机的区别是什么呢?你一定看到过很多答案,说容器更轻量,更快,虚拟机要虚拟化硬件等等,还有这张经典图,但是可能你并不是很理解,至少我当时看的一头雾水。

我们知道,ubuntu 16.04 和 ubuntu18.04 的内核版本是不一样的,ubuntu16.04 的虚拟机和 ubuntu18.04 的虚拟机的内核版本也必定不一致。那么,如果是 ubuntu16.04 和 ubuntu18.04 的 docker 环境呢?

我们来试一下:查看 linux 系统内核信息可以使用命令 uname -a,剩下的你应该知道怎么做了。不知道的话,再去看一下前几篇。

这是我在 macos 上的执行结果:

uname -a

我们发现,debian 和 ubuntu 的内核版本竟然是一样的!如果你有兴趣,还可以试验 debian 或者其他系统,你会发现他们都是一样的!

二:内核 VS 用户程序

我们知道,操作系统分为内核和用户程序,进而存在内核空间(kernel space)和用户空间(user Space)。简单说,Kernel Space 是 Linux 内核的运行空间,User space 是用户程序的运行空间。Kernel space 可以执行任意命令,调用系统的一切资源;User space 只能执行简单的运算,不能直接调用系统资源,必须通过系统接口(又称 system call),才能向内核发出指令。

而容器事实上就是一个用户程序,它同样通过调用系统接口实现功能。对于 docker 而言,如果宿主机是 linux,它就直接使用宿主机内核,如果宿主机是 macos 或者 windows,docker 服务会提供一个公用的 linux 内核供各容器调用,因此你会看到 ubuntu18.04 和 16.04 的内核版本是一致的。

而容器镜像里的文件,包括了可执行程序以及它的依赖,例如 ubuntu18.04 镜像,就是把 18.04 的所有可执行程序以及它们的依赖进行打包,容器启动的时候文件被加载进内存,事实上就对应着用户空间,而这些程序的运行,还需要调用系统接口。

你一定会说,那现在容器依赖内核接口,如果内核接口变了,容器不就无法正常运行了?

事实上,内核接口的变更是比较慎重、缓慢的,在短时间内,我们可以预期容器能够正常运行。但是,随着宿主系统尤其是内核的升级,我们并不能保证 docker 容器永远可以正常运行。

三:容器与虚拟机

现在,你应该明白了为什么 ubuntu16.04 和 18.04 的 docker 容器内核版本一样,那么,容器和虚拟机的区别也就明显了,它们虚拟化的层次并不一样。

虚拟机完整虚拟了内核和用户空间,而 docker 仅仅虚拟了用户空间,那么 docker 必然更轻量、更快。

再回头看第一张图,也就清晰了。虚拟机建立在虚拟硬件层之上,每个虚拟机都有独立的内核和用户程序以及依赖库;而 docker 容器建立在宿主机内核和 docker 服务之上,使用共同的内核,每个容器仅仅是用户程序、依赖库不同;再更进一步,普通的程序都使用共同的依赖库,只有程序文件不同。

纵观全局,从普通的应用程序,到 docker 容器,再到虚拟机技术,无非是对磁盘空间、系统资源和隔离性、安全性的权衡取舍而已。至于 docker 的自动化,对比虚拟机似乎并不公平,于 Vagrant 这样的工具相比倒是更加合适。

四:其它

再解释下之前的问题,既然 docker 容器仅仅是一个进程,与虚拟机并不一样,也就不适合以虚拟机的方式使用 docker。 docker 容器的创建、关闭代价很小,我们应该使用独立的 docker 容器执行独立的任务,就像之前 mysql、app、nginx 各自起独立的 docker 容器,这样更方便容器的复用及更新,也方便对系统资源进行管理。

既然 docker 容器只是一个进程,为什么我们使用的时候感觉和虚拟机很像?在容器里使用 ps 看不到其它进程,看不到其它文件等等。这就涉及到了 linux 内核提供的 CGroups 机制,它为进程提供了文件、cpu、内存、网络等资源的隔离,是虚拟化技术的基础,也即是我们第五篇要介绍的内容,敬请期待。

« »

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *