Docker是什么?
要理解Docker是什么,从一个比喻开始会比技术性解释来得简单,而且这个Docker的比喻非常有说服力。Docker这个单词原本是指在船只停靠港口之后将商品移进或移出的码头工人。箱子和物品的大小、形状各异,而有经验的码头工人能以合算的方式手工将商品装入船只,因而他们倍受青睐(见图1)。雇人搬东西并不便宜,但除此之外别无他法。
对在软件行业工作的人来说,这听起来应该很熟悉。大量时间和精力被花在将奇形怪状的软件放置到装满了其他奇形怪状的软件、大小各异的船只上,以便将其卖给其他地方的用户或商业机构。
图1-3展示了使用Docker概念时如何能节省时间和金钱。在Docker出现之前,部署软件到不同环境所需的工作量巨大。即使不是采用手工运行脚本的方式在不同机器上进行软件配备(有很多人这么做),用户也不得不全力应付那些配置管理工具,它们掌管着渴求资源且快速变化的环境的状态。即便将这些工作封装到虚拟机中,还是需要花费大量时间来部署这些虚拟机,等待它们启动并管理它们所产生的额外的资源开销。
图1(标准化集装箱前后的航运对比)
使用Docker,配置工作从资源管理中分离了出来,而部署工作则不值一提:执行docker run,环境的镜像会被拉取下来并准备运行,所消耗的资源更少并且是内含的,因此不会干扰其他环境。
读者无须担心容器是被分发到Red Hat机器、Ubuntu机器还是CentOS虚拟机镜像中,只要上面有Docker,就能良好地运行。
图1(使用Docker前后软件交付的对比)
Docker有什么好处?
几个重要的实际问题出现了:为什么要使用Docker?Docker用在什么地方?针对“为什么”的简要答案是:只需要一点点付出,Docker就能快速为企业节省大量金钱。部分方法(肯定不是所有的)将在随后的几节中讨论。我们已经在实际工作环境中切身体会到所有这些益处。
1. 代替虚拟机(VM)
Docker可以在很多情况下替代虚拟机。如果用户只关心应用程序而不是操作系统,可以用Docker替代虚拟机,把操作系统交给其他人去考虑。Docker不仅启动速度比虚拟机快,迁移时也更为轻量,同时得益于它的分层文件系统,与其他人共享变更时更简单、更快捷。而且,它牢牢地扎根在命令行中,非常适合脚本化。
2. 软件原型
如果想快速体验软件,同时避免干扰目前的设置或配备一台虚拟机带来的麻烦,Docker可以在几毫秒内提供一个沙盒环境。在亲身体验之前,用户很难感受到这种解放的效果。
3. 打包软件
因为对Linux用户而言,Docker镜像实际上没有依赖,所以非常适合用于打包软件。用户可以构建镜像,并确保它可以运行在任何现代Linux机器上——就像Java一样,但不需要JVM。
4. 让微服务架构成为可能
Docker 有助于将一个复杂系统分解成一系列可组合的部分,这让用户可以用更离散的方式来思考其服务。用户可以在不影响全局的前提下重组软件,使其各部分更易于管理和可插拔。
5. 网络建模
由于可以在一台机器上启动数百个(甚至数千个)相互隔离的容器,因此对网络进行建模轻而易举。这对于现实世界场景的测试非常有用,而且所费无几。
6. 离线时启用全栈生产力
因为可以将系统的所有部分捆绑在Docker容器中,所以用户可以将其编排运行在笔记本电脑中移动办公,即便在离线时也毫无问题。
7. 降低调试支出
不同团队之间关于软件交付的复杂谈判在业内司空见惯。我们亲身经历过不计其数的这类讨论:失效的库、有问题的依赖、更新被错误实施或是执行顺序有误,甚至可能根本没执行以及无法重现的错误等。估计读者也遇到过这些问题。Docker让用户可以清晰地说明(即便是以脚本的形式)在一个属性已知的系统上调试问题的步骤,错误和环境重现变得更简单,而且通常与所提供的宿主机环境是分离的。
8. 文档化软件依赖及接触点
通过使用结构化方式构建镜像,为迁移到不同环境做好准备,Docker 强制用户从一个基本出发点开始明确地记录软件依赖。即使用户不打算在所有地方都使用Docker,这种文档记录也有助于在其他地方安装软件。
9. 启用持续交付
持续交付(continuous delivery,CD)是一个基于流水线的软件交付范型,该流水线通过一个自动化(或半自动化)流程在每次变动时重新构建系统然后交付到生产环境中。
因为用户可以更准确地控制构建环境的状态,Docker 构建比传统软件构建方法更具有可重现性和可复制性。使持续交付的实现变得更容易。通过实现一个以Docker为中心的可重现的构建过程,标准的持续交付技术,如蓝/绿部署(blue/green deployment,在生产环境中维护“生产”和“最新”部署)和凤凰部署(phoenix deployment,每次发布时都重新构建整个系统)变得很简单。
关键概念
现在,我们对Docker如何能够提供帮助有了一定了解。在进入一个真实示例之前,让我们来了解几个核心概念。
图3(Docker的核心概念)
在开始执行Docker命令之前,将镜像、容器及层的概念牢记于心是极其有用的。简而言之,容器运行着由镜像定义的系统。这些镜像由一个或多个层(或差异集)加上一些Docker的元数据组成。
让我们来看一些核心的Docker命令。我们将把镜像转变成容器,修改它们,并添加层到我们即将提交的新镜像中。如果这一切听上去有点儿混乱,不用太担心。在本章结束时,一切都将更加清晰。
1. 关键的Docker命令
Docker的中心功能是构建、分发及在任何具有Docker的地方运行软件。对终端用户而言,Docker是他们运行的一个命令行程序。就像Git(或任何源代码控制工具)一样,这个程序具有用于执行不同操作的子命令。表1-1(略)中列出了将在宿主机上使用的主要的Docker子命令。
2. 镜像与容器
如果读者不熟悉Docker,可能这是第一次听说本书所说的“容器”和“镜像”这两个词。它们很可能是Docker中最重要的概念,因此有必要花点儿时间明确其差异。在图4中,读者将看到这些概念的展示,里面是从一个基础镜像启动的3个容器。
图4(Docker镜像与容器)
看待镜像和容器的一种方式是将它们类比成程序与进程。一个进程可以视为一个“被执行的应用程序”,同样,一个Docker容器可以视为一个运行中的Docker镜像。
如果读者熟悉面向对象原理,看待镜像和容器的另一种方法是将镜像看作类而将容器看作对象。对象是类的具体实例,同样,容器是镜像的实例。用户可以从单个镜像创建多个容器,就像对象一样,它们之间全都是相互隔离的。不论用户在对象内修改了什么,都不会影响类的定义——它们从根本上就是不同的东西。