From c96ce60eafe76e13c7f8c2b3243c44c9fddd6cdd Mon Sep 17 00:00:00 2001 From: conny890 <1220204125@zust.edu.cn> Date: Thu, 26 Jun 2025 13:46:54 +0800 Subject: [PATCH] feat: add Jenkinsfile and template --- Jenkinsfile.template | 289 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 Jenkinsfile.template diff --git a/Jenkinsfile.template b/Jenkinsfile.template new file mode 100644 index 0000000..42bb83d --- /dev/null +++ b/Jenkinsfile.template @@ -0,0 +1,289 @@ +pipeline { + agent any + + options { + buildDiscarder(logRotator(numToKeepStr: '10')) + timeout(time: 60, unit: 'MINUTES') + timestamps() + } + + environment { + // 目标服务器配置 + DEPLOY_SERVER = 'server_ip' //****************************************************<<<<<<<<===============填写ip + + // Docker相关环境变量 + IMAGE_NAME = 'image_name' //****************************************************<<<<<<<<===============填写镜像名称 + IMAGE_TAG = "image_tag" //****************************************************<<<<<<<<===============填写镜像tag + + // SonarQube配置 + SONAR_HOST_URL = 'sonar_host_url' //****************************************************<<<<<<<<===============填写sonar地址 + SONAR_PROJECT_KEY = 'jenkins-node' + SONAR_TOKEN = 'sqp_0275c1699b0fe76487c99168fea24c49c5272928' + } + + // tools { + // maven 'Maven-3.9.6' + // } + + stages { + stage('Checkout') { + steps { + echo '🔄 开始检出代码...' + checkout scm + + script { + env.GIT_COMMIT_SHORT = sh( + script: "git rev-parse --short HEAD", + returnStdout: true + ).trim() + } + echo "📋 Git提交ID: ${env.GIT_COMMIT_SHORT}" + } + } + + stage('环境检查') { + steps { + echo '🔍 检查构建环境...' + script { + sh ''' + echo "=== node版本 ===" + node -v + ''' + echo "✅ 构建环境检查完成" + } + } + } + + + stage('代码质量扫描') { + steps { + echo '🔍 运行SonarQube代码扫描...' + script { + try { + sh """ + sonar-scanner \ + -Dsonar.projectKey=jenkins-node \ + -Dsonar.sources=. \ + -Dsonar.host.url=http://116.62.163.84:15010 + """ + echo "✅ SonarQube代码扫描完成" + } catch (Exception e) { + echo "⚠️ SonarQube扫描失败,继续构建流程: ${e.getMessage()}" + } + } + } + } + + stage('构建Docker镜像') { + steps { + echo '🐳 构建Docker镜像...' + script { + try { + + echo "开始构建Docker镜像: ${IMAGE_NAME}:${IMAGE_TAG}" + + // 使用传统Docker构建,避免buildx的复杂性 + timeout(time: 20, unit: 'MINUTES') { + sh """ + # 使用传统Docker构建 + docker build -t ${IMAGE_NAME}:${IMAGE_TAG} . + echo "✅ Docker镜像构建完成" + """ + } + + } catch (Exception e) { + echo "❌ Docker构建失败: ${e.getMessage()}" + + // 显示更多调试信息 + sh ''' + echo "=== Docker系统信息 ===" + docker system info + echo "=== Docker磁盘使用情况 ===" + docker system df + ''' + + throw e + } + } + } + } + + stage('部署应用') { + steps { + echo '🚀 部署应用到服务器...' + script { + // 根据分支决定部署端口和配置 + def deployPort = '15022' + def containerName = 'jenkins-node' + def springProfile = 'prod' + + if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) { + deployPort = '15023' // 测试环境使用不同端口 + containerName = 'jenkins-node-test' + springProfile = 'test' + } + + // 传输镜像并部署 + sshagent(['deploy-server-ssh-key']) { + sh """ + # 保存Docker镜像为tar文件 + echo "📤 保存镜像为文件..." + docker save ${IMAGE_NAME}:${IMAGE_TAG} -o ${IMAGE_NAME}-${IMAGE_TAG}.tar + + # 传输镜像文件到目标服务器 + echo "📤 传输镜像到服务器..." + scp -o StrictHostKeyChecking=no ${IMAGE_NAME}-${IMAGE_TAG}.tar root@${DEPLOY_SERVER}:/tmp/ + + # 在目标服务器上部署 + ssh -o StrictHostKeyChecking=no root@${DEPLOY_SERVER} << 'EOF' + echo "🔄 在服务器上部署应用..." + + # 加载Docker镜像 + docker load -i /tmp/${IMAGE_NAME}-${IMAGE_TAG}.tar + + # 停止并删除现有容器 + docker stop ${containerName} || true + docker rm ${containerName} || true + + # 运行新容器 + docker run -d --name ${containerName} \\ + -p ${deployPort}:3000 \\ + --restart unless-stopped \\ + ${IMAGE_NAME}:${IMAGE_TAG} + + # 清理临时文件 + rm -f /tmp/${IMAGE_NAME}-${IMAGE_TAG}.tar + + echo "✅ 应用部署完成,端口: ${deployPort}" + echo "🔗 访问地址: http://${DEPLOY_SERVER}:${deployPort}" +EOF + + # 清理本地临时文件 + rm -f ${IMAGE_NAME}-${IMAGE_TAG}.tar + """ + } + + echo "✅ 应用部署完成!" + } + } + } + + stage('健康检查') { + steps { + echo '🏥 执行应用健康检查...' + script { + // 等待应用启动 + sleep(time: 30, unit: 'SECONDS') + + def deployPort = '15022' + if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) { + deployPort = '15023' + } + + try { + def healthCheckUrl = "http://${DEPLOY_SERVER}:${deployPort}/actuator/health" + def response = sh( + script: "curl -s -o /dev/null -w '%{http_code}' ${healthCheckUrl} || echo '000'", + returnStdout: true + ).trim() + + if (response == "200") { + echo "✅ 应用健康检查通过" + } else { + echo "⚠️ 应用健康检查失败,HTTP状态码: ${response}" + echo "🔄 应用可能还在启动中..." + + // 再等待一段时间重试 + sleep(time: 30, unit: 'SECONDS') + response = sh( + script: "curl -s -o /dev/null -w '%{http_code}' ${healthCheckUrl} || echo '000'", + returnStdout: true + ).trim() + + if (response == "200") { + echo "✅ 重试后健康检查通过" + } else { + echo "⚠️ 健康检查仍未通过,但不阻止构建" + } + } + } catch (Exception e) { + echo "⚠️ 健康检查异常: ${e.getMessage()}" + } + } + } + } + } + + post { + always { + script { + echo '🧹 清理工作空间...' + try { + // 清理Docker资源 + sh ''' + # 清理构建缓存 + docker builder prune -f || true + ''' + } catch (Exception e) { + echo "⚠️ Docker清理失败: ${e.getMessage()}" + } + } + } + + success { + script { + echo '✅ 流水线执行成功!' + + def deployPort = '15022' + if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) { + deployPort = '15023' + } + + def message = """ +🎉 Jenkins构建成功! + +📋 项目: ${env.JOB_NAME} +🔢 构建号: ${env.BUILD_NUMBER} +🌿 分支: ${env.BRANCH_NAME ?: 'unknown'} +📝 提交: ${env.GIT_COMMIT_SHORT ?: 'unknown'} +⏱️ 持续时间: ${currentBuild.durationString} +🔗 构建链接: ${env.BUILD_URL} +🌐 应用地址: http://${DEPLOY_SERVER}:${deployPort} + """ + + echo message + } + } + + failure { + script { + echo '❌ 流水线执行失败!' + def message = """ +💥 Jenkins构建失败! + +📋 项目: ${env.JOB_NAME} +🔢 构建号: ${env.BUILD_NUMBER} +🌿 分支: ${env.BRANCH_NAME ?: 'unknown'} +📝 提交: ${env.GIT_COMMIT_SHORT ?: 'unknown'} +⏱️ 持续时间: ${currentBuild.durationString} +🔗 构建链接: ${env.BUILD_URL} +📄 查看日志: ${env.BUILD_URL}console + """ + + echo message + } + } + + cleanup { + script { + try { + // 清理工作空间 + cleanWs() + echo "✅ 清理完成" + } catch (Exception e) { + echo "⚠️ 清理失败: ${e.getMessage()}" + } + } + } + } +} \ No newline at end of file