社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
关注【异步图书】微信公众号,每日赠送新书
本文摘自:人民邮电出版社异步社区《Docker实战》
本文主要内容
Docker是什么
Docker的使用以及它如何能节省时间和金钱
容器与镜像之间的区别
Docker的分层特性
使用Docker构建并运行一个to-do应用程序
Docker是一个允许用户“在任何地方构建、分发及运行任何应用”的平台。它在极短的时间内发展壮大,目前已经被视为解决软件中最昂贵的方面之一——部署的一个标准方法。
在Docker出现之前,开发流水线通常由用于管理软件活动的不同技术组合而成,如虚拟机、配置管理工具、不同的包管理系统以及各类依赖库复杂的网站。所有这些工具需要由专业的工程师管理和维护,并且多数工具都具有自己独特的配置方式。
Docker改变了这一切,允许不同的工程师参与到这个过程中,有效地使用同一门语言,这让协作变得轻而易举。所有东西通过一个共同的流水线转变成可以在任何目标平台上使用的单一的产出——无须继续维护一堆让人眼花缭乱的工具配置,如图1-1所示。
与此同时,只要现存的软件技术栈依然有效,用户就无须抛弃它——用户可以将其原样打包到一个Docker容器内供其他人使用。由此获得的额外好处是,用户能看到这些容器是如何构建的,因此如果需要深入其细节,完全没问题。
在本文中,读者将了解到Docker是什么、为什么它很重要,并开始使用它。
图1-1 Docker如何消除了工具维护的负担
在动手实践之前,我们将对Docker稍做讨论,以便读者了解它的背景、“Docker”名字的来历以及为什么使用它!
要理解Docker是什么,从一个比喻开始会比技术性解释来得简单,而且这个Docker的比喻非常有说服力。Docker原本是指在船只停靠港口之后将商品移进或移出的工人。箱子和物品的大小和形状各异,而有经验的码头工人能以合算的方式手工将商品装入船只,因而他们倍受青睐(见图1-2)。雇人搬东西并不便宜,但除此之外别无选择。
对在软件行业工作的人来说,这听起来应该很熟悉。大量时间和精力被花在将奇形怪状的软件放置到装满了其他奇形怪状软件、大小各异的船只上,以便将其卖给其他地方的用户或商业机构。
图1-2 标准化集装箱前后的航运对比
在Docker出现之前,部署软件到不同环境所需的工作量巨大。即使不是采用手工运行脚本的方式在不同机器上进行软件配备(还是有很多人这么做),用户也不得不全力应付那些配置管理工具,它们掌管着渴求资源且快速变化的环境的状态。即便将这些工作封装到虚拟机中,还是需要花费大量时间来部署这些虚拟机、等待它们启动并管理它们所产生的额外的资源开销。
使用Docker,配置工作从资源管理中分离了出来,而部署工作则是微不足道的:运行docker run
,环境的镜像会被拉取下来并准备运行,所消耗的资源更少并且是内含的,因此不会干扰其他环境。
读者无须担心容器是将被分发到Red Hat机器、Ubuntu机器还是CentOS虚拟机镜像中,只要上面有Docker,就没有问题。
图1-3展示了使用Docker概念时如何能节省时间和金钱。
图1-3 使用Docker前后软件交付的对比
几个重要的实际问题出现了:为什么要使用Docker,Docker用在什么地方?针对“为什么”的简要答案是:只需要一点点付出,Docker就能快速为企业节省大量金钱。部分方法(肯定不是所有的)将在随后的几节中讨论。我们已经在实际工作环境中切身体会到所有这些益处。
Docker可以在很多情况下替代虚拟机。如果用户只关心应用程序,而不是操作系统,可以用Docker替代虚拟机,并将操作系统交给其他人去考虑。Docker不仅启动速度比虚拟机快,迁移时也更为轻量,同时得益于它的分层文件系统,与其他人分享变更时更简单、更快捷。而且,它牢牢地扎根在命令行中,非常适合脚本化。
如果想快速体验软件,同时避免干扰目前的设置或配备一个虚拟机的麻烦,Docker可以在几毫秒内提供一个沙箱环境。在亲身体验之前,很难感受到这种解放的效果。
因为对Linux用户而言,Docker镜像实际上没有依赖,所以非常适合用于打包软件。用户可以构建镜像,并确保它可以运行在任何现代Linux机器上——就像Java一样,但不需要JVM。
Docker 有助于将一个复杂系统分解成一系列可组合的部分,这让用户可以用更离散的方式来思考其服务。用户可以在不影响全局的前提下重组软件使其各部分更易于管理和可插拔。
由于可以在一台机器上启动数百个(甚至数千个)隔离的容器,因此对网络进行建模轻而易举。这对于现实世界场景的测试非常有用,而且所费无几。
因为可以将系统的所有部分捆绑在Docker容器中,所以用户可以将其编排运行在笔记本电脑中移动办公,即便在离线时也没问题。
不同团队之间关于软件交付的复杂谈判在业内司空见惯。我们亲身经历过不计其数的这类讨论:失效的库、有问题的依赖、更新被错误实施或是执行顺序有误,甚至可能根本没执行以及无法重现的错误等。估计读者也遇到过。Docker让用户可以清晰地说明(即便是脚本的形式)在一个属性已知的系统上调试问题的步骤,错误和环境重现变得更简单,而且通常与所提供的宿主机环境是分离的。
通过使用结构化方式构建镜像,为迁移到不同环境做好准备,Docker 强制用户从一个基本出发点开始明确地记录软件依赖。即使用户不打算在所有地方都使用Docker,这种对文档记录的需要也有助于在其他地方安装软件。
持续交付(continuous delivery,CD)是一个基于流水线的软件交付范型,该流水线通过一个自动化(或半自动化)流程在每次变动时重新构建系统然后交付到生产环境中。
因为用户可以更准确地控制构建环境的状态,Docker 构建比传统软件构建方法更具有可重现性和可复制性。使持续交付的实现变得更容易。通过实现一个以Docker为中心的可重现的构建过程,标准的持续交付技术,如蓝/绿部署(blue/green deployment,在生产环境中维护“生产”和“最新”部署)和凤凰部署(phoenix deployment,每次发布时都重新构建整个系统)变得很简单。
现在,读者对Docker如何能够提供帮助有了一定了解。在进入一个真实示例之前,让我们来了解一下几个核心概念。
在本节中,我们将介绍一些关键的Docker概念,如图1-4所示。
图1-4 Docker的核心概念
在开始运行Docker命令之前,将镜像、容器及层的概念牢记于心是极其有用的。简而言之,容器
运行着由镜像
定义的系统。这些镜像由一个或多个层
(或差异集)加上一些Docker的元数据组成。
让我们来看一些核心的Docker命令。我们将把镜像转变成容器,修改它们,并添加层到我们即将提交的新镜像中。如果这一切听上去有点儿混乱,不用太担心。在本文结束时,一切都将更加清晰。
Docker的中心功能是构建、分发及在任何具有Docker的地方运行软件。
对终端用户而言,Docker是一个用于运行的命令行程序。就像git(或任何源代码控制工具)一样,这个程序具有用于执行不同操作的子命令。
表1-1中列出了将在宿主机上使用的主要的Docker子命令。
表1-1 Docker子命令
命 令 | 目 的 |
| 构建一个Docker镜像 |
| 以容器形式运行一个Docker镜像 |
| 将一个Docker容器作为一个镜像提交 |
| 给一个Docker镜像打标签 |
如果读者不熟悉Docker,这可能是第一次听说本文所说的“容器”和“镜像”这两个词。它们很可能是Docker中最重要的概念,因此有必要花点儿时间明确其差异。
在图1-5中,读者将看到这些概念的展示,里面有从一个基础镜像启动的3个容器。
图1-5 Docker镜像与容器
看待镜像和容器的一种方式是将它们类比成程序与进程。一个进程可以视为一个被执行的应用程序,同样,一个Docker容器可以视为一个运行中的Docker镜像。
如果读者熟悉面向对象原理,看待镜像和容器的另一种方法是将镜像看作类而将容器看作对象。对象是类的具体实例,同样,容器是镜像的实例。用户可以从单个镜像创建多个容器,就像对象一样,它们之间全都是相互隔离的。不论用户在对象内修改了什么,都不会影响类的定义——它们从根本上就是不同的东西。
现在,我们要动手使用Docker来构建一个简单的“to-do”应用程序(todoapp)镜像了。在这个过程中,读者会看到一些关键的Docker功能,如Dockerfile、镜像复用、端口暴露及构建自动化。这是接下来10分钟读者将学到的东西:
如何使用Dockerfile来创建Docker镜像;
如何为Docker镜像打标签以便引用;
如何运行新建的Docker镜像。
to-do应用是协助用户跟踪待完成事项的一个应用程序。我们所构建的应用将存储并显示可被标记为已完成的信息的简短字符串,它以一个简单的网页界面呈现。
图1-6展示了如此操作将得到的结果。
图1-6 构建一个Docker应用程序
应用程序的细节不是重点。我们将演示从我们所提供的一个简短的Dockerfile开始,读者可以放心地在自己的宿主机上使用与我们相同的方法构建、运行、停止和启动一个应用程序,而无须考虑应用程序的安装或依赖。这正是 Docker 赋予我们的关键部分——可靠地重现并简便地管理和分享开发环境。这意味着用户无须再遵循并迷失在那些复杂的或含糊的安装说明中。
to-do应用程序 这个to-do应用程序将贯穿本文,多次使用,它非常适合用于实践和演示,因此值得读者熟悉一下。
创建Docker镜像有4种标准的方式。表1-2逐一列出了这些方法。
表1-2 创建Docker镜像的方式
方 法 | 描 述 | 详见技巧 |
Docker命令/“手工” | 使用 | 详见技巧14 |
Dockerfile | 从一个已知基础镜像开始构建,并指定一组有限的简单命令来构建 | 稍后讨论 |
Dockerfile及配置管理(configuration management,CM)工具 | 与Dockerfile相同,不过将构建的控制权交给了更为复杂的CM工具 | 详见技巧47 |
从头创建镜像并导入一组文件 | 从一个空白镜像开始,导入一个含有所需文件的TAR文件 | 详见技巧10 |
如果用户所做的是概念验证以确认安装过程是否正常,那么第一种“手工”方式是没问题的。在这个过程中,用户应对所采取的步骤做记录,以便在需要时回到同一点上。
到某个时间点,用户会想要定义创建镜像的步骤。这就是第二种方式(也就是我们这里所用的方式)。
对于更复杂的构建,用户需要使用第三种方式,特别是在Dockerfile功能还不足以满足镜像要求的时候。
最后一种方式从一个空镜像开始,通过叠加一组运行镜像所需要的文件进行构建。如果用户想导入一组在其他地方创建好的自包含的文件,这将非常有用,不过这在主流应用中非常罕见。
现在,我们来看一下Dockerfile方法,其他方法将在本文后面再说明。
Dockerfile是一个包含一系列命令的文本文件。本示例中我们将使用的Dockerfile如下:
FROM node ⇽--- ❶ 定义基础镜像
MAINTAINER ian.miell@gmail.com ⇽--- ❷ 声明维护人员
RUN git clone -q https://github.com/docker-in-practice/todo.git ⇽--- ❸ 克隆todoapp代码
WORKDIR todo ⇽--- ❹ 移动到新的克隆目录
RUN npm install > /dev/null ⇽--- ❺ 运行node包管理器的安装命令(npm)
EXPOSE 8000 ⇽--- ❻ 指定从所构建的镜像启动的容器需要监听这个端口
CMD ["npm","start"] ⇽--- ❼ 指定在启动时需要运行的命令
Dockerfile的开始部分是使用FROM
命令定义基础镜像❶。本示例使用了一个Node.js镜像以便访问Node.js程序。官方的Node.js镜像名为node
。
接下来,使用MAINTAINER
命令声明维护人员❷。在这里,我们我们使用的是其中一个人的电子邮件地址,读者也可以替换成自己的,因为现在它是你的Dockerfile了。这一行不是创建可工作的Docker镜像所必需的,不过将其包含进来是一个很好的做法。到这个时候,构建已经继承了node容器的状态,读者可以在它上面做操作了。
接下来,使用RUN
命令克隆todoapp代码❸。这
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!