百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

基于Jenkins Pipeline构建企业级CI/CD

off999 2025-03-03 19:49 19 浏览 0 评论

案例介绍

本案例通过若依项目作为案例,通过Jenkins构建企业级CI/CD平台。

若依服务列表:

  • ruoyi-auth

  • ruoyi-system

  • ruoyi-gateway

  • ruoyi-ui

若依环境列表:

  • DEV

  • UAT

  • PROD

环境准备工作:

  • nacos安装并配置完成

  • MySQL部署完成并初始化

  • Redis部署完成

  • Harbor镜像仓库

  • Gitlab部署完成

  • Kubernetes部署完成

  • Ingress部署完成

设计思路

触发构建设计:

本设计通过Jenkins Generic Webhook Trigger 插件实现了基于Webhook自动触发流水线构建。

image

流程说明:

  • 研发项目负责人代码开发完成后进行合并代码并生成Tag

  • Gitlab通过Webhook自动触发Jenkins Pipeline构建

流水线设计:

image

Jenkins流水线完整图:

image

自定义基础镜像

在实际企业环境中,基础镜像都会根据具体得需求定义适合自己得基础镜像。

定义Maven镜像:

用于代码构建编译打包,会把Ruoyi相关依赖包打到基础镜像内,避免分层构建失败。

# 拉取源代码并切换分支
$ https://gitee.com/y_project/RuoYi-Cloud.git
$ git checkout v3.6.3
$ cd ..

#
定义Dockerfile
$ cat Dockerfile
FROM maven:3.8.6-openjdk-8
ADD RuoYi-Cloud /opt/RuoYi-Cloud
RUN cd /opt/RuoYi-Cloud && mvn clean install -DskipTests
RUN rm -rf /opt/RuoYi-Cloud

#
构建镜像
$ docker build uhub.service.ucloud.cn/kubesre/maven:jdk8 .
$ docker push uhub.service.ucloud.cn/kubesre/maven:jdk8

定义Java基础镜像:

根据需求定义适合自己的基础镜像。通过变量传递让配置变得更灵活!

# 创建个目录
$ mkdir base && cd base

#
创建启动脚本
$ cat docker-entrypoint.sh
#!/bin/sh
java -server -Xms$JVM_XMS -Xmx$JVM_XMX -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/data/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/heapdump.hprof -jar app.jar --server.port=$SERVICE_PORT --spring.profiles.active=$PROFILES_ACTIVE --spring.cloud.nacos.config.server-addr=$NACOS_ADDRESS --spring.cloud.nacos.config.namespace=$NACOS_NAMESPACE_ID --spring.cloud.nacos.config.username=$NACOS_USERNAME --spring.cloud.nacos.config.password=$NACOS_PASSWORD --spring.cloud.nacos.discovery.server-addr=$NACOS_ADDRESS --spring.cloud.nacos.discovery.namespace=$NACOS_NAMESPACE_ID --spring.cloud.nacos.discovery.username=$NACOS_USERNAME --spring.cloud.nacos.discovery.password=$NACOS_PASSWORD

#
创建down-nacos脚本
cat down-nacos.sh
#!/bin/sh
ipTrue=false
java_service_ip=""
code=false

getPodIp() {
java_service_ip=`ip a | grep inet | grep -v inet6 | grep -v '127.0.0.1' | awk '{print $2}' | awk -F / '{print$1}'`
grep -w "${java_service_ip}" /etc/hosts > /dev/
if [ $? -eq 0 ];then
echo "get java service ip success"
ipTrue=true
else
echo "get java service ip failed"
fi
}

downService(){
accessToken=`curl -s -X POST http://$NACOS_ADDRESS/nacos/v1/auth/users/login --form username=$NACOS_USERNAME --form password=$NACOS_PASSWORD|jq -r .accessToken`
curl -s -X PUT "$NACOS_ADDRESS/nacos/v1/ns/instance?language=zh-CN&accessToken=$accessToken&username=$NACOS_USERNAME&serviceName=$JAVA_SERVICE_NAME&ip=$java_service_ip&port=$SERVICE_PORT&enabled=false&namespaceId=$NACOS_NAMESPACE_ID"
if [ "$code" = "ok" ];then
echo "java service down from nacos success"
code=true
else
echo "java service down from nacos failed"
fi
}

start(){
getPodIp
if $ipTrue;then
downService
sleep 30
else
echo "down $JAVA_SERVICE_NAME failed" >> down_service.log
fi
}
start


