念封魂 發表於 2018-12-3 09:58:00

Docker最全教程——从理论到实战(三)

<h1>往期链接:</h1>
<p><span>https://www.cnblogs.com/codelove/p/10030439.html</span></p>
<p>https://www.cnblogs.com/codelove/p/10036608.html</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1><strong>写在前面</strong></h1>
<p><strong>容器是应用走向云端之后必然的发展趋势,因此笔者非常乐于和大家分享我们这段时间对容器的理解、心得和实践。</strong></p>
<p><strong>本教程持续编写了2个星期左右并且一直在完善、补充具体的细节和实践,</strong><strong>预计全部完成需要1到2个月的时间</strong><strong>。由于编写的过程中极其费时,并且还需要配合做一些实践(有些实践存在一些坑,而且极其费时费事)。因此目前产出的速度已经跟不上发布的速度了,后续的发布节奏会放慢,请大家多多理解和多多包含。</strong></p>
<p><strong>根据目前和大家的交流,笔者针对大家的情况进行了一些修改和补充,希望对大家有所帮助。</strong><strong>另外,对于熟悉容器服务的你,也可以参与进来,让我们一起打造这个系列教程,<strong>我们希望能够多多交流,多多分享,</strong>以帮助更多的人。</strong><strong>同时,我们也希望得到大家的支持。</strong></p>
<h1><strong>前言</strong></h1>
<p><strong>内容发出来之后,有部分小伙伴有些疑惑。这里我特别说明下,Docker for windows 指的是docker官方提供的windows的安装包,并不是指的基于windows镜像开发。笔者推荐的方式是——在windows上开发和调测,托管到Linux。</strong></p>
<h1><strong><strong>Docker持续开发工作流</strong></strong></h1>
<p>Docker改变了开发以及产品交付流程,以下是一般情况下的Docker应用程序的内部循环的持续开发工作流,本工作流只关注在开发人员的计算机上进行的开发工作,不包括设置环境等初始步骤,因为这些步骤只需进行一次。</p>
<p>应用程序一般由开发人员自己的服务代码和附加库(依赖项)组成,以下是生成 Docker 应用程序时常用的基本步骤,具体如下图所示:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203093658359-1691324108.png" alt=""></p>
<p>在本篇教程中,我们以开源框架Magicodes.Admin为例进行讲解。</p>
<p><span style="color: rgba(51, 51, 153, 1)">Magicodes.Admin,是心莱科技团队打造的一套高效率、易扩展、基础设施强大、代码生成完备、理念和技术先进的敏捷开发框架,同时也是一套分布式(即将提供微服务架构参考)、跨平台(linux、Docker容器支持)、多终端(包括Android、IOS、H5、小程序、微信公众号)支持的统一开发框架和解决方案。框架基于.NET Core 2.1、Angular、Ionic、EF Core、ABP和ASP.NET Zero,并在其基础上进行了封装和完善,并且编写了相关的工具(代码生成)、组件(云存储、支付、微信等等)、生成服务。</span></p>
<p>代码地址:<span style="text-decoration: underline"><span style="color: rgba(51, 102, 255, 1); text-decoration: underline">https://gitee.com/xl_wenqiang/Magicodes.Admin.Core</span></span></p>
<p>&nbsp;</p>
<p>在开始之前,我们先需要准备好相关环境和代码。比如:</p>
<p>Git clone&nbsp;<span style="text-decoration: underline"><span style="color: rgba(51, 102, 255, 1); text-decoration: underline">git@gitee.com:xl_wenqiang/Magicodes.Admin.Core.git</span></span></p>
<p>相关环境以及前期准备大家可以参阅公众号”magiccodes“中的教程,这里就不多赘述了。</p>
<p>&nbsp;</p>
<h2>开发</h2>
<p>开发过程其实和传统开发一样,也就是说,开发Docker 应用的方式与开发非Docker应用的方式类似。二者的主要区别在于,开发 Docker 应用程序时,是在本地环境中的Docker容器中部署和测试,该容器可以是Linux容器或Windows 容器。</p>
<p>&nbsp;</p>
<p>一般情况下,我们搭建好框架代码之后,就需要针对需求进行开发,以满足业务为目的,也就是这个开发过程并没有什么改变,这里我们假设所有代码均已就绪,开始下一步。</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203093956755-739308267.png" alt=""></p>
<p>&nbsp;</p>
<h2>创建Dockerfile</h2>
<p>本节内容很多,我们希望大家能够了解和使用好Dockerfile。</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094045768-1316009773.png" alt=""></p>
<h3><strong>关于dockerfile</strong></h3>
<p>虽然我们可以通过docker commit命令来手动创建镜像,但是通过Dockerfile文件,可以帮助我们自动创建镜像,并且能够自定义创建过程。本质上,Dockerfile就是由一系列命令和参数构成的脚本,这些命令应用于基础镜像并最终创建一个新的镜像。它简化了从头到尾的构建流程并极大的简化了部署工作。使用dockerfile构建镜像有以下好处:</p>
<ul class="list-paddingleft-2">
<li>
<p>像编程一样构建镜像,支持分层构建以及缓存;</p>
</li>
<li>
<p>可以快速而精确地重新创建镜像以便于维护和升级;</p>
</li>
<li>
<p>便于持续集成;</p>
</li>
<li>
<p>可以在任何地方快速构建镜像</p>
</li>
</ul>
<p>&nbsp;</p>
<h3><strong>Dockerfile指令</strong></h3>
<p>我们需要了解一些基本的Dockerfile 指令,Dockerfile 指令为 Docker 引擎提供了创建容器映像所需的步骤。这些指令按顺序逐一执行。以下是有关一些基本 Dockerfile 指令的详细信息。</p>
<h4><strong>1.FROM</strong></h4>
<p>FROM 指令用于设置在新映像创建过程期间将使用的容器映像。</p>
<p>格式:FROM&nbsp;</p>
<p>示例:</p>
<p>FROM nginx</p>
<p>FROM microsoft/dotnet:2.1-aspnetcore-runtime</p>
<p>&nbsp;&nbsp;</p>
<h4><strong>2.RUN</strong></h4>
<p>RUN&nbsp;指令指定将要运行并捕获到新容器映像中的命令。 这些命令包括安装软件、创建文件和目录,以及创建环境配置等。</p>
<p>格式:</p>
<p>RUN ["", "", ""]</p>
<p>RUN</p>
<p>示例:</p>
<p>RUN apt-get update</p>
<p>RUN mkdir -p /usr/src/redis</p>
<p>RUN apt-get update &amp;&amp; apt-get install -y libgdiplus</p>
<p>RUN ["apt-get","install","-y","nginx"]</p>
<p><span style="color: rgba(51, 51, 153, 1)">注意:每一个指令都会创建一层,并构成新的镜像。当运行多个指令时,会产生一些非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。因此,在很多情况下,我们可以合并指令并运行,例如:RUN apt-get update &amp;&amp; apt-get install -y libgdiplus。在命令过多时,一定要注意格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。使用换行符时,可能会遇到一些问题,具体可以参阅下节的转义字符。</span></p>
<p>&nbsp;</p>
<h4><strong>3.COPY</strong></h4>
<p>COPY&nbsp;指令将文件和目录复制到容器的文件系统。文件和目录需位于相对于&nbsp;Dockerfile&nbsp;的路径中。</p>
<p>格式:</p>
<p>COPY</p>
<p>如果源或目标包含空格,请将路径括在方括号和双引号中。</p>
<p>&nbsp;</p>
<p>COPY ["", ""]</p>
<p>示例:</p>
<p>COPY . .</p>
<p>COPY nginx.conf /etc/nginx/nginx.conf</p>
<p>COPY . /usr/share/nginx/html</p>
<p>COPY hom* /mydir/</p>
<p>&nbsp;</p>
<h4><strong>4.ADD</strong></h4>
<p>ADD 指令与 COPY 指令非常类似,但它包含更多功能。除了将文件从主机复制到容器映像,ADD 指令还可以使用 URL 规范从远程位置复制文件。</p>
<p>格式:</p>
<p>ADD&lt;source&gt; &lt;destination&gt;</p>
<p>示例:</p>
<p>ADD&nbsp;https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe /temp/python-3.5.1.exe</p>
<p>此示例会将 Python for Windows下载到容器映像的 c:\temp 目录。</p>
<p>&nbsp;</p>
<h4><strong>5.WORKDIR</strong></h4>
<p>WORKDIR&nbsp;指令用于为其他&nbsp;Dockerfile&nbsp;指令(如&nbsp;RUN、CMD)设置一个工作目录,并且还设置用于运行容器映像实例的工作目录。</p>
<p>格式:</p>
<p>WORKDIR</p>
<p>示例:</p>
<p>WORKDIR /app</p>
<p>&nbsp;</p>
<h4><strong>6.CMD</strong></h4>
<p>CMD指令用于设置部署容器映像的实例时要运行的默认命令。例如,如果该容器将承载&nbsp;NGINX Web&nbsp;服务器,则&nbsp;CMD&nbsp;可能包括用于启动Web服务器的指令,如&nbsp;nginx.exe。 如果&nbsp;Dockerfile&nbsp;中指定了多个CMD&nbsp;指令,只会计算最后一个指令。</p>
<p>格式:</p>
<p>CMD ["&lt;executable", "</p>
<p>CMD</p>
<p>示例:</p>
<p>CMD ["c:\\Apache24\\bin\\httpd.exe", "-w"]</p>
<p>CMD c:\\Apache24\\bin\\httpd.exe -w</p>
<p>&nbsp;</p>
<h4><strong>7.ENTRYPOINT</strong></h4>
<p>配置容器启动后执行的命令,并且不可被&nbsp;docker run&nbsp;提供的参数覆盖。每个&nbsp;Dockerfile&nbsp;中只能有一个ENTRYPOINT,当指定多个时,只有最后一个起效。</p>
<p>格式:</p>
<p>ENTRYPOINT ["", ""]</p>
<p>示例:</p>
<p>ENTRYPOINT ["dotnet", "Magicodes.Admin.Web.Host.dll"]</p>
<p>&nbsp;</p>
<h4><strong>8.ENV</strong></h4>
<p>ENV命令用于设置环境变量。这些变量以”key=value”的形式存在,并可以在容器内被脚本或者程序调用。这个机制给在容器中运行应用带来了极大的便利。</p>
<p>格式:</p>
<p>ENV==...</p>
<p>示例:</p>
<p>ENV VERSION=1.0 DEBUG=on \</p>
<p>NAME="Magicodes"</p>
<p>&nbsp;</p>
<h4><strong>9.EXPOSE</strong></h4>
<p>EXPOSE用来指定端口,使容器内的应用可以通过端口和外界交互。</p>
<p>格式:</p>
<p>EXPOSE</p>
<p>示例:</p>
<p>EXPOSE 80</p>
<p>&nbsp;</p>
<p>说了这么多,我们可以用下图来一言以蔽之:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094342684-400475059.png" alt=""></p>
<p>&nbsp;</p>
<h3><strong>转义字符</strong></h3>
<p>在许多情况下,Dockerfile 指令需要跨多个行;这可通过转义字符完成。 默认 Dockerfile 转义字符是反斜杠 \。 由于反斜杠在 Windows 中也是一个文件路径分隔符,这可能导致出现问题。</p>
<p>以下示例显示使用默认转义字符跨多个行的单个 RUN 指令。</p>
<p>FROM microsoft/windowsservercore</p>
<p>&nbsp;</p>
<p>RUN powershell.exe -Command \</p>
<p>$ErrorActionPreference = 'Stop'; \</p>
<p>wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\python-3.5.1.exe ; \</p>
<p>Start-Process c:\python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ; \</p>
<p>Remove-Item c:\python-3.5.1.exe -Force</p>
<p>&nbsp;</p>
<p>要修改转义字符,必须在 Dockerfile 最开始的行上放置一个转义分析程序指令。 如以下示例所示:</p>
<p># escape=`</p>
<p>&nbsp;</p>
<p>FROM microsoft/windowsservercore</p>
<p>&nbsp;</p>
<p>RUN powershell.exe -Command `</p>
<p>$ErrorActionPreference = 'Stop'; `</p>
<p>wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\python-3.5.1.exe ; `</p>
<p>Start-Process c:\python-3.5.1.exe -ArgumentList '/quiet InstallAllUsers=1 PrependPath=1' -Wait ; `</p>
<p>Remove-Item c:\python-3.5.1.exe -Force</p>
<p><span style="color: rgba(51, 51, 153, 1)">注意,只有两个值可用作转义字符:\ 和 ` 。</span></p>
<p>&nbsp;</p>
<h3><strong>优化</strong></h3>
<p>篇幅有限,我们这里只进行简单讲解,后续结合实际案例再进行细说。但是有几点值得注意的是:</p>
<ol class="list-paddingleft-2">
<li>
<p>不能忽视dockerfile的优化,通常情况下,我们可以忽略那些细小的优化,但是我们需要知道优化的原理,为什么要优化</p>
</li>
<li>
<p>不能为了优化而优化。镜像的构建过程视业务情况情况不同,指令就有多到少的区别,在很多情况下,我们先要以满足业务目标为准,而不是镜像层数。如果需要减少镜像的层数,我们一定要选择合适的基础镜像,或者创建符合我们需要的基础镜像。</p>
</li>
</ol>
<p>下面是一些优化的准则:</p>
<ul class="list-paddingleft-2">
<li>
<p>选择合适的基础镜像</p>
<p>这点相对最为重要。为什么这么说,我们结合现实社会也可以看到,在大部分情况下,一个人一生的成就更多的是看出身。很多情况下,基因和出身决定了你的高度和终点,这点拿到技术层面来说,也是有很大道理的,因此我们需要选择合适的父母——一个合适的镜像。</p>
<p>一个合适的基础镜像是指能满足运行应用所需要的最小的镜像,理论上是能用小的就不要用大的,能用轻量的就不要用重量级的,能用性能好的就不要用性能差的。这里有时候还需要考虑那些能够减少我们构建层数的基础镜像。</p>
<p>&nbsp;</p>
</li>
<li>
<p>优化指令顺序</p>
<p>Docker会缓存Dockerfile中尚未更改的所有步骤,但是,如果更改任何指令,将重做其后的所有步骤。也就是指令3有变动,那么4、5、6就会重做。因此,我们需要将最不可能产生更改的指令放在前面,按照这个顺序来编写dockerfile指令。这样,在构建过程中,就可以节省很多时间。比如,我们可以把WORKDIR、ENV等命令放前面,COPY、ADD放后面。</p>
<p>&nbsp;</p>
</li>
<li>
<p>合并指令</p>
<p>前面其实我们提到过这点,甚至还特地讲到了转义字符,其实主要是为此服务。前面我们说到了,每一个指令都会创建一层,并构成新的镜像。当运行多个指令时,会产生一些非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。因此,在很多情况下,我们可以合并指令并运行,例如:RUN apt-get update &amp;&amp; apt-get install -y libgdiplus。在命令过多时,一定要注意格式,比如换行、缩进、注释等,会让维护、排障更为容易,这是一个比较好的习惯。</p>
<p>&nbsp;</p>
</li>
<li>
<p>删除多余文件和清理没用的中间结果</p>
<p>这点很易于理解,通常来讲,体积更小,部署更快!因此在构建过程中,我们需要清理那些最终不需要的代码或文件。比如说,临时文件、源代码、缓存等等。</p>
<p>&nbsp;&nbsp;</p>
</li>
</ul>
<ul class="list-paddingleft-2">
<li>
<p>使用 .dockerignore</p>
<p>.dockerignore文件用于忽略那些镜像构建时非必须的文件,这些文件可以是开发文档、日志、其他无用的文件。例如:</p>
</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094654908-278191045.png" alt=""></p>
<p>说了这么多,其实我们更多的还是需要根据命令的实际执行情况来进行调整。</p>
<p>&nbsp;</p>
<h3><strong>Visual studio和dockerfile</strong></h3>
<p>如上所示,要生成自定义镜像,需为每个自定义镜像提供一个 Dockerfile。无论是从Visual Studio 自动部署,还是使用 Docker CLI(docker run 和 docker-compose 命令)手动部署,都需为每个要部署的容器提供一个 Dockerfile。如果应用程序只包含一个自定义服务,则只需要一个 Dockerfile。如果应用程序包含多个服务(如在微服务体系结构中),则每个服务都需要一个 Dockerfile。Dockerfile文件需要放在应用程序或服务的根文件夹中。</p>
<p>但是,对于.NET开发人员来说,利用Visual Studio只需单击几次鼠标即可完成此任务。如下图所示:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094748038-626845560.png" alt=""></p>
<p>还可通过在 Visual Studio 中右键单击项目文件,选择“添加 Docker 项目支持”选项,为新项目或现有项目启用 Docker 支持:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094814182-1237177358.png" alt=""></p>
<p>对项目(如 ASP.NET Web 应用程序或 Web API 服务)应用此操作后,系统会向含有所需配置的项目添加 Dockerfile。</p>
<p>在更多的情况下,笔者建议大家选择下面的菜单——容器业务流程协调程序支持:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094846595-1673565794.png" alt=""></p>
<p>因为会向整个解决方案添加 docker-compose.yml 等文件。整个过程,Visual Studio 代为执行了操作,但是,我们也需要了解 Dockerfile中的内容,否则遇到问题,会抓虾,哦,是抓瞎。</p>
<p>启用了之后,我们就可以看到顶部的菜单栏出现了一些便捷操作:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094908842-862337687.png" alt=""></p>
<p>不仅支持一键启动,还能够调试!!!这对于大部分开发者来说,简直是天籁之音哈!</p>
<p>接下来,我们以Magicodes.Admin为例。在Magicodes.Admin中,存在多个应用,比如后台服务和后台UI,目前框架中已经提供了多个dockerfile的配置,分别在相应的工程目录之中。</p>
<p>&nbsp;</p>
<h3><strong>.net core后台服务的dockerfile</strong></h3>
<p>文件所在目录如下所示:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094941836-782278861.png" alt=""></p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203094955773-1074045897.png" alt=""></p>
<p>相关指令我在注释中进行了一一说明,不过,由于Excel的导出在Linux环境需要libgdiplus库的支持,以设置字体,因此我们需要在dockerfile中配置安装此库。同时,我们还推荐使用以下简化的dockerfile:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203095037748-1848978572.png" alt=""></p>
<p>其中,包还原、编译、单元测试运行以及发布等过程我们通过脚本进行了实现,因此在Dockerfile中,命令比较简单干净,关键是整个过程我们能够在本地进行更多的自定义——比如执行单元测试并再通过之后才进行部署和推送。当然,使用第一个配置能够让我们可以更好地和线上的CI工具配套使用。</p>
<p><span style="color: rgba(51, 51, 153, 1)">注意:这里我们并没有使用其他web服务器,我们直接在代码中使用了Kestrel服务器进行托管。</span></p>
<p><span style="color: rgba(51, 51, 153, 1)"><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203095112042-1062451993.png" alt=""></span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3><strong>后台前端应用的dockerfile</strong></h3>
<p>文件所在目录如下所示:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203095148662-623576045.png" alt=""></p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203095204897-2061386883.png" alt=""></p>
<p>后台前端应用使用nginx web服务器进行托管,同时执行了copy命令复制相关配置、静态文件和ssl证书。其中nginx.conf的配置如下所示:</p>
<p><img src="https://img2018.cnblogs.com/blog/70544/201812/70544-20181203095229676-1411377860.png" alt=""></p>
<p>关于dockerfile的相关内容,我们先讲述到这里,希望大家对此有个全面的了解。如果你有疑问或者建议,欢迎讨论交流。</p>
<h1>往期链接:</h1>
<p><span>https://www.cnblogs.com/codelove/p/10030439.html</span></p>
<p>https://www.cnblogs.com/codelove/p/10036608.html</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    作者:雪雁<br>出处:http://www.cnblogs.com/codelove/
<br>如果喜欢作者的文章,请关注【CodeSpirit-码灵】公众号以便第一时间获得最新内容。本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。<br><b>静听鸟语花香,漫赏云卷云舒。</b>
<br>
<img src="https://images.cnblogs.com/cnblogs_com/codelove/315887/o_251224070213_%E5%85%AC%E4%BC%97%E5%8F%B7.jpg" width="100" height="100"><br><br>
来源:https://www.cnblogs.com/codelove/p/10056866.html
頁: [1]
查看完整版本: Docker最全教程——从理论到实战(三)