每日短讯:为了实现CI/CD,先来定制一个Docker镜像【实战精华篇】
计划把手头的项目逐步改造为基于Docker容器的方式发布,同时,项目中已经采用了云厂商提供的CI/CD自动化发布流水线。因此,为配合CI/CD操作,需要先针对项目构建一些发布的脚本,通过脚本来操作Docker镜像定制、Docker的启动和停止。
在阅读和实践本篇文章之前,如果你还未搭建Docker的环境,可参考上篇文章《Linux安装Docker完整教程》,先把整个环境搭建起来,同时熟悉一下Docker的基本操作命令。
这篇文章就配合具体的实践案例来为大家讲讲如何定制一个Docker镜像,并通过脚本来执行镜像的构建、项目的发布、容器的启动与停止等。
(资料图)
Dockerfile是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。常见的指令比如有:FROM、RUN、ADD、COPY、CMD、ENV等。
在镜像构建时,需要注意的一点是:镜像的构建是一层层构建的,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。
像上面提到的指令,每一次操作都会构建一层。比如删除前一层的文件,在最终容器运行时,虽然看不到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像时,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
另外,为了减少构建层的数量,在编写Dockerfile文件时尽量将多层的指令合并成一层执行,比如两个RUN命令可以通过&&将其合并成一条。
不建议的镜像制作方式制作Docker镜像通常有两种方式:基于docker commit和基于Dockerfile的形式。
Docker提供了一个 docker commit命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。后续运行这个新镜像时,就会拥有原有容器最后的文件变化。
docker commit的方式除了学习之外,还可以用于一些特殊的场景,比如被入侵后保存现场等。但是不要使用 docker commit定制镜像,定制镜像应该使用 Dockerfile来完成。
这是因为在使用docker commit制作镜像时,除了我们想要修改的内容(文件)之外,该命令还会修改一些其他的文件,而且所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像。
除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。即使制作镜像的人,一段时间后可能也无法记清具体的操作。这种黑箱镜像的维护工作是非常痛苦的。
另外,如果使用 docker commit制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。
因此,这里我们不采用 docker commit的方式制作镜像,如果大家感兴趣的话,可以在网络上查询一下该方式的制作流程。本文重点介绍基于 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、ENTRYPOINT五个指令,下面逐一讲解。
FROM指令所谓制作镜像,就是在已经存在的镜像的基础上进行定制。基础镜像是必须指定的,而 FROM就是指定基础镜像,因此一个 Dockerfile中 FROM是必备的指令,并且必须是第一条指令。
这里的FROM java:8,也就是采用openjdk在Docker镜像源中的镜像,版本为8。可以通过search命令查看一下这个镜像:
[docker]#dockersearchjavaNAMEDESCRIPTIONSTARSOFFICIALAUTOMATEDnodeNode.jsisaJavaScript-basedplatformfors…11734[OK]tomcatApacheTomcatisanopensourceimplementati…3368[OK]openjdk"Vanilla"buildsofOpenJDK(anopen-source…3362[OK]javaDEPRECATED;use"openjdk"(orotherJDKimpl…1976[OK]
第4个name为java的便是,为了方便后面操作,这里直接将镜像pull到本地。
dockerpulljava:8
查看本地pull之后,本地的镜像列表:
[root@iZ2zehx0enix3i0aiea7p0Zdocker]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEjava8d23bdf5b1b1b5yearsago643MB
后续执行镜像制作时便以该镜像为基础进行构建。
COPY指令COPY,复制指令,从上下文目录中复制文件或者目录到容器里指定路径。
COPY./hqy-service-channel.jar./app.jar
其中第一个参数为源文件路径,第二个参数为容器内目标文件路径。这里是将当前目录下的Spring Boot项目jar包hqy-service-channel.jar,复制到容器内并命名为app.jar。在执行创建镜像命令之前,需要把项目jar包放到Dockerfile同级目录下。
ENV指令ENV指令,用于设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。
基本格式为:
ENVENV = = ...
第一个参数为变量key,第二个参数为变量值,这里用于设置SpringBoot项目的配置文件的profile为prod(生产配置文件)。
EXPOSE指令EXPOSE指令,仅仅只是声明端口。作用是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。另外,在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
基本格式:
EXPOSE<端口1>[<端口2>...]
这里采用了8190端口。
ENTRYPOINT指令ENTRYPOINT指令,类似于CMD指令,但其不会被docker run的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT指令指定的程序。在执行docker run时可以指定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.
其中-t channel指定了构建镜像的名称,当然也可以同时指定版本编号-t channel: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
执行docker images可查看到镜像构建完毕:
[docker]#dockerimagesREPOSITORYTAGIMAGEIDCREATEDSIZEchannellatest1720afb4fec751secondsago725MBjava8d23bdf5b1b1b5yearsago643MB
后续便可以通过docker run命令来启动容器了。
这里为了方便CI/CD操作,我们可以通过脚本来完成整个容器停止、容器移除、镜像的移除、镜像的重新制作以及容器的重新启动,这样CI/CD的系统只用调用对应的脚本即可。
示例脚本start.sh如下:
#!/bin/bash#停止容器dockerstopchannelecho"停止容器success!"#移除容器dockerrmchannelecho"移除容器success!"#移除镜像dockerrmichannelecho"移除镜像success!"#制作镜像dockerbuild-tchannel/opt/channel/docker/echo"制作镜像success!"#启动容器dockerrun-d--namechannel-p8190:8190-v/opt/channel/logs/:/opt/channel/logs/channelchannel:latestecho"启动success!"
执行上述脚本之后,查看容器执行结果:
[bin]#dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMESe9eff75cdb6fchannel"java-jar-Duser.ti…"30secondsagoUp28seconds0.0.0.0:8190->8190/tcpchannel
可以看到容器已经成功启动。当重新构建新的jar包时,只需对目录中的jar包进行替换,然后再执行一遍start.sh命令即可。
小结本文带大家以具体的实例演示了如何制作Docker镜像,在制作Docker镜像过程中需要注意的事项,以及制作之后用于CI/CD的脚本编写。大家可参考以上实例,根据自己的业务场景所需进行对应的改造。
Linux安装Docker完整教程
2022-07-28
CAS+失败重试方式实现数据库的原子性更新
2022-07-27
面试被问Linux 命令su和sudo的区别?
2022-07-26
到底如何保证线程安全,你真的清楚吗?(干货推荐)
2022-07-25
4种 Redis 集群方案介绍+优缺点对比
2022-07-24
2.2w Star,这是一款什么样的Nginx可视化配置神器?
2022-07-23
如果你觉得这篇文章不错,那么,下篇通常会更好。备注“公众号”添加微信好友(微信号:zhuan2quan)。
▲长按关注”程序新视界“,洞察技术内幕相关阅读
-
世界热推荐:今晚7:00直播丨下一个突破...
今晚19:00,Cocos视频号直播马上点击【预约】啦↓↓↓在运营了三年... -
NFT周刊|Magic Eden宣布支持Polygon网...
Block-986在NFT这样的市场,每周都会有相当多项目起起伏伏。在过去... -
环球今亮点!头条观察 | DeFi的兴衰与...
在比特币得到机构关注之后,许多财务专家预测世界将因为加密货币的... -
重新审视合作,体育Crypto的可靠关系才能双赢
Block-987即使在体育Crypto领域,人们的目光仍然集中在FTX上。随着... -
简讯:前端单元测试,更进一步
前端测试@2022如果从2014年Jest的第一个版本发布开始计算,前端开发... -
焦点热讯:刘强东这波操作秀
近日,刘强东发布京东全员信,信中提到:自2023年1月1日起,逐步为...