Docker for 开发:容器化你的应用

CinUIS 8年前
   <p>这篇文章以极简的模式快速搭建一套使用与开发的Docker环境,特别面向了开发中常用的Mac和Windows环境。虽然本文模糊了大量的实现细节,但可作为在开发平台搭建Docker环境有力范本。</p>    <p>作为开发者,我们总是寻找一个捷径或更容易的方法来快速起步,对吧?如果你是团队领导,付出最少的代价让团队站在同一个起点是很重要的。 Docker可以提供帮助。</p>    <p>在软件开发领域,一直越来越强调模块化,从单一责任的一般原则到更具体的实现,例如将JavaScript功能模块化为无状态组件。在这里,我将告诉你我们如何使用Docker来模块化我们的开发环境,以获得许多类似的好处,包括帮助我们快速起步。</p>    <h3>Docker 4 开发者</h3>    <p>如你所知,为了加快项目进度,你有一个待办事项清单:</p>    <ul>     <li>拉取代码基线</li>     <li>安装外部工具,如数据库、缓存和一些额外的工具和服务</li>     <li>给外部工具打补丁和升级</li>     <li>配置数据库和服务以使他们可以通信</li>     <li>开始祈祷(可能需要好几次)</li>     <li>调试问题(至少要30几次)</li>    </ul>    <p>想象一下,如果在将应用程序的代码基线拉出存储库之后,您只需运行几个命令行(可能只有一个),可以使整个应用程序的环境准备就绪。听起来很酷,对吧?</p>    <p>这正是我们要完成的。不同于使用一个百科全书式的方法来使用Docker的所有功能和命令,我将介绍容器化开发人员的环境的主要的Docker功能。</p>    <p>这篇文章是关于利用Docker的容器化能力的一系列文章中的第一篇,它很容易构建一个可以在任何时间共享和运行的应用程序开发环境。</p>    <p>Docker Toolbox 对 Docker for X</p>    <p>Docker Toolbox是可用于处理多个Docker资源的原始工具集合,并且会根据您选择的操作系统而有所不同。但在那之后,他们发布了新的Windows和Mac原生应用程序。因此,除了Linux、OS X和Windows Docker Toolbox变体之外,您还将看到“Doc​​ker for Mac”和“Docker for Windows”。</p>    <p>要理解和决定应该使用哪种工具,我想概述使用新的原生应用程序的前提。原来的Docker Toolbox将会设置一些工具以及使用 <a href="/misc/goto?guid=4958197804256711547" rel="nofollow,noindex">VirtualBox</a> 。它还将提供在Linux Hypervisor上运行的虚拟机,用于Windows或Mac。</p>    <p>VirtualBox、Hyper-V和Hyperkit,我的上帝啊!</p>    <p>原生应用程序,例如在Docker for Mac中,实际安装的是OS X原生应用程序。它也不再使用VirtualBox,而是使用OS X的hypervisor <strong>hyperkit</strong> 。此外,共享接口和网络的管理更简单。还有一些用户体验更新以便于Docker更好的工作。在Docker for Windows原生应用程序中也会包含这些相同的更改,但是会使用Hyper-V作为hypervisor,以及其他类似的网络和工具更新的主机。</p>    <p>最后,原生应用的体验应该是一个更积极,更有效率的,而且更少的出错。但是,您会发现绝大多数与Toolbox相关的外部文档 - 因此了解Toolbox将是你的优势。</p>    <h3>开始吧</h3>    <p>这个初始教程将简单地使用一个开箱即用的express.js应用程序。当我们开始编辑源代码并在容器之间进行通信时,我将介绍一个运行Webpack的开发服务器的进阶的通用React.js应用程序,其中包含热模块重新加载,MongoDB数据库等。</p>    <h3>步骤 1:安装</h3>    <p>为了努力获得使用Docker并将开发人员环境与各种操作系统版本一起集中化的好处,我将让您选择需要下载的Docker版本(Toolbox或原生)和操作系统:</p>    <ul>     <li>Toolbox: <a href="/misc/goto?guid=4959673896733712805" rel="nofollow,noindex">https://www.docker.com/products/docker-toolbox</a> (OS X and Windows)</li>     <li>Docker for Windows: <a href="/misc/goto?guid=4959747139729144240" rel="nofollow,noindex">https://www.docker.com/products/docker#/windows</a></li>     <li>Docker for Mac: <a href="/misc/goto?guid=4959747139809990298" rel="nofollow,noindex">https://www.docker.com/products/docker#/mac</a></li>    </ul>    <p>TOOLBOX用户:Docker Toolbox配有“Docker快速入门终端”,并在运行时与Docker环境相关联。但是,通常在您选择的IDE中运行终端或独立运行终端。为了让Docker与另一个终端/提示进行交互,您需要通过运行“docker-machine env”来初始化Docker env。文本显示的末尾是一个命令,您需要从该同一终端中复制该命令以初始化Docker环境。</p>    <h3>步骤 2:源码/环境</h3>    <p>下一步是获取要容器化的开发应用程序的源代码。</p>    <p>现在,您可以从GitHub抓取这个 项目 ,这个项目保存了本教程的一些步骤。</p>    <h3>步骤 3:创建Docker镜像文件</h3>    <p>请记住,主要目标是在隔离和模块化的环境中运行我们的应用程序。为了让Docker创建这个环境,我们必须告诉它如何创建它。我们使用包含一组指令的 Docker镜像文件 。</p>    <ol>     <li>使用你的IDE,增加一个文件到工程根目录并命名为“Dockerfile”</li>     <li>拷贝下面的文件内容到这个文件</li>    </ol>    <p><strong>重要信息</strong> :该Docker镜像文件将允许我们创建一个镜像,该镜像将代表我们一直在讨论的容器化环境,并最终创建称为容器的该镜像的运行实例。但是,我们向前跳了一步,这些内容应该在第5步中。</p>    <p>Dockerfile</p>    <pre>  FROM mhart/alpine-node:6.9.2    WORKDIR /var/app    COPY . /var/app    RUN npm install --production    EXPOSE 3000    ENV NODE_ENV=production    CMD ["node", "bin/www”]  </pre>    <p>Dockerfile是Docker在镜像文件中寻找的默认名称,但它可以是任何名称,我们将在稍后的步骤中看到。这些指令告诉Docker我们要创建一个镜像:</p>    <ul>     <li>从一个基础的 <a href="/misc/goto?guid=4959747139889558673" rel="nofollow,noindex">mhart/alpine-node</a> 镜像的6.9.2标签开始创建</li>     <li>设置应用程序初始运行的工作目录WORKDIR</li>     <li>拷贝当前本地目录“."中的内容到工作目录</li>     <li>然后从工作目录运行RUN命令npm install —production”</li>     <li>我们将对外暴露 EXPOSE3000端口从容器中,当它从镜像中被创建的时候</li>     <li>另外我们想要对容器设置本地环境变量ENV NODE_ENV的值为“production”</li>     <li>最后,我们启动express.js服务,它驻留在bin目录中的“www”文件中,执行命令“node bin/www”</li>    </ul>    <p><strong>提示</strong> :Docker总是需要一个基本的镜像,镜像保持尽可能小空间占用是关键。alpine-node镜像是非常小的(49.65mb),包括Node.js和NPM。</p>    <h3>步骤 4:构建一个镜像</h3>    <p>现在我们有一个Docker镜像文件,它指定了有关如何创建镜像的详细信息,让我们停下来谈谈镜像是什么:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/859565dee70ce458cd89de57aeb48882.png"></p>    <p>镜像是只读分层文件系统。它构成了我们不断追求的环境的基本文件系统。我们在上一步中创建的镜像文件将指示Docker如何去创建镜像的每层。</p>    <p>但如果它是一个只读的文件系统,我们如何去写它,(例如,执行代码的变化对我们的应用程序的代码在开发过程中)?好问题,我会在即将到来的步骤中回答。</p>    <p>步骤 4a:构建生产镜像</p>    <ol>     <li>从一个终端中导航到项目根目录</li>     <li>运行命令(包含动图最后的命令)</li>    </ol>    <pre>  docker build -t express-prod-i .  </pre>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/0b576e77d8f70faea005895b8ab5bcf7.gif"></p>    <p><strong>提示</strong> :对于Docker Toolbox,从Docker Quickstart Terminal开始运行Docker命令会导致错误(根据操作系统而异)。为了从任何随机终端/提示符运行命令,您需要将终端/提示链接到docker环境。运行docker-machine env,并运行它提示的命令,例如“@FOR / f”tokens = *“%i IN('docker-machine env')DO @%i”</p>    <p>我们做了什么?</p>    <ol>     <li>我们运行build命令从我们在步骤3中创建的Docker镜像文件创建一个文件</li>     <li>使用标签开关-t,我们给镜像一个名称,我们可以使用它来引用它,而不必引用Docker生成的ID(例如50e8dde7e180)</li>     <li>我们指定了使用“.”指定的当前本地目录的镜像路径</li>    </ol>    <p>步骤 4b:验证镜像</p>    <p>如果所有都按计划进行,我们应该看到(如.gif中的)一个“successful build ...”消息。我们现在可以运行一个命令来记住你的镜像:</p>    <pre>  docker images  </pre>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/9829fe9226f264547ea8ac722e5831f3.png"></p>    <p>在这种情况下,我们的组合镜像大小为61.26 MB,这是基本的alpine-node镜像和我们的express应用层的组合。</p>    <p>步骤 4c:观察镜像文件层</p>    <p>如果我们想要看到为我们的镜像创建的文件层,我们可以运行命令:</p>    <pre>  docker history express-prod-i  </pre>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/6f55c702f578414544a5d78dfcbb59fe.png"></p>    <h3>步骤 5:运行镜像实例</h3>    <p>既然我们已经创建了一个镜像,我们已经准备好创建一个隔离的,模块化的应用环境。正如我已经提到了很多次那样,那个环境是一个Docker容器。Docker容器实际上是镜像的运行实例。</p>    <p>在概念上,一个镜像和容器非常类似于我们如何想到一个JavaScript Prototype或一个OOP类。所以,我们来运行一个我们创建的镜像的实例。</p>    <p>步骤 5a:生成并运行一个镜像实例</p>    <ol>     <li>运行命令:</li>    </ol>    <pre>  docker run -d --name express-prod-app -p 7000:3000 express-prod-i  </pre>    <ol>     <li> <p>登录浏览器访问 <a href="/misc/goto?guid=4959747139972910977" rel="nofollow,noindex">http://localhost:7000</a> 。</p> <p><img src="https://simg.open-open.com/show/bd2d557ae5d924e30a2ca3bc494d9395.png"></p> </li>    </ol>    <p>我们做了什么?</p>    <p>我们创建并运行了一个express-prod-i镜像的实例而作为一个容器:</p>    <ul>     <li>使用RUN命令</li>     <li>指定分离模式-d标志(所以我们不绑定当前终端/提示符)</li>     <li>并使用-name标志给我们的容器设置名字“express-prod-app”</li>     <li>将主机7000上的本地端口映射到使用-p标志显示的3000的内部容器端口</li>     <li>最后,指定我们要生成并运行我们的实例的“express-prod-i”镜像</li>    </ul>    <p>注意:我使用不同的本地端口7000来向您展示您可以将主机上的任何未使用的端口映射到容器中暴露的内部端口。</p>    <p>提示:没有-d,我们将以贴合模式启动运行容器。这意味着它将从容器中直接输出到我们运行命令的终端/提示符。通过在分离模式下启动容器,我们可以继续从终端/提示符运行容器工作。</p>    <p>顿悟#1</p>    <p>你明白了吗?我们不必在本地安装Node.js或NPM。我们没有必要运行npm安装本地使用的应用程序,我们没有从主机运行应用程序。所有这些都在主机内部发生,这是一个简单的express.js演示网站。</p>    <p>步骤 5b:验证运行容器</p>    <ol>     <li>我们可以使用这个命令观察运行时容器</li>    </ol>    <pre>  docker ps  </pre>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/b5e81ed4c1b37494e3c2b3fe82efb457.png"></p>    <ol>     <li>这将显示我们运行容器。我们还可以指定-a标志来查看所有容器。当事情出错时,可能会有用,我们想看看一个号称已经运行容器是否意外退出:</li>    </ol>    <pre>  docker ps -a  </pre>    <h3>步骤 6:停止并启动容器</h3>    <p>停止一个运行时容器是非常简单的...或许你已经猜到了:</p>    <pre>  docker stop express-prod-app  </pre>    <p>然后启动它</p>    <pre>  docker start express-prod-app  </pre>    <h3>步骤 7:删除镜像和容器</h3>    <p>当我们浏览这些教程时,可以删除镜像或容器并重新开始。以下是步骤。另外,在“奖励”部分,还有一些有用的捷径:</p>    <p>步骤 7a:删除容器</p>    <p>删除一个容器,我们可以使用“rm”命令:</p>    <pre>  docker rm express-prod-app  </pre>    <p>让容器恢复回来,我们可以使用先前使用过的命令:</p>    <pre>  docker run -d --name express-prod-app -p 7000:3000 express-prod-i  </pre>    <p>步骤 7b:删除镜像</p>    <p>删除一个镜像,我们可以使用“rmi”命令:</p>    <pre>  docker rmi express-prod-i  </pre>    <p>让镜像恢复回来,我们可以使用先前使用过的命令:</p>    <pre>  docker build -t express-prod-i .  </pre>    <p><strong>提示</strong> :删除镜像和容器命令可以使用镜像或容器ID,甚至可以使用ID的前几个字符。所以你可以任意选择。 (您还会看到我们在奖励部分中这样做。)</p>    <h3>奖励</h3>    <p>补充一些删除镜像和容器的其他手段,你可以使用一些我已经在用的有效的快捷方法。</p>    <p>删除所有容器</p>    <p>要删除所有容器,运行下面的“rm”命令,这条命令内部会返回所有的容器:</p>    <pre>  docker rm $(docker ps -a -q)  </pre>    <p>Windows用户:你需要是一个bash环境去运行这些命令。主要是因为只有GNU才能使用$()变量引用。你可以安装诸如“Bash on Ubuntu on Windows”甚至于 GIT bash。只要记得你需要运行docker-machine env来链接你的终端,并且运行显示在最后的eval命令。</p>    <p>我们做了什么?</p>    <ol>     <li>我们想container提交了删除命令“rm”</li>     <li>但是我们提交的是一个容器ID的列表,而不是提供容器标签或是container ID</li>     <li>提供所有(-a)参数并组合静默(-q)将会返回数字类型的容器ID</li>    </ol>    <p>删除所有镜像</p>    <p>删除所有的镜像我们可以类似于上面的做法,使用删除镜像命令“rmi”</p>    <pre>  docker rmi $(docker images -q)  </pre>    <p>我们做了什么?</p>    <ol>     <li>这里我们使用了删除镜像命令但是入参是一个docker镜像ID列表……</li>     <li>……使用列出docker镜像的命令</li>     <li>并且设置静默(-q)参数,这样返回了数字型的Docker ID</li>    </ol>    <p>这可以应用到生产吗?还是只是应用开发环境?</p>    <p>很明显能主要到我们刚刚创建的镜像通知Express.js和Node.js在生产环境中运行。那这是什么鬼?</p>    <p>记住我们是要为任何镜像制作基本镜像,并且谁会在开发环境中为每次修改而“重建”镜像呢?所以我们需要使用生产镜像来作为基本镜像构建我们的开发环境并且隔离开发修改到分层镜像中的开发层中。</p>    <h3>总结</h3>    <p>所以,我已经屏蔽了一大堆关于Docker的知识。创建镜像文件,构建镜像并生成该镜像的正在运行的容器。但是我们还有很多事情要去做。</p>    <p>因此,在本系列的下一篇文章中,我将针对我们的开发版本或应用程序创建一个镜像,但是基于我们的生产镜像。我们还将介绍如何利用容器中的脚本来帮助设置本地环境来更改源代码。</p>    <p> </p>    <p> </p>    <p>来自:http://dockone.io/article/2239</p>    <p> </p>