这是 Docker 的简历

在了解虚拟化和容器技术后,我们就更容易理解 Docker 的相关知识了。在这一小节中,我将介绍关于 Docker 的出现和发展,Docker 背后的技术。同时,我们将阐述 Docker 在虚拟化领域中的定位以及其带来的变革。

Docker 开源项目

如果说 Docker 的诞生,就必须从 Docker 这个开源项目提起 ( 虽然它现在已经不叫 Docker 了 )。Docker 项目是一个由 Go 语言实现的容器引擎,它最初由 dotCloud 这家做云服务的公司在 2013 年开源。

由于 Docker 带来的巨大的便利,让很多开发、测试和运维等软件开发环节上的工作被简化甚至省去,所以在短短的几年间便成为虚拟化乃至整个技术领域的热词。同时,许多开发者乃至大型科技企业都参与到了 Docker 相关领域的贡献中来,为 Docker 及其生态圈贡献了许多优秀的软件项目,这大大提高了 Docker 生态的完整性,也让 Docker 日益健壮。

也许 dotCloud 自己也没有想到,云服务没卖出几个钱,反倒是 Docker 越来越火。拥有商业头脑的他们,干脆不再做云服务了,也把公司名字改成 Docker Inc. 专门从事 Docker 周边的生意。

当然,Docker 的商业化也带来了一定的变化。为了更好的进行商业运作,Docker Inc. 将 Docker 开源项目的名称修改为了 Moby,所以大家要是在 GitHub 上没有搜索到 Docker 不要觉得惊讶,因为它现在的名字是 Moby ( https://github.com/moby/moby )。

关于 Moby 和 Docker 更多的内容,这里给大家提供一下参考资料,有兴趣的朋友们可以前往阅读:

Docker 所带来的改变

简单说了一些 Docker 的故事,接来下我们就必须重点说一下 Docker 所带来的改变。正是这些对我们工作方式的改变,让我们越来越难以离开 Docker,又源于我们对 Docker 如此的喜爱,让 Docker 能够在短时间内就从雏鸟成长为大鹏,成为万众瞩目的新星。

云计算时代的挑战

在计算机技术发展的早期,几乎所有的程序都是在开发后部署到一台或是少数几台服务器上的。那时的程序也几乎都是集所有模块和运行时环境为一身的“全栈应用”,虽然这些程序可以基于一套良好、完善的协议栈 ( 譬如一套完整的 MVC 架构 ) 进行开发,但再好的架构也无法让应用服务在这种体系下快速发展。

随着互联网的极速发展,应用程序的功能越来越丰富,而需要迭代的速度要求也越来越高,为了实现这些目标,应用的开发逐渐趋向服务化甚至微服务化。每个应用程序都有其对应依赖的操作系统或者其他程序,而在将应用程序细分为不同的微服务或者是其他形式的微小应用模块后,解决这种依赖问题会愈发显得棘手。有的应用运行环境特别复杂,搭建过程也极易出错,这都是让开发、测试、运维人员焦头烂额的地方。更多时候,开发者们肯定更愿意将他们宝贵的时间用在实际的开发中,而不是纠缠着应用运行环境的问题上。

分布式应用服务体系

同时,由于物理硬件的更新迭代速度已经难以追赶互联网的脚步,应用的部署逐渐转向集群化。应用模块的数量乘上每个应用所部署的机器的数量,会是一个非常庞大的数字。相信所有的开发或者运维都不会愿意把时间浪费在逐一搭建服务器环境这种重复的劳动上。

这些变化都对应用的开发、部署带来了不小的挑战。

我想很多读者已经想到了应对这些挑战的办法了,没错,那就是虚拟化技术。通过虚拟化技术可以让环境的搭建变得更加的容易,对我们快速部署分布式应用服务体系提供了极大的便利。

进而言之,如果我们把管理环境的复杂度,更轻量级的虚拟化实现等更加实际的问题考虑进去,容器技术自然成了虚拟化技术中最佳的选择项。

皆为效率

如果说在分布式部署中应用容器技术是一个方向,那么 Docker 就是在这个方向上跑得最快也最完美的运动员了。Docker 不论从实现容器技术的完整性上来说,还是从上手易用性来说,都是可圈可点的。

好了,这里我要穿插一下推荐 Docker 的原因了。我们使用 Docker 的目的其实很简单,就是利用它的全面性和易用性带来的提升我们的工作效率。了解了这个目的,我想大家会更容易理解很多场合 Docker 能派上用场的原因。当然,通过这个道理,你也就明白了为什么我会说 Docker 是一门新时代开发者必须掌握的技术了。毕竟所有的老板都希望找到会得多、干活快的优秀开发者 ( 亦或者说,会的多、干活快是优秀开发者所必备的品质 )。

再怎么从理论上说快也是很难服众的,是骡子是马拉出来“跑个分”就知道了。Docker 官方对 Docker 在工作上带来的提升做了调查研究,分别从工作效率的提升和技术设计投入的减少等方面数据化了 Docker 所做出的突出贡献。

相信看到这些数据,你已经明白为何 Docker 备受关注的原因了。

Docker 的技术实现

这里我们再简单了解一下 Docker 的技术实现,以便有探索欲望的读者查找相关资料进行深入阅读。

Docker 的实现,主要归结于三大技术:命名空间 ( Namespaces ) 、控制组 ( Control Groups ) 和联合文件系统 ( Union File System ) 。

Namespace

命名空间是 Linux 核心在 2.4 版本后逐渐引入的一项用于运行隔离的模块。

相信很多开发者在不同的编程语言中都见过命名空间的概念,在这些编程语言中,命名空间的主要目的就是为了集合相同模块的类,区分不同模块间的同名类。

同样的道理,Linux 内核的命名空间,就是能够将计算机资源进行切割划分,形成各自独立的空间。

就实现而言,Linux Namespaces 可以分为很多具体的子系统,如 User Namespace、Net Namespace、PID Namespace、Mount Namespace 等等。

这里我们以进程为例,通过 PID Namespace,我们可以造就一个独立的进程运行空间,在其中进程的编号又会从 1 开始。在这个空间中运行的进程,完全感知不到外界系统中的其他进程或是其他进程命名空间中运行的进程。

利用 PID Namespace,Docker 就实现了容器中隔离程序运行中进程隔离这一目标。

Control Groups

资源控制组 ( 常缩写为 CGroups ) 是 Linux 内核在 2.6 版本后逐渐引入的一项对计算机资源控制的模块。

顾名思义,资源控制组的作用就是控制计算机资源的。与以隔离进程、网络、文件系统等虚拟资源为目的 Namespace 不同,CGroups 主要做的是硬件资源的隔离。

之前我们提到了,虚拟化除了制造出虚拟的环境隔离同一物理平台运行的不同程序之外,另一大作用就是控制硬件资源的分配,CGroups 的使用正是为了这样的目的。

需要再强调一次的是,CGroups 除了资源的隔离,还有资源分配这个关键性的作用。通过 CGroups,我们可以指定任意一个隔离环境对任意资源的占用值或占用率,这对于很多分布式使用场景来说是非常有用的功能。

例如,我们在服务器上部署一个业务服务和一个健康监控服务。通常情况下,监控服务只会占用很少的计算机资源,但我们无法保证其不会因为一些逻辑问题产生 Bug 进而过分消耗计算机资源。而它申请的计算机资源越多,意味着业务服务所能使用的计算机资源也就越少,最后甚至可能造成物理服务器的崩溃。

上述的问题在没有隔离实现的普通运行环境下是比较难解决的,因为所有不从系统层面出发的限制程序资源使用的方式都并不完全有效。由于 CGroups 实现于操作系统,而操作系统垄断着系统资源的分配,所以其完全能够限制隔离环境下应用的资源占有量。

Union File System

联合文件系统 ( Union File System ) 是一种能够同时挂载不同实际文件或文件夹到同一目录,形成一种联合文件结构的文件系统。联合文件系统本身与虚拟化并无太大的关系,但 Docker 却创新的将其引入到容器实现中,用它解决虚拟环境对文件系统占用过量,实现虚拟环境快速启停等问题。

在 Docker 中,提供了一种对 UnionFS 的改进实现,也就是 AUFS ( Advanced Union File System )。

AUFS 将文件的更新挂载到老的文件之上,而不去修改那些不更新的内容,这就意味着即使虚拟的文件系统被反复修改,也能保证对真实文件系统的空间占用保持一个较低水平。

也许这个表述还不够形象,那么我们来用 Git 进行比较,会让大家会更容易理解。大家知道,我们在 Git 中每进行一次提交,Git 并不是将我们所有的内容打包成一个版本,而只是将修改的部分进行记录,这样即使我们提交很多次后,代码库的空间占用也不会倍数增加。

同样的,通过 AUFS,Docker 大幅减少了虚拟文件系统对物理存储空间的占用。由此,Docker 也开创出了虚拟化领域很多新的轻量级解决方案,这在之后的小节里我们会提到。

Docker 的理念

在对 Docker 及其背后的一些技术有了一个初步了解之后,我们还要着重说一下 Docker 本身的一些设计理念。如果说熟悉 Docker 背后的技术能够更好的帮助你正确使用 Docker,那么理解 Docker 的理念将更好的指导你如何搭配 Docker 容器间的关系。

让我们先来从一张 Docker 官方提供的架构图来看看 Docker 对容器结构的设计。

与其他虚拟化实现甚至其他容器引擎不同的是,Docker 推崇一种轻量级容器的结构,即一个应用一个容器。

举个具体的例子,在常见的虚拟机实现中,我们要搭建一套 LAMP 结构的服务,我们通常会建立一个虚拟机,在虚拟机中安装上 Linux 系统,之后分别安装 Apache、MySQL 和 PHP。而在 Docker 里,最佳的实践是分别基于 Apache、MySQL 和 PHP 的镜像建立三个容器,分别运行 Apache、MySQL 和 PHP ,而它们所在的虚拟操作系统也直接共享于宿主机的操作系统。

如果我们将 Docker 的轻量级容器实现和虚拟机的一些参数进行对比,更容易得到结果。

属性

Docker

虚拟机

启动速度

秒级

分钟级

硬盘使用

MB 级

GB 级

性能

接近原生

较低

普通机器支撑量

数百个

几个

虽然这里只列出了一些 Docker 的优势项,但这些优势都是对我们开发中环境搭建和使用极其有帮助的内容。就拿启动速度来说,我们在开发中显然不愿意调整环境或更新代码后要等待几分钟来让其生效,Docker 秒级的启动速度几乎让我们感知不到我们对环境做了什么改动。而像虚拟机占用大量操作系统资源,导致我们本地开发使用电脑过慢 ( 有时候不得不将环境搭建在另外的机器上,但这显然在代码编写到运行自测的过程中增加很多工作量 ) 等问题,也容易得到解决。

当然,在 Docker 中能实现这样的设计理念,还要归功于几项基础设施的支持。

首先,只有在容器技术的支撑下,应用即容器的方案才能有效的实施。因为容器技术既剔除了 Hypervisor 层,又干掉了虚拟操作系统的概念,让容器中应用运行的消耗与真实操作系统中运行的消耗几乎完全一致。只有这样,我们才能像在真实操作系统中开启应用一样开启新的容器,而不用过分担心虚拟化带来的性能消耗。

其次,基于联合文件系统的底层文件系统支持,让容器能够很容易在真实操作系统中共享存储资源,并由此带来了对存储空间的低消耗。与动辄就要独立开辟十几 GB 甚至几十 GB 的虚拟化实现相比,要存在巨大的优势。

当然,Docker 也支持你在容器中同时运行很多种程序,但其容器设计本身并不针对这种方案,所以如果你以这种方案在 Docker 中搭建环境,你会花费不少时间做出一些本来并不需要做的事情。虽然这看上去动手性很强,但我并不推荐在工作中这么去做,因为我们使用 Docker 本身就是为了效率,浪费时间在这些不必要的事情上,已经违背了我们使用 Docker 的初衷。

我们能用 Docker 做些什么

从理论上我们已经知道 Docker 能够为我们的工作带来巨大的便利,那么将其放于实践中,我们应该如何正确的使用它呢?这里我摘录整理了一段来自 Docker 官方文档的指导意见,希望能够对大家的实践提供参考。

更快、更一致的交付你的应用程序

使用 Docker 后,开发者能够在本地容器中得到一套标准的应用或服务的运行环境,由此可以简化开发的生命周期 ( 减少在不同环境间进行适配、调整所造成的额外消耗 )。对于整个应用迭代来说,加入 Docker 的工作流程将更加适合持续集成 ( Continuous Integration ) 和持续交付 ( Continuous Delivery )。

举个具体的例子:

跨平台部署和动态伸缩

基于容器技术的 Docker 拥有很高的跨平台性,Docker 的容器能够很轻松的运行在开发者本地的电脑,数据中心的物理机或虚拟机,云服务商提供的云服务器,甚至是混合环境中。

同时,Docker 的轻量性和高可移植性能够很好的帮助我们完成应用的动态伸缩,我们可以通过一些手段近实时的对基于 Docker 运行的应用进行弹性伸缩,这能够大幅提高应用的健壮性。

让同样的硬件提供更多的产出能力

Docker 的高效和轻量等特征,为替代基于 Hypervisor 的虚拟机提供了一个经济、高效、可行的方案。在 Docker 下,你能节约出更多的资源投入到业务中去,让应用程序产生更高的效益。同时,如此低的资源消耗也说明了 Docker 非常适合在高密度的中小型部署场景中使用。

留言互动

在这节中,我们溯源 Docker 的历史,从其诞生的背景和其解决的问题出发,阐述了 Docker 背后的技术和 Docker 本身的设计理念。这里给大家留一道思考题:

Docker 所提倡的轻量级虚拟化与其他虚拟化实现中的完整操作系统虚拟化有什么样的优势,其优势又能应用到哪些实际的场景中去呢?

欢迎大家通过留言的方式说出你的看法。我会选出有代表性的优质留言,推荐给大家。

同时,如果你对 Docker 的发展历史,Docker 背后的技术或者 Docker 所推崇的理念还有不解之处,可以加入到这本小册的官方微信群中,参与对相关问题的讨论。