快乐学习
前程无忧、中华英才非你莫属!

Docker实战-Day1*(Docker基础)


要理解Docker是什么,从一个比喻开始会比技术性解释来得简单,而且这个Docker的比喻非常有说服力。Docker原本是指在船只停靠港口之后将商品移进或移出的工人。箱子和物品的大小和形状各异,而有经验的码头工人能以合算的方式手工将商品装入船只,因而他们倍受青睐(见图1-2)。雇人搬东西并不便宜,但除此之外别无选择。对在软件行业工作的人来说,这听起来应该很熟悉。大量时间和精力被花在将奇形怪状的软件放置到装满了其他奇形怪状软件、大小各异的船只上,以便将其卖给其他地方的用户或商业机构。

图1-3展示了使用Docker概念时如何能节省时间和金钱。

在Docker出现之前,部署软件到不同环境所需的工作量巨大。


即使不是采用手工运行脚本的方式在不同机器上进行软件配备(还是有很多人这么做),用户也不得不全力应付那些配置管理工具,它们掌管着渴求资源且快速变化的环境的状态。


即便将这些工作封装到虚拟机中,还是需要花费大量时间来部署这些虚拟机、等待它们启动并管理它们所产生的额外的资源开销。


使用Docker,配置工作从资源管理中分离了出来,而部署工作则是微不足道的:运行docker run,环境的镜像会被拉取下来并准备运行,所消耗的资源更少并且是内含的,因此不会干扰其他环境。


读者无须担心容器是将被分发到Red Hat机器、Ubuntu机器还是CentOS虚拟机镜像中,只要上面有Docker,就没有问题。

图1-3 使用Docker前后软件交付的对比

1.1.2 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如何能够提供帮助有了一定了解。在进入一个真实示例之前,让我们来了解一下几个核心概念。

1.1.3 关键的概念


在本节中,我们将介绍一些关键的Docker概念,如图1-4所示。


在开始运行Docker命令之前,将镜像、容器及层的概念牢记于心是极其有用的。


简而言之,容器运行着由镜像定义的系统。这些镜像由一个或多个层(或差异集)加上一些Docker的元数据组成。


让我们来看一些核心的Docker命令。我们将把镜像转变成容器,修改它们,并添加层到我们即将提交的新镜像中。

如果这一切听上去有点儿混乱,不用太担心。在本章结束时,一切都将更加清晰。



1.关键的Docker命令

Docker的中心功能是构建、分发及在任何具有Docker的地方运行软件。


对终端用户而言,Docker是一个用于运行的命令行程序。就像git(或任何源代码控制工具)一样,这个程序具有用于执行不同操作的子命令。表1-1中列出了将在宿主机上使用的主要的Docker子命令。

2.镜像与容器

如果读者不熟悉Docker,这可能是第一次听说本文所说的“容器”和“镜像”这两个词。

它们很可能是Docker中最重要的概念,因此有必要花点儿时间明确其差异。


在图1-5中,读者将看到这些概念的展示,里面有从一个基础镜像启动的3个容器。

看待镜像和容器的一种方式是将它们类比成程序与进程。一个进程可以视为一个被执行的应用程序,同样,一个Docker容器可以视为一个运行中的Docker镜像。


如果读者熟悉面向对象原理,看待镜像和容器的另一种方法是将镜像看作类而将容器看作对象。

对象是类的具体实例,同样,容器是镜像的实例。用户可以从单个镜像创建多个容器,就像对象一样,它们之间全都是相互隔离的。


不论用户在对象内修改了什么,都不会影响类的定义--------它们从根本上就是不同的东西。

1.2 构建一个Docker应用程序

现在,我们要动手使用Docker来构建一个简单的“to-do”应用程序(todoapp)镜像了。在这个过程中,读者会看到一些关键的Docker功能,如Dockerfile、镜像复用、端口暴露及构建自动化。这是接下来10分钟读者将学到的东西:如何使用Dockerfile来创建Docker镜像;如何为Docker镜像打标签以便引用; 如何运行新建的Docker镜像。

to-do应用是协助用户跟踪待完成事项的一个应用程序。我们所构建的应用将存储并显示可被标记为已完成的信息的简短字符串,它以一个简单的网页界面呈现。图1-6展示了如此操作将得到的结果。

图1-6 构建一个Docker应用程序

应用程序的细节不是重点。


