Kubernetes从容器化开始,容器需要从Dockerfile开始。本文将介绍如何编写优雅的Dockerfile。文章主要内容包括:Docker容器Dockerfile使用多阶段构建感谢公司提供大量的机器资源和时间让我们实践,感谢部分项目和一直在这方面实践的人员的支持话题。一、Docker容器1、容器的特性我们都知道容器是一个标准的软件单元,它具有以下特性:Runanywhere:容器可以将代码、配置文件和相关依赖打包在一起,保证在任何环境下运行都保持一致.资源利用率高:容器提供进程级隔离,可以更精细地设置CPU和内存使用率,从而更好地利用服务器的计算资源。快速伸缩:每个容器都可以作为一个单独的进程运行,可以共享底层操作系统的系统资源,可以加快容器的启停效率。2.Docker容器目前,市场上主流的容器引擎有Docker、Rocket/rkt、OpenVZ/Odin等,其中占据主导地位的容器引擎是使用最多的Docker容器引擎。Docker容器是一系列与系统其余部分隔离的进程。运行这些进程所需的所有文件都由另一个映像提供。从开发到测试再到生产,Linux容器都是可移植且一致的。性别。容器运行速度比依赖复制传统测试环境的开发管道快得多,并且支持部署在各种主要的云平台(PaaS)和本地系统上。Docker容器解决了“开发环境能正常运行,上线却各种崩溃”的尴尬。Docker容器的特点:轻量级:容器是进程级的资源隔离,而虚拟机是操作系统级的资源隔离,所以Docker容器可以比虚拟机节省更多的资源开销,因为Docker容器不再需要GuestOS层作为运行系统。快速:容器的启动和创建无需启动GuestOS,秒级甚至毫秒级即可启动。可移植性:Docker容器技术将应用程序和依赖库、运行时环境技术转化为容器镜像,可以运行在不同的平台上。自动化:容器生态系统(如Kubernetes)中的容器编排可以帮助我们实现容器管理的自动化。2.DockerfileDockerfile是一个用来描述文件组成的文本文件,里面包含了用户在使用行中可以调用的所有组合Image的命令,用户也可以使用Dockerbuild来实现自动构建连续执行多个命令的行。通过编写Dockerfile原始磁力镜像,我们可以为开发和测试团队提供一个基本一致的环境,从而提高开发和测试团队的效率,不再需要为环境不一致而烦恼,同时,运维可以更方便的管理我们的镜像。Dockerfile的语法很简单,常用的只有11条:1.写一个优雅的Dockerfile。要写出优雅的Dockerfile,需要注意以下几点:Dockerfile不能太长。层数越多,最终图像就越大。构建的镜像不应包含不必要的内容,如日志、临时安装文件等。尽量使用运行时的基础镜像,运行时不需要将构建时的过程放入Dockerfile中。只要记住以上三点,就可以写出一个好的Dockerfile。为了您的方便,我们使用两个Dockerfile实例进行简单比较:FROMubuntu:16.04RUNapt-getupdateRUNapt-getinstall-yapt-utilslibjpeg-dev\python-pipRUNpipinstall--upgradepipRUNeasy_install-UsetuptoolsRUNapt-getcleanFROMubuntu:16.04RUNapt-getupdateinst&&alpt-utils\libjpeg-dev-pip\&&pipinstall--upgradepip\&&easy_install-Usetuptools\&&apt-getclean我们来看第一个Dockerfile。乍一看,组织清晰,结构合理,似乎不错。再看第二个Dockerfile。它紧凑且难以阅读。你为什么这样写?第一个Dockerfile的好处是:当正在执行的流程的某一层出现错误时,修正错误并重新构建。之前执行过的层不会再执行。这样可以大大减少下一次Build的时间,但是它的问题是镜像占用的空间也会因为关卡的增加而增加。第二个Dockerfile把所有的组件都解决在一个层次上,可以一定程度上减少镜像占用的空间,但是如果在制作基础镜像的时候其中一组编译失败,修正后重新构建就相当于从头开始scratch是的,之前编译的组件都在一层,都得重新编译,很费时间。从下表我们可以看到这两个Dockerfile编译出来的镜像大小:$dockerimages|grepubuntuREPOSITORYTAGIMAGEIDCREATEDSIZEubuntu16.049361ce633ff11daysago422MBubuntu16.04-13f5b979df1a91daysago412MB呃....貌似没什么特别的效果,不过如果Dockerfile很长,可以考虑降低*file*的层级**只能有127层。3、使用多阶段构建升级到Docker17.05后,Docker可以支持多阶段构建。为了使镜像更小,我们使用多阶段构建来打包镜像。在多阶段构建出现之前,我们通常使用一个Dockerfile或者多个Dockerfile来构建镜像。1.单文件构建在多阶段构建之前使用单个文件构建。一个文件就是把所有的构建过程(包括项目依赖、编译、测试、打开过程)都包含在一个Dockerfile下:FROMgolang:1.11.4-alpine3.8ASbuild-envENVGO111MODULE=offENVGO15VENDOREXPERIMENT=1ENVBUILDPATH=github.com/lattecake/helloRUNmkdir-p/go/src/${BUILDPATH}COPY.//go/src/${BUILDPATH}RUNcd/go/src/${BUILDPATH}&&CGO_ENABLED=0GOOS=linuxGOARCH=amd64goinstall–vCMD[/go/bin/hello]会带来一些问题:Dockerfile会很长,当需要的东西越来越多的时候,可维护性会成倍下降;镜像层数太多,镜像的大小会逐渐变大,部署会越来越慢;存在代码泄露的风险。以Golang为例。它运行时不依赖于任何环境。它只需要有一个编译环境。那么这个编译环境在实际运行时是没有任务功能的。编译完成后,那些源代码和编译器就不再对任务有用了。没有必要留在镜子里。从上表可以看出,单文件构建最终占用了312MB的空间。2.multi-stagebuild之前的multi-filebuild有什么好的解决方案吗?是的,比如使用多文件构建或者在构建服务器上安装编译器,但是我们不建议在构建服务器上安装编译器是的,因为在构建服务器上安装编译器会导致构建服务器变得非常臃肿。需要适配各个语言的多个版本和依赖,容易出错,维护成本高。所以我们只介绍多文件构建的方式。多文件构建其实就是使用多个Dockerfile,然后通过脚本进行组合。假设有三个文件:Dockerfile.run、Dockerfile.build、build.sh。Dockerfile.run是运行时程序必须需要的一些组件的Dockerfile,包含了最精简的库;Dockerfile.build只用于构建,构建完就没用了;build.sh的作用是结合Dockerfile.run和Dockerfile.build组成,将Dockerfile.build构建的东西取出来,然后执行Dockerfile.run,算是一个调度的作用。Dockerfile.buildFROMgolang:1.11.4-alpine3.8ASbuild-envENVGO111MODULE=offENVGO15VENDOREXPERIMENT=1ENVBUILDPATH=github.com/lattecake/helloRUNmkdir-p/go/src/${BUILDPATH}COPY.//go/src/${BUILDPATH}RUNcdgo/src/${BUILDPATH}&&CGO_ENABLED=0GOOS=linuxGOARCH=amd64goinstall–vDockerfile.runFROMalpine:latestRUNapk–no-cacheaddca-certificatesWORKDIR/rootADDhello.CMD["./hello"]Build.sh#!/bin/shdockerbuild-t–rmhello:build.-fDockerfile.builddockercreate–nameextracthello:builddockercpextract:/go/bin/hello./hellodockerrm-fextractdockerbuild–no-cache-t–rmhello:run.-fDockerfile.runrm-rf./hello执行build.sh完成项目建设。从上表可以看出,多文件构建大大减少了镜像占用的空间,但是它要管理三个文件,维护成本也较高。3.多阶段构建***让我们来看看万众期待的多阶段构建。要完成多阶段构建,我们只需要在Dockerfile中多次使用FORM语句即可。每条FROM指令都可以使用不同的基础镜像,每条FROM指令都会开始一个新的构建。我们可以选择将一个阶段的构建结果复制到另一个阶段,在一个阶段中,最终镜像中只会留下最后一次构建的结果,这样就可以轻松解决上述问题,只需要编写一个Dockerfile.这里值得注意的是:需要保证Docker的版本为17.05及以上。下面说说具体的操作。在Dockerfile中,可以使用as为某个阶段取一个别名“build-env”:FROMgolang:1.11.2-alpine3.8ASbuild-envCOPY–from=build-env/go/bin/hello/usr/bin/hello一个简单的例子:FROMgolang:1.11.4-alpine3.8ASbuild-envENVGO111MODULE=offENVGO15VENDOREXPERIMENT=1ENVGITPATH=github.com/lattecake/helloRUNmkdir-p/go/src/${GITPATH}COPY.//go/src/${GITPATH}RUNcd/go/src/${GITPATH}&&CGO_ENABLED=0GOOS=linuxGOARCH=amd64goinstall-vFROMalpine:latestENVapk–no-cacheaddca-certificatesCOPY--from=build-env/go/bin/hello/root/helloWORKDIR/rootCMD["/root/hello"]执行dockerbuild-t–rmhello3。然后执行dockerimages,然后我们看一下镜像的大小:多阶段构建给我们带来了很多方便,最大的好处是保证了运行的镜像足够小,同时也减轻了Dockerfile的维护负担,所以我们强烈建议使用多阶段构建将您的代码打包到Docker镜像中。【本文为栏目机构易新科技原创文章,微信公众号“易新科技(id:CE_TECH)”点击此处查看作者更多好文
