# Jenkins教程 - 3 部署SpringBoot项目

下面使用 Jenkins + Git + Maven, 自动化部署一个 SpringBoot 项目。

# 3.1 准备SpringBoot项目

# 1 新建一个SpringBoot 项目

新建一个 SpringBoot 项目,这里我就在 SpringBoot 中新建一个 Controller。

只提供了一个接口,证明 SpringBoot 能访问运行就好了。

package com.doubibiji.hellospringboot.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello SpringBoot";
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

项目结构:

# 2 将SpringBoot项目托管码云

Jenkins 服务后面需要从 git 仓库拉取代码进行构建,这里使用 gitee 码云来托管代码,所以这里将 SpringBoot 项目托管到码云。

这里细节就不介绍了,git 不太会的,可以学习本站的 git教程 (opens new window)

# 3.2 配置插件和工具

# 1 安装Maven插件

我们现在构建项目需要使用 Maven,所以还需要安装一个 Maven 插件。


在可用的插件中,搜索 maven,在搜索结果中选中 Maven Integration 进行安装。

拉到最下面,查看安装进度

# 2 配置Maven工具

我们的自动化构建任务是使用 Maven 构建的,需要告诉 Jenkins Maven的安装位置。


配置 Maven 的路径:

使用 mvn -v 可以查看 maven 安装的路径。

在前面已经配置了 Maven 使用阿里云的镜像。

# 3 安装SSH插件

这个插件的作用就是将 Jenkins 构建的 SpringBoot 项目的 jar 包发布的业务服务器上。

还是刚才安装插件的步骤:

# 4 配置业务服务器

后面需要将构建的 SpringBoot 的 jar 包发布到业务服务器,所以在这里配置一下要发布到的业务服务器的信息。

我也不知道选项为啥突然变成中文了,就是之前的 Manage Jenkins 下的 System

拉到最下面找到 Publish over SSH,安装完 SSH 插件才有这个选项。


新增一个 SSH Server。


配置上面几个选项就可以了,配置完成,在最下面有个测试的按钮,可以测试一下配置有没有问题,没有问题,保存配置。

# 3.3 创建构建任务

# 1 新建任务

在 Jenkins 管理页面,新建Item,也就是新建构建任务。

填写任务名称:

选择Maven项目,前面安装Maven插件,才有这个选项!

# 2 源码管理

分支配置:

# 3 构建配置

上面使用默认配置即可。

  • Pre Steps :表示构建前的工作,例如要执行一些脚本,这里我们不需要;
  • Post Steps :表示构建后的工作;
  • Root POM :指定构建使用的 pom.xml,这里表示在远程仓库的根目录下,如果远程仓库中,pom.xml不在根目录下,例如在 demo 目录下,则这里需要配置 demo/pom.xml

# 3.4 执行构建

前面的配置保存后,可以在控制台查看到配置的任务。如果要修改配置,直接点击任务 --> 配置,重新进行修改即可。

现在任务已经创建了,可以在任务列表,点击任务后面的按钮,手动执行构建。

执行后,在构建执行状态列表,可以看到构建的进度。

可以点击进去查看构建的控制台信息:


如果已经构建完成,也可以点击任务进入任务信息页面,查看构建失败的原因:

摸索一下页面的功能就好了。

第一次构建需要的时间可能比较久,因为要下载很多项目依赖的 jar 包。


构建如果成功,会显示成功,并在 jenkins 的工作空间下构建出 SpringBoot 项目的 jar 包。

如果没有成功,可以看一下日志,失败的原因是什么。

# 3.5 部署到业务服务器

上面的步骤只是在 Jenkins 服务器上生成了 jar 包,还没有一条龙执行到自动部署到业务服务器。下面继续。

# 1 配置传输

重新进入到构建任务的配置页面,对 Post Steps 进行配置,也就是构建后的操作。

选择 Send files or execute commands over SSH

因为 Jenkins 服务和 业务服务不是在同一个服务器上,所以才使用 Send files or execute commands over SSH ,如果在一台服务器 上,直接选择 执行 shell 就可以了,都不用将文件推送到别的服务器,直接使用shell命令 mv 去移动文件就可以了。