我们将演示从我们所提供的一个简短的Dockerfile开始,读者可以放心地在自己的宿主机上使用与我们相同的方法构建、运行、停止和启动一个应用程序,而无须考虑应用程序的安装或依赖。这正是 Docker 赋予我们的关键部分——可靠地重现并简便地管理和分享开发环境。这意味着用户无须再遵循并迷失在那些复杂的或含糊的安装说明中。

1.2.1 创建新的Docker镜像的方式创建Docker镜像有4种标准的方式。表1-2逐一列出了这些方法。

如果用户所做的是概念验证以确认安装过程是否正常,那么第一种“手工”方式是没问题的。


在这个过程中,用户应对所采取的步骤做记录,以便在需要时回到同一点上。

到某个时间点,用户会想要定义创建镜像的步骤。这就是第二种方式(也就是我们这里所用的方式)。


对于更复杂的构建,用户需要使用第三种方式,特别是在Dockerfile功能还不足以满足镜像要求的时候。


最后一种方式从一个空镜像开始,通过叠加一组运行镜像所需要的文件进行构建。

如果用户想导入一组在其他地方创建好的自包含的文件,这将非常有用,不过这在主流应用中非常罕见。


现在,我们来看一下Dockerfile方法,其他方法将在本书后面再说明。


1.2.2 编写一个DockerfileDockerfile是一个包含一系列命令的文本文件。


本示例中我们将使用的Dockerfile如下:

FROM node (1)

MAINTAINER ian.miell@gmail.com (2)

RUN git clone -q https://github.com/docker-in-practice/todo.git (3)

WORKDIR todo (4)

RUN npm install > /dev/null (5)

EXPOSE 8000 (6)

CMD ["npm","start"] (7)

(1)❶定义基础镜像

(2)❷声明维护人员

(3)❸克隆todoapp代码

(4)❹移动到新的克隆目录

(5)❺运行node包管理器的安装命令(npm)

(6)❻指定从所构建的镜像启动的容器需要监听这个端口

(7)❼指定在启动时需要运行的命令


Dockerfile的开始部分是使用FROM命令定义基础镜像❶。


本示例使用了一个Node.js镜像以便访问Node.js程序。官方的Node.js镜像名为node。接下来,使用MAINTAINER命令声明维护人员❷。


在这里,我们我们使用的是其中一个人的电子邮件地址,读者也可以替换成自己的,因为现在它是你的Dockerfile了。


这一行不是创建可工作的Docker镜像所必需的,不过将其包含进来是一个很好的做法。到这个时候,构建已经继承了node容器的状态,读者可以在它上面做操作了。

接下来,使用RUN命令克隆todoapp代码❸。这里使用指定的命令获取应用程序的代码:在容器内运行git。


在这个示例中,Git是安装在基础node镜像里的,不过读者不能对这类事情做假定。现在移动到使用WORKDIR命令新克隆的目录中❹。这不仅会改变构建环境中的目录,最后一条WORKDIR命令还决定了从所构建镜像启动容器时用户所处的默认目录。


接下来,运行node包管理器的安装命令(npm)❺。这将为应用程序设置依赖。我们对输出的信息不感兴趣,所以将其重定向到/dev/null上。


由于应用程序使用了8000端口,使用EXPOSE命令告诉Docker从所构建镜像启动的容器应该监听这个端口❻。最后,使用CMD命令告诉Docker在容器启动时将运行哪条命令❼。


这个简单的示例演示了Docker及Dockerfile的几个核心功能。Dockerfile是一组严格按顺序运行的有限的命令集的简单序列。


它们影响了最终镜像的文件和元数据。这里的RUN命令通过签出并安装应用程序影响了文件系统,而EXPOSE、CMD和WORKDIR命令影响了镜像的元数据。


1.2.3 构建一个Docker镜像读者已经定义了自己的Dockerfile的构建步骤。



现在可以键入图1-7所示的命令从而构建Docker镜像了

读者将看到类似下面这样的输出:


Sending build context to Docker daemon 178.7 kB (1)

Sending build context to Docker daemon Step 0 : FROM node (2)

(1)Docker会上传docker build指定目录下的文件和目录

(2)每个构建步骤从 0 开始按顺序编号,并与命令一起输出

(3)每个命令会导致一个新镜像被创建出来,其镜像ID在此输出

(4)为节省空间,在继续前每个中间容器会被移除

(5)构建的调试信息在此输出(限于篇幅,本代码清单做了删减)

(6)此次构建的最终镜像ID,可用于打标签现在,拥有了一个具有镜像ID(前面示例中的“66c76cea05bb”,不过读者的ID会不一样)的Docker镜像。总是引用这个ID会很麻烦,可以为其打标签以方便引用。


