被忽略的一点:Docker 的单进程模型
Dokcer 大家都在用,docker run 就可以快速的上手一个 demo 应用,但是大家有没有思考过 docker run 背后的原理,其内部的应用又是如何被创建的呢?
我先抛出本文要说明的知识点:docker 是一个单进程模型。
本文分为几个步骤讲解:
首先讲解容器是如何实现隔离的,主要介绍的是 Namespace 技术。
接着介绍容器的本质:其就是一个进程。
最后会引出本文的主题,Docker 是一个单进程模型。
一、演示环境准备
此处为了演示,我们在同一台宿主机上创建 2 个 Docker 容器。
命令如下:
查看两个容器的运行:
为了下面的演示,我们将 b4bb5fb2d10c 当做 docker1。
将 31e14d3dd570 当做 docker2。
现在整体的图示如下:
二、容器的"集装箱模型"
我们经常都把容器比喻为"集装箱",把容器内部运行的内容比喻为"货物"。
这种"集装箱"模型最大的好处就是,我们将自己处理好的应用封装起来,每个"集装箱"负责一款应用,每个应用与应用之间互相隔离,可以运行在同一个宿主机上。
当然,这里我要说一个"集装箱"模型最大的好处:任意移动。什么意思呢?
写过 Java 的同学肯定都听过一句话:"一次编译,到处运行"。(具体是什么含义我就不解释了)
面对到容器这里也是同样的意思:"一次封装,到处运行"。(我想大家一定都体会到这种模型的好处了吧)
三、Namespace:实现容器隔离
上面我们所说的"集装箱"模型,代表着每个 Docker 容器都是独立存在于宿主机上的,每个容器中的内容都相互隔离,互不干扰。那么其是如何实现这种环境隔离的呢?
这里一定要提到一个重要的概念了:Namespace。
Linux Namespace 的一句话概述:用来修改进程视图的主要方法,实现进程隔离。
备注:关于 Namespace 的概念我就不过多介绍了,网上有很多标准的概述,我相信学过容器的同学应该也都或多或少了解过这些概念。这里我系统通过演示案例带大家更好的理解这些内容。
3.1-查看容器 1 的 Namespace
每个运行的容器都会与一个 Namespace 进行绑定,从而拥有各自 Namespace 中的视图。
下面,我们首先查看 docker1 的 Namespace,然后再查看 docker2 的 Namespace。
执行如下的命令,查看 docker1 所运行的进程 PID:
然后我们查看宿主机的 proc 文件,看到上述 2022 进程所有 Namespace 对应的文件:
从上述我们可以看到,这个 Docker 所关联的 Namespace 中相关的内容:
ipc:进程间通信。
mnt:挂载。
net:网络。
等等
3.2-查看容器 2 的 Namespace
跟上面一样的方式,输入如下的命令查看 docker2 的 PID:
然后再查看对应的 Namespace 文件:
3.3-小结
到这里,我们可以看到每一个 Docker 都绑定着各自的 Namespace 环境,然后每个 Namespace 环境下都有各自对应的网络/挂载/用户等环境内容。
现在,我们更新图片:
宿主机所在的环境我们暂称之为"main namespace"。
docker1 所处的 namespace 环境我们称之为"docker1 namespace"。
docker2 所处的 namespace 环境我们称之为"docker2 namespace"。
现在再来看这张图片,可以看到宿主机/docker1/docker2 都运行在不同的 namespace 中,并且每个 namespace 中都有自己的一套网络/挂载/用户信息等配置。因此也就从逻辑上实现了环境的隔离。
四、探讨容器本质:进程
上面知道你在机器上运行的任何程序,他们都归属于同一类:进程。没错,docker 容器也不例外,其本质上也只是一个进程。
4.1-查看容器 1 进程
在上面我们使用了 docker inspect 命令查看了每个容器对应的进程,那个时候我们没有过多的介绍为什么要通过 docker 的 PID 去查看 namespace,这个地方我就来介绍一下。
首先,我们再次来执行下面的命令,查看容器 1 的进程 PID:
可以看到,命令结果显式其 PID 为 2022。
上面我们说了,容器本质上就是个进程,那么这个进程是运行在哪里的呢?没错,当然是在宿主机上。于是,我们可以输入如下的命令查看在宿主机上 PID 为 2022 的内容是什么。
命令结果显式,PID 2022 进程是存在的。
另外,可以看到这个进程的启动命令是/bin/sh。
emm....有的同学就会问了,2022 这个进程的启动命令不就是我们 docker run 启动容器的时候,让容器执行的命令么?没错,是的。至于原理,我先不说,再往下看。
4.2-查看容器 2 进程
与上面一样,先查看容器 2 的进程 PID,然后再查看宿主机上相同 PID 的进程内容。
与预期的结果一样,宿主机的 PID 2742 进程就是容器 2,并且启动命令也是/bin/sh。
4.3-小结
通过上面的介绍,我们知道,docker 容器其实就是宿主机上运行的一个进程:
docker1 的 PID 为 2022。
docker2 的 PID 为 2742。
此时我们可以得到如下的概览图:
五、最终的主题:Docker 的单进程模型
在介绍完了上面这么多的内容之后,我们就开始来介绍最终的主题了:Docker 的单进程模型。
在宿主机查看容器 PID 的时候,看到其启动的命令就是 docker run 时指定的命令(/bin/sh),也就是要让 Docker 容器要运行的功能(也称为其职责)。
5.1-容器的进程视图
下面我们分别看一下各自容器中的进程视图是什么样的。
首先,我们在容器 1 看一下其进程内容:
然后在容器 2 中查看一下其进程内容:
可以看到,在容器内部,进程非常非常的少,只有 2 个进程:
其中一个 PID 为 1,就是 docker run 要运行的内容。
另外一个 PID 为 7 是 ps 执行命令所创建的,可以忽略。
这里我就要说明一个点:在容器内部看到的/bin/sh 进程其实就是宿主机上的/bin/sh 进程。其中:
docker1 内部的/bin/sh PID 1 号进程就是宿主机的 PID 2022 号进程。
docker1 内部的/bin/sh PID 1 号进程就是宿主机的 PID 2742 号进程。
那么,这是如何做到的呢?
实际上,Docker 在启动了容器之后,对/bin/sh 程序做了"障眼法",将宿主机中的/bin/sh 进程"虚拟"成了容器内部的第 1 号进程,使得每个容器内部看到的/bin/sh 程序就如何运行在自己独立隔离的环境中一样。
当然,上面的实现依靠的就是我们最初所说的 Linux 的 Namespace 机制。
此时,我们的系统结构图就如下所示了:
我们在宿主机上启动了两个容器 docker1 和 docker2。
docker1 和 docker2 分别都有自己的 namespace。
docker1 在宿主机上对应的 PID 为 2022,docker2 在宿主上对应的 PID 为 2742。
每个容器内部虚拟出 PID 为 1 的进程映射为宿主机的 PID(此处为 2022 和 2742)。
5.2-僵尸进程/孤儿进程
在继续讲解之前,我介绍几个概念:
父进程:父进程拥有一系列的子进程。
子进程:父进程所管理的进程称为子进程。当一个子进程结束时,正常的情况下,父进程会对子进程进行善后处理(获取终止子进程的有关信息、释放它仍占用的资源)。
僵尸进程/僵死进程:当一个子进程终止时,其父进程没有对其进行善后处理,此时我们称这个子进程为僵尸进程。僵尸进程会一直等待父进程来处理自己,如果父进程没有对其进行处理,会造成资源的浪费。
孤儿进程:一个进程都有一个父进程来管理(系统进程除外),如果父进程在子进程结束之前终止,那么我们就称这个子进程为孤儿进程(失去了父亲)。
5.3-Docker 为单进程的原因
我们说 Docker 容器是"单进程模型",并不代表容器只能运行一个进程,而是指容器没有回收孤儿进程的能力。
例如我们在上面的演示案例中,为容器起了一个/bin/sh 进程(称为容器的 1 号进程),如果我们继续在容器中创建进程,那么新的进程都是这个 1 号进程的子进程。Docker 判断一个容器是否启动正常,是判断 Docker 容器的 1 号进程的状态,如果 1 号进程启动正常,那么就认为容器运行正常,否则,容器运行失败。
因此,如果在容器内部启动了过多的进程,那么当容器的 1 号进程结束之后,由于 1 号进程不具有管理多进程,多线程的能力,所以在容器内部创建的其他进程都会处于没有人接管的状态,此时这些进程都会变为孤儿进程。
六、小结
本文就介绍到这里了,主要讲解了容器是如何通过 Namespace 进行隔离的,以及容器的单进程模型。
有些人肯定就会说了,一个容器对应一个进程,对于大型的项目来说,运行着大量的软件应用,这些软件之间的协作岂不是称为大问题了(设计复杂的跨容器操作等)。提出这种想法是可以理解的,但是容器最佳的应用场景并不是"单独作战",而是多个容器进行协作,也就是我们经常谈到的"容器编排",例如 Docker Swarm,以及目前大火的 Kubernetes。
在后续我会再单独出一篇文章讲解 Kubernetes 的 Pod 的设计原理:Pod 作为 Kubernetes 调度的最小单位,其允许在一个 Pod 中运行多个容器,从而将很多个的单进程(容器)组建为进程组(多个容器)。
版权声明: 本文为 InfoQ 作者【董哥的黑板报】的原创文章。
原文链接:【http://xie.infoq.cn/article/5ee74750ecd5fe3eb05569b5d】。文章转载请联系作者。
评论