#
定义Dockerfile
$ cat Dockerfile
FROM openjdk:8-jre
WORKDIR /data
COPY ./down-nacos.sh .
COPY ./docker-entrypoint.sh .
RUN chmod +x docker-entrypoint.sh && chmod +x down-nacos.sh
ENTRYPOINT ["./docker-entrypoint.sh"]

#
构建镜像
$ docker build uhub.service.ucloud.cn/kubesre/java-base:v8 .
$ docker push uhub.service.ucloud.cn/kubesre/java-base:v8

变量说明:

  • JVM_XMS:最小JVM堆栈内存

  • JVM_XMX:最大JVM堆栈内存

  • SERVICE_PORT:应用服务端口

  • NACOS_ADDRESS:Nacos地址

  • NACOS_USERNAME:Nacos用户名

  • NACOS_PASSWORD:Nacos密码

  • NACOS_NAMESPACE_ID:Nacos命名空间ID

  • PROFILES_ACTIVE:环境名称

Dockerfile编写

分层构建好处:

  • 不依赖本地环境

  • 减小容器镜像大小

Java Dockerfile(分层构建):

FROM uhub.service.ucloud.cn/kubesre/maven:jdk8 AS build
COPY src /opt/src/
COPY pom.xml /opt/
RUN cd /opt/ && mvn clean install -DskipTests