输入图1-8所示的命令,将66c76cea05bb替换成读者生成的镜像ID.

现在就能从一个Dockerfile构建自己的Docker镜像副本,并重现别人定义的环境了




1.2.4 运行一个Docker容器


读者已经构建出Docker镜像并为其打上了标签。现在可以以容器的形式来运行它了:


docker run -p 8000:8000 --name example1 todoapp (1)    

npm install   

npm info it worked if it ends with ok   

npm info using npm@2.14.4   

npm info using node@v4.1.1   

npm info prestart todomvc-swarm@0.0.1   

> todomvc-swarm@0.0.1 prestart /todo (2)   

> make all

npm install   

npm info it worked if it ends with ok   

npm info using npm@2.14.4   

npm info using node@v4.1.1   

npm WARN package.json todomvc-swarm@0.0.1 No repository field.   

npm WARN package.json todomvc-swarm@0.0.1 license should be a    

➥ valid SPDX license expression   

npm info preinstall todomvc-swarm@0.0.1   

npm info package.json statics@0.1.0 license should be a valid    

➥ SPDX license expression   

npm info package.json react-tools@0.11.2 No license field.   

npm info package.json react@0.11.2 No license field.

npm info package.json node-jsx@0.11.0 license should be a valid    

➥ SPDX license expression  

npm info package.json ws@0.4.32 No license field.   

npm info build /todo   

npm info linkStuff todomvc-swarm@0.0.1   

npm info install todomvc-swarm@0.0.1   

npm info postinstall todomvc-swarm@0.0.1   

npm info prepublish todomvc-swarm@0.0.1   

npm info ok   

if [ ! -e dist/ ]; then mkdir dist; fi   

cp node_modules/react/dist/react.min.js dist/react.min.js

LocalTodoApp.js:9:   

// TODO: default english version   

LocalTodoApp.js:84:        

fwdList =    

➥ this.host.get('/TodoList#'+listId); // TODO fn+id sig   

TodoApp.js:117:   // TODO scroll into view   

TodoApp.js:176:   if (i>=list.length()) { i=list.length()-1;}   

 ➥ // TODO .length   local.html:30:  <!-- TODO 2-split, 3-split -->   

model/TodoList.js:29:    

➥ // TODO one op - repeated spec? long spec?   

view/Footer.jsx:61:    // TODO: show the entry&apos;s metadata   

view/Footer.jsx:80:   

todoList.addObject(new TodoItem());    

➥ // TODO create default   view/Header.jsx:25:    

➥ // TODO list some meaningful header (apart from the id) npm info start todomvc-swarm@0.0.1

 > todomvc-swarm@0.0.1 start /todo 

> node TodoAppServer.js Swarm server started port 8000

 ^C (3)                  

$ docker ps –a (4)            

CONTAINER ID IMAGE     COMMAND     CREATED   

 ➥ STATUS          PORTS NAMES b9db5ada0461 todoapp:latest "npm start" 2 minutes ago 

 ➥ Exited (130) 2 minutes ago example1 

$ docker start example1 (5)     

example1

docker ps -a 

CONTAINER ID  IMAGE      COMMAND  CREATED 

➥ STATUS      PORTS         NAMES 

b9db5ada0461 todoapp:latest "npm start" 8 minutes ago 

➥ Up 10 seconds 0.0.0.0:8000->8000/

tcp example1 (6)

 $ docker diff example1 (7)            

 C /todo (8)              

 A /todo/.swarm 

 A /todo/.swarm/TodoItem 

 A /todo/.swarm/TodoItem/1tlOc02+A~4Uzcz (9)     

 A /todo/.swarm/_log   

 A /todo/dist A /todo/dist/LocalTodoApp.app.js

 A /todo/dist/TodoApp.app.js 

 A /todo/dist/react.min.js

   


(1)❶docker run子命令启动容器,-p将容器的 8000 端口映射到宿主机的8000端口上,--name给容器赋予一个唯一的名字,最后一个参数是镜像

(2)容器的启动进程的输出被发送到终端中

(3)❷在此按Ctrl+C终止进程和容器

(4)❸运行这个命令查看已经启动和移除的容器,以及其ID和状态(就像进程一样)

(5)❹重新启动容器,这次是在后台运行

(6)❺再次运行ps命令查看发生变化的状态

(7)❻docker diff子命令显示了自镜像被实例化成一个容器以来哪些文件受到了影响

(8)❼修改了/todo目录

