前言
公司项目使用Coding进行管理,每次打包部署都需要经历以下流程
- 代码提交
- 项目打包
- 登录服务器
- 上传应用到服务器
- 执行部署脚本
- 发现Bug ->修改Bug -> 重复第一道流程
了解过Jenkins等持续集成工具后,查看到Coding也有相关服务,并且Coding还提供一台云主机来进行构建操作。
如果你也长期经历以上流程,强烈建议你了解一下持续集成,因为手动部署实在是麻烦又耗时,使用持续集成后省下的时间又可以多写两行Bug了🤪
需求
我们希望每次写完代码,提交到主分支以后,项目能够自动编译打包,上传至服务器,并自动重启应用
前期工作
凭证管理
官方文档,凭证管理 https://coding.net/help/docs/project-settings/credential.html
生成Rsa私钥
登录服务器,生成Rsa私钥
生成好的文件在/root/.ssh
路径下
将id_rsa.pub
添加到authorized_keys
文件中,并重启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
AppName=jenkins_demo-0.0.1-SNAPSHOT.jar
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_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,该持续集成应该怎么实现呢?评论区有答案吗?