FROM uhub.service.ucloud.cn/kubesre/java-base:v8
# 复制jar文件到路径
COPY --from=build /opt/target/*.jar /data/app.jar

Vue Dockerfile(分层构建):

FROM node:16 AS builder

#
设置工作目录
WORKDIR /usr/src/app

#
将项目文件复制到 Docker 镜像中
COPY . .

#
安装项目依赖
RUN npm install --registry=https://registry.npmmirror.com

#
构建静态文件
RUN npm run build:prod

#
使用 Nginx 镜像作为基础镜像,用于托管静态文件
FROM nginx:stable-alpine

WORKDIR /home/ruoyi/projects/ruoyi-ui
# 将 VuePress 构建的静态文件复制到 Nginx 的网站目录
COPY --from=builder /usr/src/app/dist /home/ruoyi/projects/ruoyi-ui
COPY ./nginx/conf/nginx.conf /etc/nginx/nginx.conf

#
暴露 80 端口
EXPOSE 80

#
启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

Pipeline编写

如下所有Pipeline文件,需要自行修改内容:

  • credentialsId

  • robot

  • 镜像仓库地址

如何查找credentialsId:

image

如何查找robot:

image

Java Pipeline:

pipeline {
agent any
triggers {
GenericTrigger(
genericVariables: [
[key: 'ref', value: '$.ref'], //获取分支
[key: 'user_username', value: '$.user_username'], //获取自动构建用户名
[key: 'GitRepository', value: '$.project.git_http_url'], //获取gitlab ssh项目地址
[key: 'project', value: '$.project.name'], //获取项目名称
[key: 'repository', value: '$.repository.name'],
],
token: "$JOB_NAME",
causeString: 'Triggered on $branch',
printContributedVariables: true,
printPostContent: true,
silentResponse: false,
)
}
environment {
// pipeline配置路径
pipeline_dir="/var/lib/jenkins/workspace/pipeline"
// 项目版本
Tag=sh(script: 'echo "${ref}" | awk -F"/" \'{print $3}\'', returnStdout: true).trim()
// 项目名称
Project_Name="${project}"
// 上一次版本
Revsion_Prod=''
//Depolyment名称
DeploymentName=''
// 生产名称空间
Namespace_Prod=''
// 灰度模式
GrayHeaderMode=''
// 灰度Depolyment名称
GrayDeploymentName=''
// 灰度Service名称
GrayServiceName=''
// 灰度Ingress名称
GrayIngressName=''
// 是否灰度
GrayEnable='yes'
}
options {
// 表示保留10次构建历史
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Pull Code') {
// 拉取代码
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "$ref"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
userRemoteConfigs: [[
credentialsId: 'ac66550d-6999-485c-af3a-7e6189f765f0',
url: "$GitRepository"
]]
])

script{
currentBuild.displayName = "#${BUILD_NUMBER} - ${Project_Name} - ${Tag}"
}
}
}
// // 代码构建
// stage('Build Code') {
// steps {
// sh '/application/maven/bin/mvn -f pom.xml -s settings.xml clean package -DskipTests'
// }
// }
// 镜像构建
stage('Build Image') {
steps {
sh '''
/usr/bin/docker build -t uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag .
/usr/bin/docker push uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag
'
''
}
post {
success {
wrap([$class: 'BuildUser']) {
lark (
robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
type: "CARD",
title: "?? Jenkins 镜像构建成功",
text: [
"?? **任务名称**:[${JOB_NAME}](${JOB_URL})",
"?? **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"?? **构建状态**: 成功",
"?? **镜像版本**: $Tag",
"?? **镜像仓库**: uhub.service.ucloud.cn/kubesre/$Project_Name",
"?? **构建用时**: ${currentBuild.duration} ms",
"?? **执 行 者**: ${env.BUILD_USER}",
""
],
buttons: [
[
title: "更改记录",
url: "${BUILD_URL}changes"
],
[
title: "控制台",
type: "danger",
url: "${BUILD_URL}console"
]
]
)}
}
}
}
stage('DeployDev'){
steps {
echo "部署开发环境"
script {
def userInput = input (
message: '确定要发布到DEV环境吗?',
parameters:[
choice(name: '操作', choices: ['发布', '跳过'])
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '发布'){
echo "部署Dev环境开始"
sh '''
echo $pipeline_dir
echo "打印编排文件详细信息"
if [ -e "$pipeline_dir/dev/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/dev/$Project_Name/service.yml" ]; then
cat $pipeline_dir/dev/$Project_Name/service.yml
cat $pipeline_dir/dev/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/dev/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/dev/$Project_Name/ingress.yml
cat $pipeline_dir/dev/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'
''
} else {
echo "不发布"
}
}
}
}
stage('DeployUat'){
steps {
echo "部署测试环境"
script {
def userInput = input (
message: '确定要发布到UAT环境吗?',
parameters:[
choice(name: '操作', choices: ['发布', '跳过'])
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '发布'){
echo "发布"
sh '''
echo $pipeline_dir
echo "打印编排文件详细信息"

if [ -e "$pipeline_dir/uat/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/uat/$Project_Name/service.yml" ]; then
cat $pipeline_dir/uat/$Project_Name/service.yml
cat $pipeline_dir/uat/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/uat/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/uat/$Project_Name/ingress.yml
cat $pipeline_dir/uat/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'
''
} else {
echo "不发布"
}
}
}
}
stage('DeployGray'){
steps {
echo "部署灰度环境"
script {
def GraysMode = input (
message: '确定要灰度验证吗?',
parameters:[
choice(name: 'operation', choices: ['基于权重灰度','基于请求头灰度','跳过'])
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
if (GraysMode['operation'] == '基于权重灰度'){
def WeightMode = input (
message: '请输入权重比例!',
parameters:[
string(name: 'workload_weight',defaultValue: '',description: ''),
string(name: 'grayload_weight',defaultValue: '',description: '')
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
sh """
echo $pipeline_dir
echo "
打印编排文件详细信息"
if [ -e "
$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "
s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "
s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

echo "
配置权重"

echo ${WeightMode['grayload_weight']}
if [ -e "
$pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed "
s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g"
cat $pipeline_dir/prod/$Project_Name/ingress-gray-weight.yml | sed "
s/WEIGHT-VALUE/${WeightMode['grayload_weight']}/g" | /usr/bin/kubectl apply -f -
fi
"
""
}
if (GraysMode['operation'] == '基于请求头灰度'){
GrayHeaderMode = input (
message: '请输入请求头!',
parameters:[
string(name: 'header_key',defaultValue: '',description: ''),
string(name: 'header_value',defaultValue: '',description: '')
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)

sh """
echo ${GrayHeaderMode['header_value']}
echo $pipeline_dir
echo "
打印编排文件详细信息"

if [ -e "
$pipeline_dir/prod/$Project_Name/deployment-gray.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "
s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | sed "
s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

echo "
配置请求头"
echo ${GrayHeaderMode['header_key']}
echo ${GrayHeaderMode['header_value']}

if [ -e "
$pipeline_dir/prod/$Project_Name/ingress-gray-header.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed "
s/header-key/${GrayHeaderMode['header_key']}/g" | sed "s/header-value/${GrayHeaderMode['header_value']}/g"
cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | sed "
s/header-value/${GrayHeaderMode['header_key']}/g" | sed "s/header-value/${GrayHeaderMode['header_value']}/g" | /usr/bin/kubectl apply -f -
fi
"
""
}
// 默认模式为yes,如果跳过为no
if (GraysMode['operation'] == '跳过'){
GrayEnable='no'
}
}
}
}
stage('DeployProd'){
steps {
echo "部署生产环境"
script {
def userInput = input (
message: '确定要发布到生产环境吗?',
parameters:[
choice(name: '操作', choices: ['发布', '跳过'])
],
ok: '确定',
submitter: 'test',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '发布'){
echo "发布"
Namespace_Prod = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'", returnStdout: true).trim()
DeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
Revsion_Prod = sh(script: "kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsonpath='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'", returnStdout: true).trim()
GrayDeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
GrayServiceName = sh(script: "cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
GrayIngressName = sh(script: "cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()

sh '''
echo $pipeline_dir
echo "开始部署生产环境"
echo "打印编排文件详细信息"

if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/service.yml
cat $pipeline_dir/prod/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress.yml
cat $pipeline_dir/prod/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'
''
if (GrayEnable == 'yes'){
sh """
kubectl delete deployment ${GrayDeploymentName} -n ${Namespace_Prod}
kubectl delete service ${GrayServiceName} -n ${Namespace_Prod}
kubectl delete ingress ${GrayIngressName} -n ${Namespace_Prod}
"
""
}

} else {
echo "不发布"
}
}
}
post {
success {
wrap([$class: 'BuildUser']) {
lark (
robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
type: "CARD",
title: "?? Jenkins 应用发布成功",
text: [
"?? **应用名称**:[${JOB_NAME}](${JOB_URL})",
"?? **应用环境**:Prod",
"?? **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"?? **发布状态**: 成功",
"?? **镜像版本**: $Tag",
"?? **镜像仓库**: harbor.kubesre.com:8443/kubesre/$Project_Name",
"?? **执 行 者**: ${env.BUILD_USER}",
""
],
buttons: [
[
title: "更改记录",
url: "${BUILD_URL}changes"
],
[
title: "控制台",
type: "danger",
url: "${BUILD_URL}console"
]
]
)}
}
}
}

stage('RollBack'){
steps {
echo "版本回滚操作"
script {
def RollBack = input (
message: '确定要执行回滚操作吗?',
parameters:[
choice(name: '是否回滚', choices: ['是', '否']),
string(name: '回滚版本', defaultValue: Revsion_Prod,description: '默认上一个版本')
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
if (RollBack['是否回滚'] == '是'){
echo "版本回滚成功"
echo RollBack['回滚版本']
sh """
if [ -e "
$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "
s/TAG/${RollBack['回滚版本']}/g"
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "
s/TAG/${RollBack['回滚版本']}/g" | /usr/bin/kubectl apply -f -
fi
"
""
} else {
echo "放弃版本回滚"
echo RollBack['回滚版本']
}
}
}
}
}
}

Vue Pipeline:

pipeline {
agent any
triggers {
GenericTrigger(
genericVariables: [
[key: 'ref', value: '$.ref'], //获取分支
[key: 'user_username', value: '$.user_username'], //获取自动构建用户名
[key: 'GitRepository', value: '$.project.git_http_url'], //获取gitlab ssh项目地址
[key: 'project', value: '$.project.name'], //获取项目名称
[key: 'repository', value: '$.repository.name'],
],
token: "$JOB_NAME",
causeString: 'Triggered on $branch',
printContributedVariables: true,
printPostContent: true,
silentResponse: false,
)
}
environment {
// pipeline配置路径
pipeline_dir="/var/lib/jenkins/workspace/pipeline"
// 项目版本
Tag=sh(script: 'echo "${ref}" | awk -F"/" \'{print $3}\'', returnStdout: true).trim()
// 项目名称
Project_Name="${project}"
// 上一次版本
Revsion_Prod=''
//Depolyment名称
DeploymentName=''
// 生产名称空间
Namespace_Prod=''
// 灰度模式
GrayHeaderMode=''
// 灰度Depolyment名称
GrayDeploymentName=''
// 灰度Service名称
GrayServiceName=''
// 灰度Ingress名称
GrayIngressName=''
// 是否灰度
GrayEnable='yes'
}
options {
// 表示保留10次构建历史
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Pull Code') {
// 拉取代码
steps {
checkout([
$class: 'GitSCM',
branches: [[name: "$ref"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
userRemoteConfigs: [[
credentialsId: 'ac66550d-6999-485c-af3a-7e6189f765f0',
url: "$GitRepository"
]]
])

script{
currentBuild.displayName = "#${BUILD_NUMBER} - ${Project_Name} - ${Tag}"
}
}
}
// // 代码构建
// stage('Build Code') {
// steps {
// sh '/application/maven/bin/mvn -f pom.xml -s settings.xml clean package -DskipTests'
// }
// }
// 镜像构建
stage('Build Image') {
steps {
sh '''
/usr/bin/docker build -t uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag .
/usr/bin/docker push uhub.service.ucloud.cn/kubesre/$Project_Name:$Tag
'
''
}
post {
success {
wrap([$class: 'BuildUser']) {
lark (
robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
type: "CARD",
title: "?? Jenkins 镜像构建成功",
text: [
"?? **任务名称**:[${JOB_NAME}](${JOB_URL})",
"?? **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"?? **构建状态**: 成功",
"?? **镜像版本**: $Tag",
"?? **镜像仓库**: uhub.service.ucloud.cn/kubesre/$Project_Name",
"?? **构建用时**: ${currentBuild.duration} ms",
"?? **执 行 者**: ${env.BUILD_USER}",
""
],
buttons: [
[
title: "更改记录",
url: "${BUILD_URL}changes"
],
[
title: "控制台",
type: "danger",
url: "${BUILD_URL}console"
]
]
)}
}
}
}
stage('DeployDev'){
steps {
echo "部署开发环境"
script {
def userInput = input (
message: '确定要发布到DEV环境吗?',
parameters:[
choice(name: '操作', choices: ['发布', '跳过'])
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '发布'){
echo "部署Dev环境开始"
sh '''
echo $pipeline_dir
echo "打印编排文件详细信息"
if [ -e "$pipeline_dir/dev/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/dev/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/dev/$Project_Name/service.yml" ]; then
cat $pipeline_dir/dev/$Project_Name/service.yml
cat $pipeline_dir/dev/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/dev/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/dev/$Project_Name/ingress.yml
cat $pipeline_dir/dev/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'
''
} else {
echo "不发布"
}
}
}
}
stage('DeployUat'){
steps {
echo "部署测试环境"
script {
def userInput = input (
message: '确定要发布到UAT环境吗?',
parameters:[
choice(name: '操作', choices: ['发布', '跳过'])
],
ok: '确定',
submitter: 'admin',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '发布'){
echo "发布"
sh '''
echo $pipeline_dir
echo "打印编排文件详细信息"

if [ -e "$pipeline_dir/uat/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/uat/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/uat/$Project_Name/service.yml" ]; then
cat $pipeline_dir/uat/$Project_Name/service.yml
cat $pipeline_dir/uat/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/uat/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/uat/$Project_Name/ingress.yml
cat $pipeline_dir/uat/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'
''
} else {
echo "不发布"
}
}
}
}
stage('DeployProd'){
steps {
echo "部署生产环境"
script {
def userInput = input (
message: '确定要发布到生产环境吗?',
parameters:[
choice(name: '操作', choices: ['发布', '跳过'])
],
ok: '确定',
submitter: 'test',
submitterParameter: 'APPROVER'
)
if (userInput['操作'] == '发布'){
echo "发布"
Namespace_Prod = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep namespace | awk -F ':' '{print \$2}'", returnStdout: true).trim()
DeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
Revsion_Prod = sh(script: "kubectl get deployment $DeploymentName -n ${Namespace_Prod} -o=jsonpath='{.spec.template.spec.containers[*].image}' | awk -F ':' '{print \$NF}'", returnStdout: true).trim()
GrayDeploymentName = sh(script: "cat $pipeline_dir/prod/$Project_Name/deployment-gray.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
GrayServiceName = sh(script: "cat $pipeline_dir/prod/$Project_Name/service-gray.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()
GrayIngressName = sh(script: "cat $pipeline_dir/prod/$Project_Name/ingress-gray-header.yml | grep name: | head -n 1 | awk -F ':' '{print \$2}'", returnStdout: true).trim()

sh '''
echo $pipeline_dir
echo "开始部署生产环境"
echo "打印编排文件详细信息"

if [ -e "$pipeline_dir/prod/$Project_Name/deployment.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g"
cat $pipeline_dir/prod/$Project_Name/deployment.yml | sed "s/TAG/${Tag}/g" | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/prod/$Project_Name/service.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/service.yml
cat $pipeline_dir/prod/$Project_Name/service.yml | /usr/bin/kubectl apply -f -
fi

if [ -e "$pipeline_dir/prod/$Project_Name/ingress.yml" ]; then
cat $pipeline_dir/prod/$Project_Name/ingress.yml
cat $pipeline_dir/prod/$Project_Name/ingress.yml | /usr/bin/kubectl apply -f -
fi
'
''

} else {
echo "不发布"
}
}
}
post {
success {
wrap([$class: 'BuildUser']) {
lark (
robot: "9f7c94cd-491e-4309-83b4-9290d01fc285",
type: "CARD",
title: "?? Jenkins 应用发布成功",
text: [
"?? **应用名称**:[${JOB_NAME}](${JOB_URL})",
"?? **应用环境**:Prod",
"?? **任务编号**:[${BUILD_DISPLAY_NAME}](${BUILD_URL})",
"?? **发布状态**: 成功",
"?? **镜像版本**: $Tag",
"?? **镜像仓库**: harbor.kubesre.com:8443/kubesre/$Project_Name",
"?? **执 行 者**: ${env.BUILD_USER}",
""
],
buttons: [
[
title: "更改记录",
url: "${BUILD_URL}changes"
],
[
title: "控制台",
type: "danger",
url: "${BUILD_URL}console"
]
]
)}
}
}
}
}
}

配置Jenkins

依赖的组件(自行安装):

  • Generic Webhook Trigger

  • Pipeline(所有以Pipeline开头的组件)

  • build user vars

  • Blue Ocean

  • Lark Notice(通过上传文件的方式安装)
    https://721806280.github.io/lark-notice-plugin-doc/

配置Lark Notice:

Lark Notice Plugin 是一个用于 Jenkins 的构建通知插件,可以将 Jenkins构建过程以及结果通知推送到 Lark、飞书、钉钉 协作平台。 可配置多个的通知时机,包括 构建启动时、构建中断、构建失败、构建成功时、构建不稳定 等。 支持多种不同类型的消息,包括 文本消息、图片消息, 群名片消息、富文本消息、卡片消息; 同时该插件还提供了自定义模板和变量的功能,使您能够根据自己的需求来定制通知消息的内容和格式。(本次案例是基于飞书进行验证)

准备工作,在飞书群新建一个机器人(
https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot):

在飞书群,点击设置:

image

然后点击群机器人:

image

在飞书群,选择添加机器人

image

填写相应配置信息并点击保存:

image
image

选择系统管理-Lark Notice:

通知时机全部勾选:

image

配置机器人信息并保存

image

配置Java Pipeline

新建任务:

image

填写任务名称,并选择流水线,点击确定:

image

配置Pipeline SCM:

image

修改脚本路径,点击确定:

image

点击构建让配置生效:

image

其他Java项目配置都一样!

配置Vue Pipeline

新建任务:

image

填写任务名称,选择流水线:

image

配置Pipeline SCM:

image

修改脚本路径,点击确定:

image

点击构建让配置生效:

image

其他Vue项目配置都一样!

配置Gitlab Webhook

进入项目,选择webhook:

image

选择添加Webhook:

image

配置webhook URL,Token以Job Name:

image

勾选标签推送事件,也就是说只有标签推送事件才会触发流水线:

image

到此为止,Webook已配置完毕!所有项目配置都一样

触发验证

触发Java Pipeline:

进入标签管理:

image

新建标签:

image

填写信息并点击创建标签(此标签名称也是容器镜像的Tag):

image

进入Jenkins可以看到Gateway Pipeline已经触发了:

image
image

选择发布,并点击确定,将新版本发布到Dev环境:

image

选择发布,并点击确定,将新版本发布到Uat环境:

image

选择对应的灰度发布方式或者跳过:

image

选择发布,并点击确定,将新版本发布到Prod环境:

image

也可以回滚,默认是上一个版本也可修改成想要回滚到的版本:

image

触发 Vue流水线:

进入标签管理:

image

创建标签:

image

填写信息并点击创建标签(此标签名称也是容器镜像的Tag):

image
image

进入Jenkins可以看到ruoyi-ui Pipeline已经触发了:

image

选择发布,并点击确定,将新版本发布到Dev环境:

image

选择发布,并点击确定,将新版本发布到Uat环境:

image

选择发布,并点击确定,将新版本发布到Prod环境:

image

构建通知

image
??

相关推荐

Python钩子函数实现事件驱动系统(created钩子函数)

钩子函数(HookFunction)是现代软件开发中一个重要的设计模式,它允许开发者在特定事件发生时自动执行预定义的代码。在Python生态系统中,钩子函数广泛应用于框架开发、插件系统、事件处理和中...

Python函数(python函数题库及答案)

定义和基本内容def函数名(传入参数):函数体return返回值注意:参数、返回值如果不需要,可以省略。函数必须先定义后使用。参数之间使用逗号进行分割,传入的时候,按照顺序传入...

Python技能:Pathlib面向对象操作路径,比os.path更现代!

在Python编程中,文件和目录的操作是日常中不可或缺的一部分。虽然,这么久以来,钢铁老豆也还是习惯性地使用os、shutil模块的函数式API,这两个模块虽然功能强大,但在某些情况下还是显得笨重,不...

使用Python实现智能物流系统优化与路径规划

阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。在现代物流系统中,优化运输路径和提高配送效率是至关重要的。本文将介绍如何使用Python实现智能物流系统的优化与路...

Python if 语句的系统化学习路径(python里的if语句案例)

以下是针对Pythonif语句的系统化学习路径,从零基础到灵活应用分为4个阶段,包含具体练习项目和避坑指南:一、基础认知阶段(1-2天)目标:理解条件判断的逻辑本质核心语法结构if条件:...

[Python] FastAPI基础:Path路径参数用法解析与实例

查询query参数(上一篇)路径path参数(本篇)请求体body参数(下一篇)请求头header参数本篇项目目录结构:1.路径参数路径参数是URL地址的一部分,是必填的。路径参...

Python小案例55- os模块执行文件路径

在Python中,我们可以使用os模块来执行文件路径操作。os模块提供了许多函数,用于处理文件和目录路径。获取当前工作目录(CurrentWorkingDirectory,CWD):使用os....

python:os.path - 常用路径操作模块

应该是所有程序都需要用到的路径操作,不废话,直接开始以下是常用总结,当你想做路径相关时,首先应该想到的是这个模块,并知道这个模块有哪些主要功能,获取、分割、拼接、判断、获取文件属性。1、路径获取2、路...

原来如此:Python居然有6种模块路径搜索方式

点赞、收藏、加关注,下次找我不迷路当我们使用import语句导入模块时,Python是怎么找到这些模块的呢?今天我就带大家深入了解Python的6种模块路径搜索方式。一、Python模块...

每天10分钟,python进阶(25)(python进阶视频)

首先明确学习目标,今天的目标是继续python中实例开发项目--飞机大战今天任务进行面向对象版的飞机大战开发--游戏代码整编目标:完善整串代码,提供完整游戏代码历时25天,首先要看成品,坚持才有收获i...

python 打地鼠小游戏(打地鼠python程序设计说明)

给大家分享一段AI自动生成的代码(在这个游戏中,玩家需要在有限时间内打中尽可能多的出现在地图上的地鼠),由于我现在用的这个电脑没有安装sublime或pycharm等工具,所以还没有测试,有兴趣的朋友...

python线程之十:线程 threading 最终总结

小伙伴们,到今天threading模块彻底讲完。现在全面总结threading模块1、threading模块有自己的方法详细点击【threading模块的方法】threading模块:较低级...

Python信号处理实战:使用signal模块响应系统事件

信号是操作系统用来通知进程发生了某个事件的一种异步通信方式。在Python中,标准库的signal模块提供了处理这些系统信号的机制。信号通常由外部事件触发,例如用户按下Ctrl+C、子进程终止或系统资...

Python多线程:让程序 “多线作战” 的秘密武器

一、什么是多线程?在日常生活中,我们可以一边听音乐一边浏览新闻,这就是“多任务处理”。在Python编程里,多线程同样允许程序同时执行多个任务,从而提升程序的执行效率和响应速度。不过,Python...

用python写游戏之200行代码写个数字华容道

今天来分析一个益智游戏,数字华容道。当初对这个游戏颇有印象还是在最强大脑节目上面,何猷君以几十秒就完成了这个游戏。前几天写2048的时候,又想起了这个游戏,想着来研究一下。游戏玩法用尽量少的步数,尽量...

取消回复欢迎 发表评论: