目的
- 实现后台java的maven部署
- 实现前端npm的部署
准备
-
两台服务器,我这边都是windows的:
- 服务器A,用于安装jenkins等服务
- 服务器B,待部署机器
-
Jenkins,除了默认插件外,额外需要手动安装插件:
- Blue Ocean,更方便的展示进度等
- Publish Over SSH,用于通过ssh来部署远程服务器
- DingTalk,钉钉机器人通知插件
-
其他的
java
、maven
、npm
等工具
安装软件
- 在
服务器A
中安装jenkins
,由于我这边还是旧的windows server 2012
,对docker
支持不好,所以直接安装的msi文件,具体安装步骤参考官方文档,安装完成后再安装上述的几个插件。 - 在
服务器A
安装Git
,方便从github等代码工具中远程拉取代码,具体安装步骤可以自行网上搜索,由于我们后期会用到ssh
和tar
等工具,所以需要将安装目录\Git\usr\bin
此路径加入到windows环境变量中,这样就可以用里面的各种工具了,如果是npm项目,需要在服务器B
中也要安装,主要是为了用tar
打包命令,如果用其他打包方式也可以不安装。 - 在
服务器A
安装java
、maven
、npm
,最好与开发服务上版本保持一致,并将其加入到windows环境变量中,这样的话jenkins
就可以直接使用。 - 在
服务器B
安装openssh
,安装流程查看官方文档:Install Win32 OpenSSH,同样需要将此路径加入环境变量,且需要按照文档开启端口、开机自启动等
ssh密钥联通
-
整体的流程就是
服务器B
相当于服务端,服务器A
相当于客户端,我们需要用客户端通过ssh链接到服务端,然后传输部署的文件过去,所以需要在客户端中生成私钥和公钥,公钥会给到服务端中,私钥则还是留在客户端,这样ssh链接时通过私钥和公钥验证通过链接到一起就可以传输文件了。 -
在
服务器A
生成密钥,需要注意的是jenkins
的Publish Over SSH
不支持最新的密钥格式,只支持旧的pem格式的,所以需要加一些参数才行,在刚安装的git命令工具中输入ssh-keygen -m PEM -t rsa -b 4096
,按照提示一步步执行,生成的会放在~/.ssh
文件下:xxx_rsa #私钥 xxx_rsa.pub #公钥
-
在
服务器B
可以先启动一下openssh
服务然后关闭,此时会自动生成C:\Program Data\ssh
目录,然后我们配置此目录下的sshd_config
文件,改动如下:PubkeyAcceptedKeyTypes=+ssh-rsa #由于上方说了用的旧的格式密钥,新版本默认已不支持了,所以这里需要额外配置增加支持 PubkeyAuthentication yes #原先是注释来着,打开并设置为yes,表示开启密钥验证 PasswordAuthentication no #原先注释来着,打开并设置为no, 关闭密码验证,否则不安全
-
将
服务器A
生产的公钥放到服务B
中,这里需要注意,服务器B
的用户是管理员用户,则需要放到C:\ProgramData\ssh\administrators_authorized_keys
文件中,如果是普通用户,则需要放到C:\Users\用户名\.ssh\authorized_keys
文件中,通过这里我们也可以发现密钥实际上是和用户名没有关系的,放到哪个用户名下,是根据位置来的,如果这个用户有权限访问到这个密钥实际上就是可以用这个用户名来登陆的。 -
测试联通性,可以在
服务器A
的~/.ssh/config
中配置:Host b_name #服务器B随便起个名字 HostName 192.168.0.31 #服务器B的IP地址 IdentityFile ~/.ssh/xxx_rsa #自身生成的私钥 User administrator #服务器B的登陆用户名 Port 22 #没改端口的话默认22 ServerAliveInterval 60
然后可以
ssh b_name
来测试是否可以联通 -
在
jenkins
的Publish Over SSH
插件配置中并测试联通性,位置Manage Jenkins
->Configure System
->SSH Servers
中:点击测试,看是否联通成功,如果出现类似
Fail Auth
等错误,可以参考此解决方法来解决:Jenkins Publish over ssh authentification failed with private key
git 密钥联通
- 同上在
服务器A
中同上创建私钥和公钥,但是github只支持最新版本的,可以不用加那些参数了,具体参考官方文档:新增 SSH 密钥到 GitHub 帐户 - GitHub Docs - 以github为例,在个人主页的
Setting
->SSH AND GPG keys
->SSH keys
中新增,将生成的公钥粘贴到github中的字段中就可以了 - 和上方一样来测试下联通是否OK,其他
gitee
等原理基本一样
maven项目部署
-
如果不熟悉
jinkins
的pipeline
相关的话,请先按照官方文档demo操作一遍:Build a Java app with Maven -
官方文档demo中都是用的docker,我这边没有,所以不太一样,而且官方文档没有回滚等操作,所以这边给出参考的
Jenkinsfile
:pipeline { agent any //没有用docker parameters {//这个是参数化构建 choice choices: ['deploy', 'rollback'], description: '', name: 'action' string description: 'rollback ID (only number)', name: 'version' } stages { stage('Build') { when {//如果是部署 expression { params.action == 'deploy' } } steps {//maven打包 bat 'mvn -B -DskipTests clean package'//linux下是用sh,windows下则bat } } stage('Archive') { when {//如果是部署 expression { params.action == 'deploy' } } steps {//将打包出来的jar包归档,方便回滚时用到 archiveArtifacts artifacts: '自身相关路径/target/*.jar', followSymlinks: false, onlyIfSuccessful: true } } stage('Rollback') { when {//如果是回滚 expression { params.action == 'rollback' } } steps {//回滚就不需要打包了,直接将原先版本的归档复制到打包出来的路径 bat 'xcopy /s /f /y "%JENKINS_HOME%\\jobs\\%JOB_NAME%\\builds\\%version%\\archive\\自身相关路径\\target" 自身相关路径\\target' } } stage('Publish') { steps {//publish over ssh插件相关参数,verbose为true,则jenkins_deploy.bat脚本的执行信息会返回 sshPublisher(publishers: [sshPublisherDesc(configName: '刚才设置的ssh的名称', sshRetry: [retries: 3, retryDelay: 5000], transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'cmd /c E:\\服务器B相关部署路径\jenkins_deploy.bat $BUILD_NUMBER', execTimeout: 240000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '服务器B相关路径', remoteDirectorySDF: false, removePrefix: '自身相关路径/target', sourceFiles: '自身相关路径/target/*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)]) } } } }
-
上方代码一些注解:
-
上方的
参数化构建
、归档
、publish over ssh
语法都是可以由可视化界面生成的,随便点开一个job后在左侧有流水线语法
这个选项,点开后页面在左侧Declarative Directive Generator
中选择parameters:Parameters
可以生成参数化构建片段;而左侧片段生成器
中archiveArtifacts archive the artifacts
用来生成归档片段,sshPublishers: Send build artifacts over SSH
则用来生成发布的片段,这些具体的使用,可以参考官方文档也可以自行搜索更详细的用法文章。 -
参数化构建如果在pipeline中写了后,不需要在界面中再配置,只需要运行一次后会自动从pipeline中获取,下次再构建就发现有参数需要填写了,要想回滚则选择
rollback
然后输入回滚的构建数字运行则会回滚:
-
一些全局变量,可以参考官方文档:Using a Jenkinsfile,需要注意的是在linux中可以
$JENKINS_HOME
,但是在windows中就必须%JENKINS_HOME%
这样了,需要注意的是publish over ssh
给jenkins_deploy.bat
传递参数的$BUILD_NUMBER
则还是用$
才行,用%%
不起作用,且执行windows脚本时需要用cmd /c
开头。
-
-
服务器B
端的结构及脚本:-
项目结构:
abc: ├─backup │ └─test_2022_06_27_203000_3.jar #在服务端也备份一份,格式:名称+日期+构建号+jar └─prod └─test.jar #生产文件
-
jenkins_deploy.bat
脚本:@echo off ::设置utf8编码运行 chcp 65001 ::移动到当前目录 cd /d E:\abc ::设置时间变量,需要注意的是utf编码和ansi编码所取时间的位数不一样 set "Ymd=%date:~3,4%_%date:~8,2%_%date:~11,2%_%time:~0,2%%time:~3,2%%time:~6,2%" ::将时间的空格用0补齐;将参数可能携带的引号去掉 set "param=%1" ::%Ymd: =0%是将空格转为0,%param:"=%是将"去掉 set backup_name=%Ymd: =0%_%param:"=% ::将现有的移到backup中 copy prod\test.jar backup\test_%backup_name%.jar echo 'prod中的备份到backup中' ::关闭服务 net stop test ::将最新的移动到生产目录中 move /y test.jar prod\test.jar echo '最新的移动到prod中' ::开启服务 net start test @echo on
-
-
上方脚本一些注释:
- jenkins编码没有做设置,所以要用utf8编码来执行bat脚本,否则在jenkins中的信息是乱码。
- 时间参数在
utf8
和ansi
中取值是不一样的,需要注意. 1%
是bat语法中第一个参数,就是上方的$BUILD_NUMBER
构建号,但传递过来可能后面带引号,例如2"
,所以要把"
也去掉%Ymd: =0%
意思是将其中的空格转为0,%param:"=%
意思是将其中的"
去掉- 需要先将
test.jar
改成用服务的方式运行,最好也要有优雅关闭,可以参考:spring boot不同版本的优雅关闭(graceful shutdown)和在windows下winsw服务方式运行的配置
npm项目部署
-
整体流程和maven一样,同样官方也有参考demo,Build a Node.js and React app with npm,这里只给出参考的文件:
-
Jenkinsfile
:pipeline { agent any parameters { choice choices: ['deploy', 'rollback'], description: '', name: 'action' string description: 'rollback ID (only number)', name: 'version' } stages { stage('Install') { when { expression { params.action == 'deploy' } } steps {//install bat 'npm install --registry https://registry.npm.taobao.org' } } stage("Build"){ when { expression { params.action == 'deploy' } } steps {//build bat 'npm run build' } } stage("Archive"){ when { expression { params.action == 'deploy' } } steps {//先打包成压缩文件,再归档,同时也方便传输 bat 'tar -zcvf dist.tar.gz -C dist .' archiveArtifacts artifacts: '*.tar.gz', followSymlinks: false, onlyIfSuccessful: true } } stage('rollback') { when { expression { params.action == 'rollback' } } steps {//将压缩文件拷贝到正常打包后的位置 bat 'copy /y "%JENKINS_HOME%\\jobs\\%JOB_NAME%\\builds\\%version%\\archive\\dist.tar.gz" dist.tar.gz' } } stage("Publish"){ steps { sshPublisher(publishers: [sshPublisherDesc(configName: '刚才设置的ssh的名称', sshRetry: [retries: 3, retryDelay: 5000], transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'cmd /c E:\\服务器B相关部署路径\\jenkins_deploy.bat $BUILD_NUMBER', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '服务器B相关部署路径', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'dist.tar.gz')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: true)]) } } } }
-
服务器B的
jenkins_deploy.bat
脚本,结构和maven项目差不多。-
项目结构:
abc: ├─backup │ └─test_2022_06_27_203000_3.tar.gz #在服务端也备份一份,格式:名称+日期+构建号+tar.gz └─prod ├─static └─index.html
-
jenkins_deploy.bat
脚本:@echo off ::设置utf8编码运行 chcp 65001 ::移动到当前目录 cd /d E:\abc ::设置时间变量,需要注意的是utf编码和ansi编码所取时间的位数不一样 set "Ymd=%date:~3,4%_%date:~8,2%_%date:~11,2%_%time:~0,2%%time:~3,2%%time:~6,2%" ::获取第一个参数 set "param=%1" ::将时间的空格用0补齐;将参数可能携带的引号去掉 set backup_name=%Ymd: =0%_%param:"=% ::将现有的打包后移到backup中 tar -zcvf test_%backup_name%.tar.gz -C prod . move test_%backup_name%.tar.gz backup\test_%backup_name%.tar.gz echo 'prod中内容打包到backup中' ::清空prod文件夹,然后将传递过来的解压到prod中 del /q prod\* for /d %%x in (prod\*) do @rd /s /q "%%x" tar -zxvf dist.tar.gz -C prod echo '最新的解压到prod中' ::删除传递过来的 del dist.tar.gz echo '删除传递过来的文件' @echo on
-
构建通知
- 目前插件可以通过钉钉和企业微信机器人通知,目前只测试了钉钉的,感觉还可以,目前我这边没有在pipeline中自定义,只是用了通用功能,更多功能可查看官方文档或自行搜索相关文章。
完工
- 以上就是整体流程,只是记录了下一些关键点。