本案例通过若依项目作为案例,通过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自动触发流水线构建。
图片
流程说明:
研发项目负责人代码开发完成后进行合并代码并生成Tag
Gitlab通过Webhook自动触发Jenkins Pipeline构建
流水线设计:
图片
Jenkins流水线完整图:
图片
在实际企业环境中,基础镜像都会根据具体得需求定义适合自己得基础镜像。
定义Maven镜像:
用于代码构建编译打包,会把Ruoyi相关依赖包打到基础镜像内,避免分层构建失败。
# 拉取源代码并切换分支$ https://gitee.com/y_project/RuoYi-Cloud.git$ git checkout v3.6.3$ cd..# 定义Dockerfile$ cat DockerfileFROMmaven:3.8.6-openjdk-8ADDRuoYi-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/shjava-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/shipTrue=falsejava_service_ip=""code=falsegetPodIp(){ 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/nullif[$?-eq0];thenecho"get java service ip success"ipTrue=trueelseecho"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"];thenecho"java service down from nacos success"code=trueelseecho"java service down from nacos failed"fi }start(){ getPodIpif$ipTrue;thendownService sleep30elseecho"down $JAVA_SERVICE_NAME failed">>down_service.log fi }start# 定义Dockerfile$ cat DockerfileFROMopenjdk:8-jre WORKDIR/dataCOPY./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:环境名称
分层构建好处:
不依赖本地环境
减小容器镜像大小
Java Dockerfile(分层构建):
FROMuhub.service.ucloud.cn/kubesre/maven:jdk8ASbuild COPY src/opt/src/COPY pom.xml/opt/RUN cd/opt/&&mvn clean install-DskipTestsFROMuhub.service.ucloud.cn/kubesre/java-base:v8# 复制jar文件到路径COPY--from=build /opt/target/*.jar /data/app.jar
Vue Dockerfile(分层构建):
FROMnode:16ASbuilder# 设置工作目录WORKDIR/usr/src/app# 将项目文件复制到 Docker 镜像中COPY..# 安装项目依赖RUN npm install--registry=https://registry.npmmirror.com# 构建静态文件RUN npm run build:prod# 使用 Nginx 镜像作为基础镜像,用于托管静态文件FROMnginx:stable-alpine WORKDIR/home/ruoyi/projects/ruoyi-ui# 将 VuePress 构建的静态文件复制到 Nginx 的网站目录COPY--from=builder /usr/src/app/dist /home/ruoyi/projects/ruoyi-uiCOPY./nginx/conf/nginx.conf/etc/nginx/nginx.conf# 暴露 80 端口EXPOSE80# 启动 NginxCMD["nginx","-g","daemon off;"]
如下所有Pipeline文件,需要自行修改内容:
credentialsId
robot
镜像仓库地址
如何查找credentialsId:
图片
如何查找robot:
图片
Java Pipeline:
pipeline { agentanytriggers { 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})","🌟 **构建状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: uhub.service.ucloud.cn/kubesre/$Project_Name","🕐 **构建用时**: ${currentBuild.duration} ms","👤 **执 行 者**: ${env.BUILD_USER}","<at id=all></at>"],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,如果跳过为noif(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=jsnotallow='{.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})","😘 **发布状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: harbor.kubesre.com:8443/kubesre/$Project_Name","😝 **执 行 者**: ${env.BUILD_USER}","<at id=all></at>"],buttons:[[title:"更改记录",url:"${BUILD_URL}changes"],[title:"控制台",type:"danger",url:"${BUILD_URL}console"]])} } } } stage('RollBack'){ steps { echo"版本回滚操作"script { defRollBack=input(message:'确定要执行回滚操作吗?',parameters:[choice(name:'是否回滚',choices:['是','否']),string(name:'回滚版本',defaultValue: Revsion_Prod,description:'默认上一个版本')],ok:'确定',submitter:'admin',submitterParameter:'APPROVER')if(RollBack['是否回滚']=='是'){ echo"版本回滚成功"echoRollBack['回滚版本']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"放弃版本回滚"echoRollBack['回滚版本']} } } } } }
Vue Pipeline:
pipeline { agentanytriggers { 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})","🌟 **构建状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: uhub.service.ucloud.cn/kubesre/$Project_Name","🕐 **构建用时**: ${currentBuild.duration} ms","👤 **执 行 者**: ${env.BUILD_USER}","<at id=all></at>"],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=jsnotallow='{.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})","😘 **发布状态**: <font color='green'>成功</font>","🤩 **镜像版本**: $Tag","😎 **镜像仓库**: harbor.kubesre.com:8443/kubesre/$Project_Name","😝 **执 行 者**: ${env.BUILD_USER}","<at id=all></at>"],buttons:[[title:"更改记录",url:"${BUILD_URL}changes"],[title:"控制台",type:"danger",url:"${BUILD_URL}console"]])} } } } } }
依赖的组件(自行安装):
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):
在飞书群,点击设置:
图片
然后点击群机器人:
图片
在飞书群,选择添加机器人
图片
填写相应配置信息并点击保存:
图片
图片
选择系统管理-Lark Notice:
图片
通知时机全部勾选:
图片
配置机器人信息并保存
图片
新建任务:
图片
填写任务名称,并选择流水线,点击确定:
图片
配置Pipeline SCM:
图片
修改脚本路径,点击确定:
图片
点击构建让配置生效:
图片
其他Java项目配置都一样!
新建任务:
图片
填写任务名称,选择流水线:
图片
配置Pipeline SCM:
图片
修改脚本路径,点击确定:
图片
点击构建让配置生效:
图片
其他Vue项目配置都一样!
进入项目,选择webhook:
图片
选择添加Webhook:
图片
配置webhook URL,Token以Job Name:
图片
勾选标签推送事件,也就是说只有标签推送事件才会触发流水线:
图片
到此为止,Webook已配置完毕!所有项目配置都一样。
触发Java Pipeline:
进入标签管理:
图片
新建标签:
图片
填写信息并点击创建标签(此标签名称也是容器镜像的Tag):
图片
进入Jenkins可以看到Gateway Pipeline已经触发了:
图片
图片
选择发布,并点击确定,将新版本发布到Dev环境:
图片
选择发布,并点击确定,将新版本发布到Uat环境:
图片
选择对应的灰度发布方式或者跳过:
图片
选择发布,并点击确定,将新版本发布到Prod环境:
图片
也可以回滚,默认是上一个版本也可修改成想要回滚到的版本:
图片
触发 Vue流水线:
进入标签管理:
图片
创建标签:
图片
填写信息并点击创建标签(此标签名称也是容器镜像的Tag):
图片
图片
进入Jenkins可以看到ruoyi-ui Pipeline已经触发了:
图片
选择发布,并点击确定,将新版本发布到Dev环境:
图片
选择发布,并点击确定,将新版本发布到Uat环境:
图片
选择发布,并点击确定,将新版本发布到Prod环境:
图片
图片