背景计划逐步将手头的项目转为基于Docker容器的发布。同时,该项目采用了云厂商提供的CI/CD自动化你的发布流水线。因此,为了配合CI/CD运行,需要先为项目构建一些发布的脚本,使用脚本自动操作Docker镜像定制,Docker启停。在阅读和实践本文之前,如果你还没有搭建过Docker环境,可以先参考上篇文章《Linux安装Docker完整教程》搭建好整个环境,熟悉Docker的基本操作命令。本文通过具体的实战案例来讲解如何自定义Docker镜像,并使用脚本来执行镜像构建、项目发布、容器启停等操作。什么是Dockerfile?Dockerfile是一个用于构建镜像的文本文件,文本内容包含了构建镜像的说明和说明。常见的指令有:FROM、RUN、ADD、COPY、CMD、ENV等。在构建镜像时,需要注意的是镜像是一层一层构建的,上一层是下一层的基础.每一层构建完成后,不会再发生变化,下一层的任何变化只发生在本层。与上面提到的命令一样,每个操作都会构建一个层。比如你删除了上一层的文件,虽然最后容器运行的时候你看不到这个文件,但是这个文件其实会一直跟在镜像后面。因此,在构建图像时需要格外小心。每层应该只包含需要添加到该层的内容,任何多余的东西都应该在该层构建结束之前清理干净。另外,为了减少构建层数,在编写Dockerfile时,尽量将多层指令合并到一层执行。例如,两个RUN命令可以通过&&合并为一个。不推荐的镜像创建方式通常有两种方式创建Docker镜像:基于dockercommit和基于Dockerfile。Docker提供了dockercommit命令,可以将容器的存储层保存为镜像。也就是说,在原有镜像的基础上,增加容器的存储层,形成新的镜像。当新镜像随后运行时,它将具有原始容器的最后文件更改。dockercommit方法除了学习之外,还可以用在一些特殊场景下,比如网站被黑后的保存等。但是不要使用dockercommit自定义镜像,自定义镜像应该用Dockerfile来完成。这是因为在使用dockercommit制作镜像的时候,除了我们要修改的内容(文件)之外,这个命令还会修改一些其他的文件,而且对镜像的所有操作都是黑盒操作,生成的镜像也被封锁了。它被称为黑盒镜像。除了制作图像的人知道执行了什么命令以及图像是如何生成的,其他人没有任何办法知道。即便是制作镜像的人,过了一段时间也不一定能记住具体的操作。这个黑盒镜像的维护是非常痛苦的。另外,如果使用dockercommit创建镜像,以后再修改,每次修改都会让镜像更加臃肿,被删除的上层不会丢失,会一直跟在镜像后面,即使此时不可访问全部。这会使图像更加臃肿。所以这里我们不使用dockercommit方式制作镜像。有兴趣的可以上网查一下这种方法的制作过程。本文重点介绍基于Dockerfile制作镜像。以下示例显示了如何构建Docker映像。编写Dockerfile命令,在/opt目录下创建一个业务目录/opt/channel/docker(这里部署的项目是一个名为channel的通道项目),Dockerfile和待发布的jar包等资源文件存放在这个目录下目录。$cd/opt/channel/docker$touchDockerfile上面的命令首先进入/opt/channel/docker目录,创建一个空的Dockerfile(文本)文件。编辑Dockerfile的内容如下:FROMjava:8COPY./hqy-service-channel.jar./app.jarENVspring.profiles.activeprodEXPOSE8190ENTRYPOINT["java","-jar","-Duser.timezone=GMT+08","./app.jar"]Dockerfile涉及FROM、COPY、ENV、EXPOSE、ENTRYPOINT5条指令,下面一一说明。FROM命令所谓的镜像创建,就是在已有镜像的基础上进行定制。必须指定基础镜像,FROM是指定基础镜像,所以FROM是Dockerfile中的必要指令,必须是第一条指令。这里的FROMjava:8是Docker镜像源中openjdk的镜像,版本是8。可以通过搜索命令查看这个镜像:[docker]#dockersearchjavaNAMEDESCRIPTIONSTARSOFFICIALAUTOMATEDnodeNode.js是一个JavaScript基于平台的s...11734[OK]tomcatApacheTomcat是一个开源实现...3368[OK]openjdkOpenJDK的“Vanilla”构建(一个开源...3362[OK]java已弃用;使用“openjdk"(orotherJDKimpl...1976[OK]第四个名字是java,为了后续操作方便,这里直接拉取镜像到本地。dockerpulljava:8查看本地拉取后,本地镜像列表:[root@iZ2zehx0enix3i0aiea7p0Zdocker]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEjava8d23bdf5b1b1b5yearsago643MB后续镜像创建将基于此镜像。COPY命令COPY,复制命令,将文件或目录从上下文目录复制到容器中的指定路径。COPY./hqy-service-channel.jar./app.jar第一个参数是源文件路径,第二个参数是容器中的目标文件路径。这里是将当前目录下的SpringBoot项目jar包hqy-service-channel.jar复制到容器中,并命名为app.jar。在执行创建镜像命令前,需要将项目jar包与Dockerfile放在同一目录下。ENV指令ENV指令用于设置环境变量,定义环境变量,那么在后续的指令中就可以使用这个环境变量。基本格式为:ENVENV==...第一个参数为变量key,第二个参数为变量value,用于设置SpringBoot项目的配置文件的profile为prod(productionprofile)。EXPOSE命令EXPOSE命令,声明端口即可。作用是帮助镜像用户了解镜像服务的保护端口,方便配置映射。另外,在运行时使用随机端口映射时,即dockerrun-P,会自动随机映射EXPOSE的端口。基本格式:EXPOSE[...]这里使用8190端口。ENTRYPOINT指令ENTRYPOINT指令类似于CMD指令,但不会被dockerrun的命令行参数指定的指令覆盖,这些命令行参数会作为参数发送给ENTRYPOINT指令指定的程序。在执行dockerrun时,可以指定ENTRYPOINT运行所需的参数。ENTRYPOINT["","","",...]这里的ENTRYPOINT命令用于执行jar-jar启动SpringBoot工程。RUN命令虽然示例中没有使用RUN命令,但它也是一个很常用的命令。用于执行后面的命令行命令,有以下两种格式。Shell格式:RUN<命令行命令>#<命令行命令>相当于在终端上操作的shell命令。exec格式:RUN["可执行文件","参数1","参数2"]#例如:#RUN["./test.php","dev","offline"]等同于RUN./test.phpdevoffline经过以上一系列的操作,Dockerfile就写好了。构建命令时需要注意的是:根据Docker最佳实践,容器不应该向其存储层写入任何数据,容器存储层应该保持无状态。所有的文件写入操作都应该使用数据卷(Volume)或者绑定主机目录。在这些位置读写会跳过容器存储层,直接读写宿主机(或网络存储)。稳定性更高。在构建镜像上准备好Dockerfile文件,然后将相应的jar包放在指定位置,在Dockerfile文件的目录下执行构建命令即可,如:dockerbuild-tchannel。-tchannel指定构建镜像的名称,当然你也可以同时指定版本号-tchannel:v1。下列”。”指的是当前目录。执行结果如下:[docker]#dockerbuild-tchannel.SendingbuildcontexttoDockerdaemon82.31MBStep1/5:FROMjava:8--->d23bdf5b1b1bStep2/5:COPY./hqy-service-channel.jar./app.jar--->10cb376c7572Step3/5:ENVspring.profiles.activetest--->Runninginca70651b21b6Removingintermediatecontainerca70651b21b6--->ec420f94df51Step4/5:EXPOSE8190--->Runningin318e718d552aRemovingintermediatecontainer318e718d552a--->6746bad4a990Step5/5:ENTRYPOINT["java","-jar","-Duser.timezone=GMT+08","./app.jar"]--->Runningin135de4d42ec8Removingintermediatecontainer135de4d42ec8--->1720afb4fec7Successfullybuilt1720afb4fec7Successfullytaggedchannel:latest执行dockerimages可查看到镜像构建完成:[docker]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEchannellatest1720afb4fec751秒前725MB5b1java8643MB之后,可以使用dockerrun命令启动容器。这里,为了方便CI/CD操作,我们可以使用脚本来停止整个容器,移除容器,移除镜像,重新制作镜像,重启容器。CI/CD系统只需要调用相应的脚本即可。示例脚本start.sh如下:#!/bin/bash#停止容器dockerstopchannelecho"停止容器成功!"#移除容器dockerrmchannelecho"移除容器成功!"#移除镜像dockerrmichannelecho"Removethemirrorsuccess!"#Makeamirrordockerbuild-tchannel/opt/channel/docker/echo"Makeamirrorsuccess!"#启动容器dockerrun-d--namechannel-p8190:8190-v/opt/channel/logs/:/opt/channel/logs/channelchannel:latestecho"启动成功!"执行以上脚本后,查看容器执行结果:[bin]#dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMESe9eff75cdb6fchannel"java-jar-Duser.ti..."30secondsagoUp28seconds0.0.0.0:8190->8190/tcpchannel可以看到容器已经成功启动。重建新的jar包时,只需替换目录下的jar包,然后再次执行start.sh命令即可。小结本文通过具体实例演示了如何制作Docker镜像,制作Docker镜像过程中需要注意的事项,以及制作完成后CI/CD的脚本编写。大家可以参考上面的例子,根据自己的业务场景进行相应的改造。博主简介:《SpringBoot技术内幕》技术书籍作者,热爱研究技术,撰写技术文章。公众号:《程序新视界》,博主的公众号,欢迎关注~技术交流:请联系博主微信号:zhuan2quan