因为是不同服务器,所以需要使用SSH将文件传输到服务器,这里配置远程服务器信息,填写如下配置:

  • SSH Server:需要配置业务服务器,这里才有选择,看一下 3.2.4。

  • Source files :在 Jenkins 服务器上,构建的 jar 包在 Jenkins 的工作空间下,也就 ~/.jenkins/workspace ,我这里构建的任务名称叫 hello-jenkins ,所以 jar 包的地址是 /home/doubi/.jenkins/workspace/hello-jenkins/target/hello-springboot-0.0.1-SNAPSHOT.jar。这里填写的地址是从构建的任务所在的文件夹开始算的,也就是 /home/doubi/.jenkins/workspace/hello-jenkins 算起,所以这里我填写了 target/*.jar ,使用了通配符,表示 target 目录下所有的 jar 包。

  • Remote directory :表示传输到的远程服务器的路径,也是从家目录 ~ 开始算起,表示传输到 ~/projects 目录下,也就是传输到 /home/doubi/projects 目录下,则传输完成,在业务服务器上 jar 包的路径是 /home/doubi/projects/hello-springboot-0.0.1-SNAPSHOT.jar ,如果路径不存在,会自动创建。

  • Remove prefix :这个表示删除的路径的前缀,如果不填写,则复制到远程后,会将 target 目录一起复制,会变成 /home/doubi/projects/target/hello-springboot-0.0.1-SNAPSHOT.jar ,这里将 target 路径删掉。

  • Exec command :表示上传完成执行的命令,这里直接运行 jar 包,注意要使用 nohup 命令,在后台运行,并指定日志的输出,否则可能以为执行超时而失败。

执行的命令:

nohup java -jar /home/doubi/projects/hello-springboot-0.0.1-SNAPSHOT.jar > hello-springboot.log 2>&1 &
1

命令就是在后端运行jar包。

# 2 重新构建

除了可以点击 Dashboard 页面,任务列表后面的构建按钮,可以构建。


还可以在任务详情页面,点击 立即构建 按钮。


在 Jekins 的控制台页面,可以查看构建日志:

可以看到是否构建和传输成功,根据提示信息修改即可,如果传输成功,运行不成功,可以查看业务服务器上 jar 包有没有传输到对应的目录。

Post StepsSend files or execute commands over SSH 的高级中,有一个 Exec timeout 选项,表示执行超时时间,默认是2分钟,如果任务构建时间比较长,可以将时间设置的长一些。

我这里是 201ms 就成功的。

# 3 访问SpringBoot项目

访问 SpringBoot 项目测试的接口,发现可以访问。

注意,上面传输完成的脚本只是启动了jar包,没有涉及到其他的操作,所以如果重新再执行构建,并不会将之前的 SpringBoot 项目停掉,而是会再次运行 SpringBoot 项目,会出现端口冲突,启动失败。

下面来处理这个问题。

# 4 执行脚本

在上面构建的时候,最终执行的命令是 nohup java -jar /home/doubi/projects/hello-springboot-0.0.1-SNAPSHOT.jar > hello-springboot.log 2>&1 & ,这个只是启动项目,没有备份、停止操作。

我们可以在业务服务器中,新建一个脚本文件,在脚本文件中进行备份、停止、启动项目的操作,然后在构架的时候,在 Post Steps 中执行这个脚本文件即可。

下面就是编写这个脚本文件,我这里起名叫 deploy.sh

#!/bin/bash

# jar包的名字
fileName=hello-springboot-0.0.1-SNAPSHOT.jar

# 这里是获取正在运行的程序的PID,通过PID来杀掉之前运行的程序
PID=$(ps -ef | grep $fileName | grep -v grep | awk '{ print $2 }')

# 如果PID不为空
if [ -n "${PID}" ]; 
then
  # 杀掉程序
  kill -9 $PID
  echo 'Application is stopping...'
else
  echo 'Application is not running'
fi

# 创建日志目录
mkdir -p "./logs/"

# 运行jar包
nohup java -jar ./$fileName > ./logs/hello-springboot.log 2>&1 &

# 创建备份目录(如果不存在)
mkdir -p "./packages/"

# 备份jar包
backupTimestamp=$(date +%Y%m%d%H%M)
cp $fileName ./packages/$backupTimestamp-$fileName
echo "backup jar success!"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

在上面的脚本中,进行了杀掉原来的程序、运行新的jar包,备份jar包的操作,编写完脚本文件,最好先本地执行测试一下,确保可用。

上面的 PID=$(ps -ef | grep $fileName | grep -v grep | awk '{ print $2 }') 就是从下面查询到的进程信息中获取正在运行的项目的PID 赋值给 PID 变量。

grep $fileName 就是过滤名称带有 hello-springboot-0.0.1-SNAPSHOT.jar 的进程,如下图:

grep -v grep 就是再过滤掉含有 grep 的进程,那么就剩下第一条记录,awk '{ print $2 }' 表示取出这个记录中的第二个字段,也就是PID 2483


然后给这个文件添加执行权限:

chmod +x deploy.sh
1


脚本写好了,现在让 Jenkins 直接在上传完文件执行这个脚本就可以了。

重新配置 Post Steps

配置完成,重新直接构建,可以看到,每次SpringBoot项目都会重启启动执行。

# 5 修改SpringBoot项目

现在重新修改SpringBoot项目,上传到gitee码云,然后使用 Jenkins 重新执行构建,看看能否自动拉取代码,完成自动部署更新。

重新修改 SpringBoot Controller的代码:

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello www.doubibiji.com";
    }

}
1
2
3
4
5
6
7
8
9

并推送到 gitee。

然后重新使用 Jekins 执行构建。构建完成,访问 SpringBoot 项目。发现自动构建更新完成。

至此,完成了整个 SpringBoot 项目的自动构建。