初学Docker(2)——Jenkins+Git+Docker打包应用成Docker镜像
编辑说在前面
虽然说跨度有点大,但是我写这文章是给有些基础的人们看的,而不是入门教程。
Jenkins
什么是Jenkins
Jenkins是开源CI&CD软件领导者, 提供超过1000个插件来支持构建、部署、自动化, 满足任何项目的需要。
上面的是Jenkins官方对自己的描述。
在开始之前,我们再次描述一下上一篇文章说过的开发流程:省去前面的分析步骤以及开发步骤,软件开发完成之后,我们需要在本地IDE打包,然后将打包之后的jar通过ssh或者其他方式上传到运行环境,然后停止旧版本的jar,运行新版本的jar。而每一次修改、发布,我们都需要做 build-package-upload-stop-run
的重复操作,费时而且每一次的编译都在本地,如果配置不够,那么编译时间也不短(真实经历)。
借助Git,我们能够保证在多台机器上拥有相同版本的源代码,那么我们可以将上面的流程修改为: 本地提交-运行服务器更新源代码-运行服务器编译-运行服务器重启项目
。少去了上传jar的步骤,但是一般来说运行服务器的配置并不算高,如果配置还不错,那么可能会存在性能过剩的问题,况且我们还是需要自己ssh上去执行编译命令,依旧比较繁琐。
好的,说回Jenkins,Jenkins是一个持续构建的可视化web工具,什么是持续构建呢?也就是我们上面提到的 build-package-upload-stop-run
这一套流程,不过是全自动的。
Jenkins的安装
安装步骤照着官网来就行了,对于Linux平台,官方提供了三种方式安装: Docker
、 war
、 包管理工具
,war我没有试过,Docker说是最好不要用这种方式,因为后面我们需要与Docker一起使用,装在里面怪怪的(而且我也没成功过,已经成功了)。最后我选择通过包管理工具进行安装。
安装完成之后通过向导的设置,当询问“安装插件”时,安装建议的插件就行了,虽然还是差一些插件,需要时安装就行了。
提示:安装以及向导的过程最好还是放到互联网的环境中,不然可能会有点慢,等进入之后,可以设置插件的镜像地址,教程:
- 进入jenkins系统管理(Manage Jenkins)
- 进入插件管理(Manage Plugins)
- 点击高级(Advanced),修改升级站点的地址为清华大学镜像地址: https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json
Jenkins
中文的方法:前往插件中心安装 Localization: Chinese (Simplified)
插件即可。(我安装了之后部分还是英语,不知道什么原因)
安装需要的插件
- Git Plugin
- GitLab Plugin
- GitHub Plugin
可能还需要安装其他插件,但是记不起来了,暂时先安装这些吧。
Docker
Docker的相关概念以及安装我们已经在上一篇文章中介绍了 (安装的步骤直接用的是官网的,因为真的是被写烂了) ,所以我们在这里,需要在项目中提供一个Dockerfile文件用来构建Docker镜像,弄好Docker的相关环境变量以及配置之后,我们将打包镜像以及上传的操作放到Shell脚本中去,这样子我们的Jenkins只需要执行这一个脚本就行了。
项目的配置
因为我的项目基本上全部都是使用Gradle进行构建的,所以我这里只介绍Gradle项目的构建,除了通过Gradle打包Docker镜像的部分,其他的步骤在maven项目也是一样的,只是命令不同而已。
配置Gradle项目打包Docker镜像
在这里有两个方法,一种是通过Gradle插件进行自动打包甚至可以自动发布,另一种是通过我们编写的Dockerfile进行打包。如果是第一种,那么就用不到Jenkins了,这一种方法我们只打包成Docker镜像并放到本地,后续的上传操作交给脚本进行。我比较推荐的是第二种方法,更加的定制化一些,而且第一种本质上也是通过Gradle配置生成Dockerfile然后打包。
关于Gradle插件,有很多,我在这里介绍 bmuschko/gradle-docker-plugin
,我觉得这个插件方便配置一些。
添加plugins
plugins {
id 'com.bmuschko.docker-spring-boot-application' version '4.10.0'
}
然后在下面添加docker配置项:
docker {
springBootApplication {
baseImage = 'openjdk:8-alpine'
ports = [8080]
tag = 'awesome-spring-boot:1.115'
jvmArgs = ['-Dspring.profiles.active=production', '-Xmx2048m']
}
}
其中 baseImage
是我们的镜像的“父镜像”(还记得上次提到的 FROM
吗), ports
是需要暴露的端口,也就是 EXPOSE
部分。 tag
就不需要说了吧, jvmArgs
是运行时指定的命令。
添加之后刷新Gradle的依赖,然后我们就可以在 Gradle 页面中找到docker的相关task。
这四个Task分别是:
Task名称 | 依赖于 | 描述 |
---|---|---|
dockerSyncBuildContext | classes | 将应用程序文件复制到临时目录以进行映像创建。 |
dockerCreateDockerfile | dockerSyncBuildContext | 为Spring Boot应用程序创建Dockerfile文件。 |
dockerBuildImage | dockerCreateDockerfile | 为Spring Boot应用程序构建Docker镜像。 |
dockerPushImage | dockerBuildImage | 将创建的Docker镜像推送到存储库。 |
从这四个Task来看也知道这个插件的流程,也是通过生成Dockerfile来构建Docker镜像的。
编写Dockerfile
我们也可以通过自己编写Dockerfile构建我们的Docker镜像,Dockerfile的相关命令知识我们在上一次已经介绍过了,所以不再过多的阐述。以下是我自己使用的Dockerfile
FROM openjdk:8-alpine
MAINTAINER mystery0dyl520@gmail.com
LABEL maintainer=mystery0
VOLUME ["/logs","/config"]
COPY build.jar run.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Duser.timezone=GMT+08","-jar","/run.jar"]
EXPOSE 8080
之所以挂载 /logs
和 /config
目录是因为我编译的项目是将jar打包进Docker镜像,而上面的那个插件是将编译之后生成的class文件打包进镜像,所以插件生成的Dockerfile和我自己定义的不相同,而SpringBoot项目启动的时候会在指定的目录寻找配置文件(application.properties 或者 application.yaml),为了在运行时比较灵活,所以将jar所在的根目录下的 /config
暴露出来,挂载我们宿主机的目录,从而在生产环境使用生产环境的配置文件。 /logs
也是一样的原理,logback在项目中配置的日志生成目录在这个目录下,为了方便寻找错误,所以我们将这个目录放在宿主机。
COPY
命令将打包生成的jar拷贝进镜像的根目录,用来运行。 EXPOSE
暴露8080端口。
添加编译脚本build.sh
在Gradle项目中都提供了gradle的脚本,也就是项目根目录的 gradlew
以及 gradlew.bat
,bat是提供给Windows环境使用的,我们是Linux环境,所以在build.sh中使用以下命令即可:
./gradlew clean build dockerBuildImage
clean
以及 build
是清理上一次编译的文件然后重新进行编译,最后的 dockerBuildImage
就是通过插件自动打包Docker镜像到本地仓库中,执行完毕之后使用 docker images
命令就能看到对应的镜像了。
如果你使用的是自己编写Dockerfile的方法,那么最后的 dockerBuildImage
Task不用添加,只需要执行 clean
和 build
命令就能得到jar了,但是需要将这个jar拷贝到指定的目录,和Dockerfile放在一起,这样子我们才能使用 docker build
进行打包镜像的操作,所以我们需要得到生成的jar的名字,路径在 build/libs
下面,但是名字根据项目的不同而不同,对应的 baseName
是从 settings.gradle
中获取的, version
是从 build.gradle
中获取的,所以我们需要把这些操作也写到脚本中:
# 获取打包的jar文件名的信息
BASE_NAME=$(cat settings.gradle | grep "rootProject.name = " | awk -F "= \"" '{print substr($NF,0,match($NF,/"/)-1)}')
VERSION_NAME=$(cat build.gradle | grep "version = " | awk -F "= \"" '{print substr($NF,0,match($NF,/"/)-1)}')
# 创建一个临时目录,用来打包Docker镜像
mkdir build/docker
cp "build/libs/$BASE_NAME-$VERSION_NAME.jar" "build/docker/build.jar"
Dockerfile文件我是放在根目录的 script/docker 下面的
cp script/docker/Dockerfile build/docker/Dockerfile
# 打包镜像
docker build -t "$DOCKER_TAG:$VERSION_NAME-$VERSION_CODE" build/docker
因为本人对于脚本不是很熟悉,默认情况下,build.gradle 和 settings.gradle中的相关配置项都是用单引号框起来的,但是我在使用 awk 命令的时候一直没法弄这个单引号,所以就把配置文件中的相关配置改成了双引号。具体如下:
至此,我们已经完成了从springboot项目打包Docker镜像到本地的步骤了。
上传到Registry
到了这里我们可以修改Docker镜像的tag,然后执行 docker push
命令将镜像上传到。 在此之前,我们需要一个Registry。
构建自己的Registry
可以参考这篇文章在Docker中搭建自己的Docker私服,然后将Docker镜像上传到私服中就可以了。 因为我只有一台服务器,所以如果要搭建Registry也是和Jenkins放在一起,为了Registry的验证和安全性着想,还有服务器自身的网络复杂(服务器本身没有公网ip,是通过frp将端口映射到公网服务器上,但是转发Registry的时候一直存在问题),所以我折腾了两天之后放弃了,直接使用的是阿里云的容器镜像服务。
使用阿里云的“容器镜像服务”
https://cr.console.aliyun.com/cn-hangzhou/instances/repositories 创建仓库之后里面有对应的登录命令和push、pull命令。
tag然后上传
Docker镜像上传到Registry的时候,是通过tag来区分服务器的,所以我们先用tag命令改成Registry地址,然后再push。
因为我们前面打包时的tag包含了版本号信息,我们只需要修改前面的DOCKER_TAG就行了,也可以在docker build的时候直接生成对应的tag
复制镜像
docker tag "$DOCKER_TAG:$VERSION_NAME-$VERSION_CODE" "$DOCKER_TAG:latest"
上传
docker push "$DOCKER_TAG:latest"
现在,我们所有的流程已经跑通了,一切只差Jenkins的“自动化"了。
创建Jenkins项目
然后创建了项目之后继续进行配置。
在这里填入项目的github或gitlab地址,然后添加对应的账号以及密码,如果github启用了”两步验证“,那么密码需要填 Personal Access Token
。
如果没有凭据,那么在这里添加一个。
添加构建的操作,也就是执行我们写好的 build.sh 脚本。
最后,点击保存即可。
开始构建 上面的配置我们是手动的,因为对于后端项目来说不是每一次push都需要构建,所以是手动的。
下面的就是构建历史,然后在构建的时候可以查看相应的日志。
日志里面记录了详细的步骤和整个构建的过程,如果构建成功,那么我们就可以在Docker私服中获取构建的Docker镜像了。
至此,整个”自动化“就完成了。接下来我们可以在生产环境上登录私服,然后使用 docker pull
拉取我们的镜像,然后直接运行即可。
参考链接
- 0
- 0
-
分享