(9)❽增加了/todo/.swarm目录docker run子命令启动容器❶。-p标志将容器的8000端口映射到宿主机的8000端口上,读者现在应该可以使用浏览器访问http://localhost:8000来查看这个应用程序了。--name标志赋予了容器一个唯一的名称,以便后面引用。最后的参数是镜像名称。一旦容器启动,我们按下CTRL-C终止进程和容器❷。读者可以运行ps命令查看被启动且未被移除的容器❸。注意,每个容器都具有自己的容器 ID 和状态,与进程类似。它的状态是Exited(已退出),不过读者可以重新启动它❹。这么做之后,注意状态已经改变为Up(运行中),且容器到宿主机的端口映射现在也显示出来了❺。docker diff子命令显示了自镜像被实例化成一个容器以来哪些文件受到了影响❻。在这个示例中,修改了todo目录❼,并增加了其他列出的文件❽。没有文件被删除,这是另一种可能性。如读者所见,Docker“包含”环境的事实意味着用户可以将其视为一个实体,在其上执行的动作是可预见的。这赋予了Docker宽广的能力——用户可以影响从开发到生产再到维护的整个软件生命周期。


这种改变正是本书所要描述的,在实践中展示Docker所能完成的东西。接下来读者将了解Docker的另一个核心概念——分层。


1.2.5 Docker分层

Docker分层协助用户管理在大规模使用容器时会遇到的一个大问题。想象一下,如果启动了数百甚至数千个to-do应用,并且每个应用都需要将文件的一份副本存储在某个地方。可想而知,磁盘空间会迅速消耗光!

默认情况下,Docker在内部使用写时复制(copy-on-write)机制来减少所需的硬盘空间量(见图 1-9)。每当一个运行中的容器需要写入一个文件时,它会通过将该项目复制到磁盘的一个新区域来记录这一修改。在执行Docker提交时,这块磁盘新区域将被冻结并记录为具有自身标识符的一个层。

图1-9 启动时复制与写时复制对比这部分解释了Docker容器为何能如此迅速地启动——它们不需要复制任何东西,因为所有的数据已经存储为镜像。



写时复制 写时复制是计算技术中使用的一种标准的优化策略。在从模板创建一个新的(任意类型)对象时,只在数据发生变化时才能将其复制进来,而不是复制整个所需的数据集。依据用例的不同,这能省下相当可观的资源。


图1-10展示了构建的to-do应用,它具有我们所感兴趣的3层。因为层是静态的,所以用户只需要在想引用的镜像之上进行构建,变更的内容都在上一层中。在这个to-do应用中,我们从公开可用的node镜像构建,并将变更叠加在最上层。

所有这3层都可以被多个运行中的容器共享,就像一个共享库可以在内存中被多个运行中的进程共享一样。对于运维人员来说,这是一项至关重要的功能,可以在宿主机上运行大量基于不同镜像的容器,而不至于耗尽磁盘空间。


想象一下,你将所运行的to-do应用作为在线服务提供给付费用户,可以将服务扩散给大量用户。


如果你正在开发中,可以一下在本地机器上启动多个不同的环境。如果你正在进行测试,可以比之前同时运行更多测试,速度也更快。


有了分层,所有这些东西都成为了可能。


通过使用Docker构建和运行一个应用程序,读者开始见识到Docker能给工作流带来的威力。重现并分享特定的环境,并能在不同的地方落地,让用户在开发过程中兼具灵活性和可控性。



1.3 小结依据读者以往的Docker经验不同,本章可能存在一个陡峭的学习曲线。我们在短时间内讨论了大量的基础内容。读者现在应该:理解Docker镜像是什么;


知道Docker分层是什么,以及为什么它很有用;能够从一个基础镜像提交一个新的Docker镜像;知道Dockerfile是什么。

我们已经使用这项知识:创建了一个有用的应用程序;毫不费力地重现了一个应用程序中的状态。


接下来,我们将介绍几个有助于读者理解Docker如何工作的技巧,然后讨论有关Docker用法的一些更广泛的技术争论。前两个介绍性的章节构成了本书其余部分的基础,后者涵盖了从开发到生产的全过程,展示如何使用Docker来提升工作流。

来源:Docker实战  作者:Jeff Nickoloff

打赏
赞(0) 打赏
未经允许不得转载:同乐学堂 » Docker实战-Day1*(Docker基础)

特别的技术,给特别的你!

联系QQ:1071235258QQ群:710045715

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

error: Sorry,暂时内容不可复制!