想直接看在Linux下jenkins实现全自动化的流水线配置的可以直接看后面的
流水线实现
运行的视频效果可以看: http://lemon8.group/movie/softwareconf.mp4
流水线运行效果和企业微信群效果
目录
环境准备和软件安装
服务器选购怎么选购?
gitee webhook是什么怎么配?
为什么启动jar包要用到shell脚本?
jenkins在Linux环境下写流水线要注意的点(踩过的坑)
推送到企业微信shell脚本和实现是怎样的?
项目的主要流程是怎么样的?
流水线实现
1. git pull: 这个步骤从Gitee仓库拉取代码,确保Jenkins工作空间有最新的代码。
2. maven test: 在指定的目录下运行Maven测试,就是构建我们自己写的junit白盒测试,确保代码在构建之前没有明显的错误。
3. maven build: 首先输出Maven的版本,然后执行Maven的clean和package命令来清理之前的构建产物并打包应用程序。最后,将打包好的jar文件移动到指定的目录。
4. jar running: 在指定目录下运行jar包,设置环境变量以防止Jenkins节点被杀死,并运行Spring Boot脚本,查看Java进程。
5. vue running: 编译并打包Vue前端项目,将编译后的文件复制到Nginx服务器的HTML目录下。
6. nginx reload: 重新加载Nginx配置,使前端项目可以被访问。
7. setting env: 设置构建过程中的环境变量,如构建用户信息。
8. post: 定义了构建成功后和失败后要执行的脚本,这些脚本会发送构建状态通知。
全部代码
环境准备和软件安装
环境交代:Linux阿里云服务器centos7.9部署jenkins和全部项目
注:(因为gitee webhook触发时要公网的IP地址),所以要用到云服务器。
需要安装的软件:
Linux的centos7.9系统需要安装的软件分别为:jenkins最新版和jdk17,jre17(用于输入jps查看Java进程),这部分可以上jenkins官网上查看有引导性指令。这些都是可以用yum指令安装的。
https://www.jenkins.io/download/
如图可以根据系统选择下载
https://pkg.jenkins.io/redhat-stable/
选择Red Hat可以看到引导的下载指令
源码压缩包安装maven,注意!一定要wget URL 下载压缩包或者win下载传到centos,不能用yum指令安装,因为这样安装的maven会连带安装到其他版本的jdk,jenkins会启动不了!这里给个参考的博客关于压缩包安装maven的
https://blog.csdn.net/m0_52985087/article/details/136155283#:~:text=1.检查当前环境是否安装maven 2.下载maven,3.上传maven压缩包 4.解压maven包 5.移动到/usr/local目录下方便管理
除了jenkins,jdk,jre,maven之外还需要安装git,nginx,这部分可以yum安装,但是也是推荐压缩包安装。有太多教程,这里就不细说了。关于jenkins初始化那些也可以在网络上搜jenkins安装教程会有相应的内容。
除了这些剩下的就是根据你的项目自选,比如说你的项目用到的MySQL和Redis那可以安装一个docker配置一个端口映射数据卷那些的进行容器化管理。或者直接yum安装MySQL也可以。我是docker安装了MySQL因为方便管理卸载等等
服务器选购怎么选购?
阿里云针对大学生有6个月的免费服务器使用,具体信息参考这个博客
阿里云学生服务器免费用7个月(申请全流程)
https://developer.aliyun.com/article/1256821
要是想购买。可以选天翼云也可以选阿里云,我这里是阿里云99元包年,还买了域名,可以自行去阿里云或者天翼云官网看优惠政策,好像天翼云新用户特惠非常便宜。
天翼云的优惠政策
订购的阿里云套餐
gitee webhook是什么怎么配?
gitee的webhook是在gitee仓库里面配置用于在仓库有push或者其他事件发生时,配置gitee webhook可以让仓库向特定URL发送http或者https请求,比如说jenkins收到请求就可以拉取最新的代码自动构建。注意:webhook是要公网地址才可以推送的到!
gitee webhook不仅可以向jenkins发送请求触发jenkins指定项目构建,还可以推送到比如钉钉等等平台推送仓库的变更内容。
需要大概3个步骤:
1.jenkins下载插件 Gitee Plugin
This plugin allows Gitee to trigger builds in Jenkins when code is committed or pull requests are opened/updated. It can also send build status back to Gitee.
因为版本不同,如果装了gitee插件新建项目的时候构建触发器里面没有gitee webhook触发构建,那就装个Generic Webhook Trigger,或者自行搜索教程,关键词:jenkins webhook
2.无论是流水线还是自由风格的项目,在配置项目里面有个构建触发器, 安装了插件之后我们勾选Gitee webhook触发构建,这里可以看到我的提示
Gitee webhook 中填写 URL: http://i.lemon8.group:10086/gitee-project/Microservice-Shop
这里的URL就是gitee webhook 要填的地址
然后我们还要选定触发的分支以及生成webhook的密码
允许触发构建的分支,这里我是根据分支名过滤,仅仅包括 devops的分支,也就是只用devops分支提交了推送才会触发
生产webhook密码是我们点击下面的生成按钮就有密码了
3 gitee仓库配置webhook
在上一步我们拿到了webhook的URL和密码,下面我们到我们的仓库去配置
这里的URL就是上一步在jenkins里面的,密码也是,还可以勾选事件
为什么启动jar包要用到shell脚本?
因为我们在nohup java -jar 启动jar包时,这个时候如果有占用相同端口的进程就会启动不成功,这时我们写shell脚本就是先判断是否有占用相同端口的进程或者之前运行的旧程序占用相同端口,如果有先kill掉,再创建或者清空对应log日志,再后台运行jar包并输出到指定目录。
下面是我的shell脚本
注意这里的services里面的jar包名和日志名要替换成自己项目的
我这里的逻辑是先检查并终止正在运行的服务进程再启动服务,并将输出重定向到对应日志文件,看注释也能看懂意思
#!/bin/bash
# 定义微服务和其对应的jar文件名、日志文件名
services=(
"springboot.jar:springboot.log"
)
for service in "${services[@]}"; do
# 解析服务名、jar文件名、日志文件名
IFS=':' read -r jarName logName <<< "$service"
# 创建或覆盖日志文件
> "$logName"
# 检查并终止正在运行的服务进程
PID=$(pgrep -f "$jarName")
if [ -n "$PID" ]; then
echo "Stopping $jarName..."
kill -9 "$PID"
echo "$jarName stopped."
fi
# 启动服务,并将输出重定向到对应日志文件
echo "Starting $jarName..."
BUILD_ID=dontKillMe
nohup java -jar $jarName &>$logName &
echo "nohup java -jar $jarName &>$logName &"
echo "$jarName started. Logs in $logName."
done
echo "All services have been processed."
jenkins在Linux环境下写流水线要注意的点(踩过的坑)
1.在特定目录下运行多个指令要这么写,
比如我要在/var/lib/jenkins/workspace/electric_vehicle/springboot里面去执行mvn test运行我们写的junit白盒测试代码,就需要像下面这样
因为我们需要在同一个目录进行多个Linux指令的操作,所以这样子写最好
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot')
stage('maven test') {
steps {
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot'){
sh "mvn test"
}
}
}
2.要想jar包在流水线执行结束之后不被杀死要这样写
一开始jenkins在流水线结束之后杀死运行的Java进程,搜了好久这样子配置流水线配置结束之后才不会被杀死
withEnv(['JENKINS_NODE_COOKIE=dontkillme'])
stage('jar running') {
steps {
withEnv(['JENKINS_NODE_COOKIE=dontkillme']){
dir('/develop/jar'){
echo '运行jar包'
sh "ls -l"
echo '运行springboot脚本'
sh "chmod 777 springboot.sh"
sh "nohup ./springboot.sh"
echo '查看java进程'
sh "jps"
}
}
}
}
推送到企业微信shell脚本和实现是怎样的?
1.首先我们企业微信要创建一个群机器人,然后点击机器人的详情
我这里的群是自己创的课程小组的群,注意人数要大于等于3人
是自己创建的团队,因为好像在广州南方学院那里看不到群机器人选项
所以就新建了一个团队再新建一个群拉人
创建完这里就可以看到提示了,在详情里也可以看到完整的地址,
要复制?key= 这后面的值
2.创建一个发送到企业微信的shell脚本
注意!!!!下方的CHAT_WEBHOOK_KEY 要替换成自己的群机器人key值 就是上一部的webhook链接的key= 后面的值
#!/bin/sh
#替换成自己的群机器人key值
CHAT_WEBHOOK_KEY="cc275577-5b23-418d-8d93-afd8d1311d74"
CHAT_CONTENT_TYPE='Content-Type: application/json'
#-o代表或的意思,成功或失败 CHAT_WEBHOOK_URL都是一样的WEBHOOK url
if [ _"${TYPE}" = _"success" -o _"${TYPE}" = _"failure" ]; then
CHAT_WEBHOOK_URL='https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key'
fi
if [ _"${CHAT_WEBHOOK_KEY}" = _"" ]; then
echo "please make sure CHAT_WEBHOOK_KEY has been exported as environment variable"
fi
echo "## send message for : ${TYPE}"
if [ _"${TYPE}" = _"success" ]; then
curl "${CHAT_WEBHOOK_URL}=${CHAT_WEBHOOK_KEY}" \
-H "${CHAT_CONTENT_TYPE}" \
-d '
{
"msgtype": "markdown",
"markdown": {
"content": "<font color=\"warning\">**Jenkins任务通知**</font> \n
>构建人:<font color=\"comment\">'"${BUILD_USER}"'</font>
>构建编号:<font color=\"comment\">'"${BUILD_NUM}"'</font>
>构建时间:<font color=\"comment\">'"${BUILD_TIME}"'</font>
>任务名称:<font color=\"comment\">'"${JOB_NAME}"'</font>
>任务地址:<font color=\"comment\">[点击查看]('"${URL_JOB}"')</font>
>构建日志:<font color=\"comment\">[点击查看]('"${URL_LOG}"')</font>
>构建状态:<font color=\"info\">**Success**</font> \n
>任务已构建完成请确认: <@'"${JOB_TIPS1}"'>"
}
}
'
elif [ _"${TYPE}" = _"failure" ]; then
curl "${CHAT_WEBHOOK_URL}=${CHAT_WEBHOOK_KEY}" \
-H "${CHAT_CONTENT_TYPE}" \
-d '
{
"msgtype": "markdown",
"markdown": {
"content": "<font color=\"warning\">**Jenkins任务通知**</font> \n
>构建人:<font color=\"comment\">'"${BUILD_USER}"'</font>
>构建编号:<font color=\"comment\">'"${BUILD_NUM}"'</font>
>构建时间:<font color=\"comment\">'"${BUILD_TIME}"'</font>
>任务名称:<font color=\"comment\">'"${JOB_NAME}"'</font>
>任务地址:<font color=\"comment\">[点击查看]('"${URL_JOB}"')</font>
>构建日志:<font color=\"comment\">[点击查看]('"${URL_LOG}"')</font>
>构建状态:<font color=\"comment\">**Failure**</font>
>项目构建失败了!!!!!!!!!!!!!!!请确认:<@'"${JOB_TIPS1}"'>"
}
}
'
fi
- 流水线要配置
这里的配置就是不管构建成功还是构建失败都要发送短信
stage('setting env') 是设置username和usernameid,我们都可以自定义设置,
前提是我们要安装这个插件
然后post {
success { 和failure {
就像if else 一样,成功了会走成功的分支,失败了会走失败的分支TYPE=success或者TYPE=failure发送不同的内容,仔细看再结合上面发送短信的脚本就能看懂了
注意!!,这个脚本要注意存放位置我是放在项目目录下所以就sh send_message-export.sh',要是不同位置要变化
stage('setting env') {
options {
skipDefaultCheckout(true)
}
steps {
// 使用wrap和BuildUser插件捕获构建用户信息
wrap([$class: 'BuildUser']) {
script {
env.BUILD_USERNAME = ""
env.BUILD_USERNAMEID = ""
}
}
}
}
}
post {
success {
script {
sh 'export TYPE=success;export JOB_NAME="${JOB_BASE_NAME}";export BUILD_NUM="$BUILD_NUMBER";export BUILD_TIME="$BUILD_TIMESTAMP";export BUILD_USER="${BUILD_USERNAME}"; export URL_JOB="${BUILD_URL}";export URL_LOG="${BUILD_URL}console";export JOB_TIPS1="${BUILD_USERNAMEID}" ;sh send_message-export.sh'
}
}
failure {
script {
sh 'export TYPE=failure;export JOB_NAME="${JOB_BASE_NAME}";export BUILD_NUM="$BUILD_NUMBER";export BUILD_TIME="$BUILD_TIMESTAMP"; export BUILD_USER="${BUILD_USERNAME}"; export URL_JOB="${BUILD_URL}";export URL_LOG="${BUILD_URL}console";export JOB_TIPS1="${BUILD_USERNAMEID}" ;sh send_message-export.sh'
}
}
项目的主要流程是怎么样的?
我的流水线主要分为8个阶段
以下是流水线的主要步骤和它们的作用:
1. git pull: 这个步骤从Gitee仓库拉取代码,确保Jenkins工作空间有最新的代码。
2. maven test: 在指定的目录下运行Maven测试,就是构建我们自己写的junit白盒测试,确保代码在构建之前没有明显的错误。
3. maven build: 首先输出Maven的版本,然后执行Maven的clean和package命令来清理之前的构建产物并打包应用程序。最后,将打包好的jar文件移动到指定的目录。
4. jar running: 在指定目录下运行jar包,设置环境变量以防止Jenkins节点被杀死,并运行Spring Boot脚本,查看Java进程。
5. vue running: 编译并打包Vue前端项目,将编译后的文件复制到Nginx服务器的HTML目录下。
6. nginx reload: 重新加载Nginx配置,使前端项目可以被访问。
7. setting env: 设置构建过程中的环境变量,如构建用户信息。
8. post: 定义了构建成功后和失败后要执行的脚本,这些脚本会发送构建状态通知。
流水线实现
下面我会说一下这八个阶段具体该怎么手把手写流水线
注!!!要根据自己的项目结构去调整目录,比如shell脚本的目录,
pom.xml文件下的目录,前端vue项目下的目录,这样指令才能成功
还有就是要会配nginx的配置文件,这样子才能实现反向代理和请求分发才能看的出效果
这是项目的主要配置
pipeline {
agent any
options {
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5'))
}
}
1. git pull: 这个步骤从Gitee仓库拉取代码,确保Jenkins工作空间有最新的代码。
stages {
stage('git pull') {
steps {
git branch: 'devops', credentialsId: 'cd801976-1e4c-4331-95cd-5fdd8adf38a6', url: 'https://gitee.com/wsx8888/electric_vehicle.git'
echo 'gitee拉取代码成功'
}
}
}
不知道怎么写的可以这样
我们填好仓库的URL和要拉取的分支和凭据,点击生成就可以生成了,在steps里面粘贴就可以了
2. maven test: 在指定的目录下运行Maven测试,就是构建我们自己写的junit白盒测试,确保代码在构建之前没有明显的错误。
这一步很简单,就是要根据自己的项目目录,要进入到springboot目录下,在有
pom.xml的目录下执行mvntest指令,运行自己写的单元测试
stage('maven test') {
steps {
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot'){
sh "mvn test"
}
}
}
3. maven build: 首先输出Maven的版本,然后执行Maven的clean和package命令来清理之前的构建产物并打包应用程序。最后,将打包好的jar文件移动到指定的目录。
这一步也比较简单都是maven的指令
mv指令是把jar包移动到shell脚本的目录下
stage('maven build') {
steps {
sh "mvn --version"
echo '输出maven版本成功'
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot'){
sh "mvn clean"
sh "mvn package"
}
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot/target'){
sh "mv springboot.jar /develop/jar"
}
}
}
4. jar running: 在指定目录下运行jar包,设置环境变量以防止Jenkins节点被杀死,并运行Spring Boot脚本,查看Java进程。
这一步就是运行shell脚本
注意如同上面说的withEnv(['JENKINS_NODE_COOKIE=dontkillme'])是必须要带的
然后jps查看Java进程
stage('jar running') {
steps {
withEnv(['JENKINS_NODE_COOKIE=dontkillme']){
dir('/develop/jar'){
echo '运行jar包'
sh "ls -l"
echo '运行springboot脚本'
sh "chmod 777 springboot.sh"
sh "nohup ./springboot.sh"
echo '查看java进程'
sh "jps"
}
}
}
}
5. vue running: 编译并打包Vue前端项目,将编译后的文件复制到Nginx服务器的HTML目录下。
这一步主要是npm的指令去打包我们前端的项目,所以要在vue项目的目录下运行
第二步就是把dist文件夹的全部内容移动到nginx的html目录下面
stage('vue running') {
steps {
echo '编译打包'
dir('/var/lib/jenkins/workspace/electric_vehicle/vue'){
sh "chmod -R +x node_modules"
sh "npm install"
sh "npm run build"
sh "ls"
sh "cp -rf dist/* /usr/local/nginx/html/nanyuan"
}
}
}
6. nginx reload: 重新加载Nginx配置,使前端项目可以被访问。
因为我们更新了nginx的html目录文件所以要进入到nginx目录进行重启
这里的nginx目录要根据自己的安装地址进行改变
stage('nginx reload') {
steps {
echo '重启nginx'
dir('/usr/local/nginx/sbin'){
sh "./nginx -s reload"
}
}
}
7. setting env: 设置构建过程中的环境变量,如构建用户信息。
前提是我们要安装这个插件
这里的BUILD_USERNAME和BUILD_USERNAMEID就可以配置我们的id和名字
stage('setting env') {
options {
skipDefaultCheckout(true)
}
steps {
// 使用wrap和BuildUser插件捕获构建用户信息
wrap([$class: 'BuildUser']) {
script {
env.BUILD_USERNAME = ""
env.BUILD_USERNAMEID = ""
}
}
}
}
8. post: 定义了构建成功后和失败后要执行的脚本,这些脚本会发送构建状态通知。
参考上面的推送到企业微信shell脚本和实现是怎样的?
注意这个是插入到pipeline大括号结束的,也就是pipeline{的下方
以及这里运行的脚本要注意位置
post {
success {
script {
sh 'export TYPE=success;export JOB_NAME="${JOB_BASE_NAME}";export BUILD_NUM="$BUILD_NUMBER";export BUILD_TIME="$BUILD_TIMESTAMP";export BUILD_USER="${BUILD_USERNAME}"; export URL_JOB="${BUILD_URL}";export URL_LOG="${BUILD_URL}console";export JOB_TIPS1="${BUILD_USERNAMEID}" ;sh send_message-export.sh'
}
}
failure {
script {
sh 'export TYPE=failure;export JOB_NAME="${JOB_BASE_NAME}";export BUILD_NUM="$BUILD_NUMBER";export BUILD_TIME="$BUILD_TIMESTAMP"; export BUILD_USER="${BUILD_USERNAME}"; export URL_JOB="${BUILD_URL}";export URL_LOG="${BUILD_URL}console";export JOB_TIPS1="${BUILD_USERNAMEID}" ;sh send_message-export.sh'
}
}
}
全部代码
pipeline {
agent any
options {
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5'))
}
stages {
stage('git pull') {
steps {
git branch: 'devops', credentialsId: 'cd801976-1e4c-4331-95cd-5fdd8adf38a6', url: 'https://gitee.com/wsx8888/electric_vehicle.git'
echo 'gitee拉取代码成功'
}
}
stage('maven test') {
steps {
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot'){
sh "mvn test"
}
}
}
stage('maven build') {
steps {
sh "mvn --version"
echo '输出maven版本成功'
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot'){
sh "mvn clean"
sh "mvn package"
}
dir('/var/lib/jenkins/workspace/electric_vehicle/springboot/target'){
sh "mv springboot.jar /develop/jar"
}
}
}
stage('jar running') {
steps {
withEnv(['JENKINS_NODE_COOKIE=dontkillme']){
dir('/develop/jar'){
echo '运行jar包'
sh "ls -l"
echo '运行springboot脚本'
sh "chmod 777 springboot.sh"
sh "nohup ./springboot.sh"
echo '查看java进程'
sh "jps"
}
}
}
}
stage('vue running') {
steps {
echo '编译打包'
dir('/var/lib/jenkins/workspace/electric_vehicle/vue'){
sh "chmod -R +x node_modules"
sh "npm install"
sh "npm run build"
sh "ls"
sh "cp -rf dist/* /usr/local/nginx/html/nanyuan"
}
}
}
stage('nginx reload') {
steps {
echo '重启nginx'
dir('/usr/local/nginx/sbin'){
sh "./nginx -s reload"
}
}
}
stage('setting env') {
options {
skipDefaultCheckout(true)
}
steps {
// 使用wrap和BuildUser插件捕获构建用户信息
wrap([$class: 'BuildUser']) {
script {
env.BUILD_USERNAME = ""
env.BUILD_USERNAMEID = ""
}
}
}
}
}
post {
success {
script {
sh 'export TYPE=success;export JOB_NAME="${JOB_BASE_NAME}";export BUILD_NUM="$BUILD_NUMBER";export BUILD_TIME="$BUILD_TIMESTAMP";export BUILD_USER="${BUILD_USERNAME}"; export URL_JOB="${BUILD_URL}";export URL_LOG="${BUILD_URL}console";export JOB_TIPS1="${BUILD_USERNAMEID}" ;sh send_message-export.sh'
}
}
failure {
script {
sh 'export TYPE=failure;export JOB_NAME="${JOB_BASE_NAME}";export BUILD_NUM="$BUILD_NUMBER";export BUILD_TIME="$BUILD_TIMESTAMP"; export BUILD_USER="${BUILD_USERNAME}"; export URL_JOB="${BUILD_URL}";export URL_LOG="${BUILD_URL}console";export JOB_TIPS1="${BUILD_USERNAMEID}" ;sh send_message-export.sh'
}
}
}
}