使用coding持续集成SpringBoot项目

前言

公司项目使用Coding进行管理,每次打包部署都需要经历以下流程

  • 代码提交
  • 项目打包
  • 登录服务器
  • 上传应用到服务器
  • 执行部署脚本
  • 发现Bug ->修改Bug -> 重复第一道流程

了解过Jenkins等持续集成工具后,查看到Coding也有相关服务,并且Coding还提供一台云主机来进行构建操作。

如果你也长期经历以上流程,强烈建议你了解一下持续集成,因为手动部署实在是麻烦又耗时,使用持续集成后省下的时间又可以多写两行Bug了🤪

需求

我们希望每次写完代码,提交到主分支以后,项目能够自动编译打包,上传至服务器,并自动重启应用

前期工作

凭证管理

官方文档,凭证管理 https://coding.net/help/docs/project-settings/credential.html

生成Rsa私钥

登录服务器,生成Rsa私钥

1
ssh-keygen -t rsa

生成好的文件在/root/.ssh路径下

id_rsa.pub添加到authorized_keys文件中,并重启sshd服务

1
systemctl  restart sshd

添加至Coding凭证管理

1.将id_rsa文件中的内容添加至Coding

2.在录入凭据页面,输入相关信息

  • 选择「SSH 私钥」凭据类型
  • 输入凭据名称,必填项,长度不超过 255 个字符
  • 输入 SSH 私钥,必填项
  • 输入私钥口令,非必填
  • 输入描述,非必填

3.勾选需要授权的持续集成功能。只有进行凭据授权后,在使用 CODING 持续集成功能模块创建构建计划时才有权限使用该凭据。

4.点击「创建」即可完成创建。录入成功的凭据会显示在凭据管理页面。

白名单释放

因为我们使用的是阿里云的服务器,所以需要释放Coding的IP,如果你使用的不是阿里云可忽略

Coding文档:https://coding.net/help/docs/ci/faq/job-fail.html#aliyun

执行 SSH 命令访问阿里云主机时提示 Connection reset 错误。

此问题是阿里云侧白名单未放行 CODING IP 所致。前往阿里云「安全管控平台」→「安全管控」→「新增访问白名单」,将构建机的 IP 加入至白名单中可以防止其在访问云主机时被拦截。

CODING 构建机所使用的出口 IP 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 中国上海节点

111.231.92.100

81.68.101.44

# 中国香港节点

124.156.164.25

119.28.15.65

# 美国硅谷节点

170.106.136.17

170.106.83.77

构建计划

创建构建计划

首先进入Coding的项目中,找到持续集成-构建计划,新建构建计划

点击自定义构建流程

Jenkinsfile

创建自定义流程以后我们开始编写Jenkinsfile

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

pipeline {
agent any
stages {
stage('检出') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: GIT_BUILD_REF]],
userRemoteConfigs: [[
url: GIT_REPO_URL,
credentialsId: CREDENTIALS_ID
]]])
}
}

stage('编译') {
steps {
echo '构建中...'
sh 'mvn clean package'
echo '构建完成'
echo '当前所在位置:'
sh '''pwd
ls'''
sh '''ls ${project_target_path}
'''
sh '''tar -zcf /root/workspace/tmp.tar.gz ${project_target_path}/*.jar
ls /root/workspace/'''
}
}

stage('重启应用') {
steps {
echo '开始发送文件到远端服务器...'
script {
def remote = [:]
remote.name = 'web-server'
remote.allowAnyHosts = true
remote.host = host
remote.port = port as Integer
remote.user = user
// 把「CODING 凭据管理」中的「凭据 ID」填入 credentialsId,而 id_rsa 无需修改
withCredentials([sshUserPrivateKey(credentialsId: credentialsId, keyFileVariable: 'id_rsa')]) {
remote.identityFile = id_rsa
// SSH 上传文件到远端服务器
sshPut remote: remote, from: "/root/workspace/tmp.tar.gz", into: '/tmp/'
// 解压缩
sshCommand remote: remote, command: "tar -zxf /tmp/tmp.tar.gz -C /tmp/"
//复制文件到运行目录
sshCommand remote: remote, sudo: true, command: "cp -R /tmp/${project_target_path}/* ${project_path}"
//执行脚本重启应用
sshCommand remote: remote, sudo: true, command: sh_path_command


}
}

echo '部署成功...'
}
}

}
}

Jenkinsfile文件中有七个环境变量字段,如下

字段值 字段名称 字段注释 示例
project_path 项目存放的目录地址 jar在服务器上实际存放的地址 /usr/local/java/demo
sh_path_command 需要执行的sh命令 一般是 sh 脚本所在位置加执行的操作 sh /usr/local/java/demo/start.sh restart 此命令是执行/usr/local/java/demo目录下start.sh脚本的restart操作
host:目标服务器地址 服务器IP地址 111.111.111.111
port SSH端口号 端口号 22
user SSH用户名 登录用户名 root
credentialsId SSH登录凭据 ssh登录凭证 选择项,需要配置私钥并添加至coding凭证管理中
project_target_path jar包所在目录 执行打包命令后jar所在的目录 demo/target

以上环境变量需要添加至该构建计划的【变量与缓存】中,如图:

我们的项目采用Maven构建,我勾选上了缓存Maven目录,这样每次构建就不需要重复下载依赖了,速度更快

启动脚本

此脚本来自ruoyi-vue项目,脚本内容需要调整$AppName$APP_HOME参数,$JVM_OPTS参数可酌情调整

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/bin/sh
# ./start.sh start 启动
# ./start.sh stop 停止
# ./start.sh restart 重启
# ./start.sh status 状态
AppName=jenkins_demo-0.0.1-SNAPSHOT.jar
# JVM参数
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512M -Xmx512M -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
#App所在目录
APP_HOME="/usr/local/java/demo"
LOG_PATH=$APP_HOME/logs/$AppName.log

if [ "$1" = "" ];
then
echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m"
exit 1
fi

if [ "$AppName" = "" ];
then
echo -e "\033[0;31m 未输入应用名 \033[0m"
exit 1
fi

function start()
{
echo "开始执行start"
cd $APP_HOME
nohup java -jar $APP_HOME/$AppName > nohup.out & 2>&1 &
echo "结束执行start"
}

function stop()
{
echo "Stop $AppName"

PID=""
query(){
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
}

query
if [ x"$PID" != x"" ]; then
kill -TERM $PID
echo "$AppName (pid:$PID) exiting..."
while [ x"$PID" != x"" ]
do
sleep 1
query
done
echo "$AppName exited."
else
echo "$AppName already stopped."
fi
}

function restart()
{
stop
sleep 2
start
}

function status()
{
PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l`
if [ $PID != 0 ];then
echo "$AppName is running..."
else
echo "$AppName is not running..."
fi
}

case $1 in
start)
start;;
stop)
stop;;
restart)
restart;;
status)
status;;
*)

esac

疑问/讨论

如果使用Nginx配置了负载均衡,部署了多个Jar,该持续集成应该怎么实现呢?评论区有答案吗?


使用coding持续集成SpringBoot项目
https://www.songhaozhi.com/2022/10/17/使用coding持续集成SpringBoot项目/
Beitragsautor
宋浩志
Veröffentlicht am
October 17, 2022
Urheberrechtshinweis