cd(持续部署)是非常好的软件研发实践。
本文介绍如果通过jenkins(和一些插件) + docker实现java项目的持续部署(cd)。
在教程开始前先说几个背景:
- 方案分为3步: gradle构建、docker镜像编译及上传、docker容器部署
- gradle构建:jenkins机器
- docker编译上传:机器B,由于我的jenkins本身也跑在一个docker容器内,再装一层docker就比较蛋疼,所以采用over ssh远程调用另一个安装了docker的机器B完成build并上传到私有docker image 仓库
- docker容器部署:为了简单,这里我们也使用B作为部署机
- 代码放在gerrit上,理论上也可以用任意的git服务器,例如github
另外,为了方便重用,本文的许多变量是通过插件注入的,请大家自行理解,不再详细讲解了。
1、配置git仓库
我这里使用的是gerrit,可自行查找资料,或者参考我的开源项目docker-gerrit-test
2、jenkins安装和插件配置
基本安装可以自行查找资料,或者参考我的开源项目docker-jenkins-test
安装以下插件:插件列表:
- gerrit插件
- git插件
- gradle插件
- publish over ssh插件
- Environment Injector Plugin插件
3、一个私有docker image仓库
基本安装可以自行查找资料,或者参考我的开源项目docker-camp2-test
4、一个使用gradle进行构建的Java项目
我这里是采用gradle进行的构建,如果你需要使用maven,替换下文对应的插件即可。
https://github.com/liheyuan/eureka-server
我这里把它放到了gerrit中,具体可以自行搜索教程
5、一个远端机器B
这里B机器安装了docker,将来的build和运行都是在这个机器上执行的。
并创建目录 $HOME/jenkins_workspace 这个是将来jenkins工作的根路径
6、配置jenkins上的一个远程登录机器
安装publish over ssh后,可以添加多个远程机器
管理员登录jenkins -> Manage Jenkins -> Publish over SSH
配置下key和host B
(1) 配置gradle版本
在安装了gradle插件后,还需要有一个可执行的gradle版本
Manage Jenkins -> Global Tool Configuration -> Gradle
添加一个新版本,我这里是4.2.1,不推荐远程安装,建议直接拷贝到jenkins机器的某个路径下,然后指定路径。
(2) jenkins新建一个free style项目eureka-server
eureka配置 代码管理选择git,如果没有认证,搞一下add(推荐用直接输入的方式,毕竟ssh-keygen重启容器会消失)
(3) eureka-server 配置一些常量
在Build中新建一个Executer shell 和 Inject environment variables,分别配置如下:
如上图所示,主要是将我们需要的build脚本拷贝到当前工作路径,以及一些环境变量配置,我们通过inject插件注入这些环境变量。
脚本内容如下:docker_build_springboot.sh
#!/bin/bash set -x if [ x"$#" != x"4" ];then echo "Usage: $0 <project_name> <version> <jar_filename> <expose_port>" exit -1 fi PRIVATE_DOCKER_HUB="docker.coder4.com:5000" PROJECT_NAME="$1" PROJECT_VERSION="$2" JAR_FILE="$3" EXPOSE_PORT="$4" DOCKER_IMAGE_VERSION="build_$PROJECT_VERSION" DOCKER_IMAGE_FULL_VERSION="$PROJECT_NAME:$DOCKER_IMAGE_VERSION" EXPOSE_CMD="" if [ x"$EXPOSE_PORT" != x"" ];then EXPOSE_CMD="EXPOSE $EXPOSE_PORT" fi # Make Docker file cat > Dockerfile <<EOF FROM java:8-jdk-alpine VOLUME /tmp /app WORKDIR /app COPY ${JAR_FILE} /app $EXPOSE_CMD ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/${JAR_FILE}"] EOF # docker build docker build -t $PROJECT_NAME . docker tag $PROJECT_NAME $PRIVATE_DOCKER_HUB/$DOCKER_IMAGE_FULL_VERSION docker push $PRIVATE_DOCKER_HUB/$DOCKER_IMAGE_FULL_VERSION
(4)eureka-server 配置 gradle编译及部署
Build中添加一个Invoke Gradle script选择刚才的gradle版本,以及task为build
现在可以Build一把了,没什么问题,会成功的!
8、ci/cd 第2步 docker镜像编译
eureka-server Build中 新增 Send files or execute command OVER SSH,选择node-1 并设置2个transfer set,如下:
上面主要是配置了docker镜像build脚本的分发以及执行,脚本在上面步骤中有了,不再重复。
此时再次执行Build,远程机器应该有类似如下结构:
/home/coder4/jenkins_workspace/eureka_server_1
并且顺利的话,会完成编译、上传到私有docker镜像的步骤
9、ci/cd 第3步 docker运行
我们可以想象下,运行这个过程其实可以重用的(每个不同镜像运行都不会有太多需要定制化的参数),有镜像名字+版本就可以了
所以我们新建一个infra -deploy工程
(1) 选择"This project is parameterized",并添加上述2个参数:
(2) Build增加Send files or execute commands over SSH的步骤
如上图,也是分发脚本,远程执行
脚本内容如下:
#!/bin/bash set -e if [ x"$#" != x"2" ];then echo "Usage: $0 <project_name> <version>" exit -1 fi PRIVATE_DOCKER_HUB="docker.coder4.com:5000" NAME="$1" PROJECT_VERSION="$2" DOCKER_IMAGE_VERSION="build_$PROJECT_VERSION" DOCKER_IMAGE_FULL_VERSION="$NAME:$DOCKER_IMAGE_VERSION" EXPOSE_PARAM="" if [ x"$EXPOSE_PORT" != x"" ];then EXPOSE_PARAM="-p $EXPOSE_PORT:$EXPOSE_PORT" fi # remove and run docker ps -q -a --filter "name=$NAME" | xargs -I {} docker rm -f {} docker run \ --network camp \ --name $NAME \ --hostname $NAME \ --detach \ docker.coder4.com:5000/$DOCKER_IMAGE_FULL_VERSION
保存成功后,我们填入eureka-server和一个成功的构建版本,一切顺利的话,应该可以在机器B上面成功运行这个实例啦!
到这里,基本的配置都已经完成了。
但是,这个ci/cd还是需要2个步骤,能不能合并成一个步骤(即编译最新代码并部署上线)呢?
当然可以!
10、ci/cd 第4步 使用pipeline串联任务
安装pipeline插件,然后新建一个pipeline任务,叫eureka -server -rocket吧
pipeline如下配置
上述分为了2个stage,会分别调用我们上文定义的编译、运行的job
贴一下代码吧:
def build_num proj_name=env.JOB_NAME.replace("-rocket", "") node { stage('build') { def job = build(proj_name) build_num = job.number } stage('deploy') { build job: 'infra-deploy', parameters: [ [$class: 'StringParameterValue', name: 'BUILD_NAME', value: proj_name], [$class: 'StringParameterValue', name: 'BUILD_VERSION', value: build_num.toString()], ] } }
好了,现在在pipeline build一把,享受cd的便捷吧~