java_demo/Jenkinsfile
wangtianqi 5db7bc7815 fix: 修复Jenkins流水线JDK安装和上下文问题
- 移除tools自动安装配置,改用环境检测
- 修复所有sh和cleanWs步骤的上下文问题
- 增加异常处理和错误恢复机制
- 支持Maven Wrapper作为fallback
- 添加Jenkins工具配置和问题排查指南
2025-06-23 20:02:56 +08:00

404 lines
15 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
timeout(time: 30, unit: 'MINUTES')
timestamps()
}
environment {
// 使用系统默认或Jenkins节点上已安装的JDK
// 不再强制指定JAVA_HOME让Jenkins自动检测
// 目标服务器配置
DEPLOY_SERVER = '116.62.163.84'
// Docker相关环境变量
IMAGE_NAME = 'jenkins-demo'
IMAGE_TAG = "${BUILD_NUMBER}"
// SonarQube配置
SONAR_HOST_URL = 'http://116.62.163.84:15010'
SONAR_PROJECT_KEY = 'jenkins-demo'
SONAR_TOKEN = 'squ_7e4217cabd0faae6f3b8ee359b3b8e2ac52eb69a'
}
// 移除tools配置改用node内部的检测和安装
stages {
stage('环境设置') {
steps {
echo '🔧 设置构建环境...'
script {
// 在node内部设置环境变量
env.WORKSPACE_PATH = pwd()
// 检测并设置Java环境
try {
sh 'java -version'
echo '✅ Java环境检测成功'
} catch (Exception e) {
echo '⚠️ 使用系统默认Java环境'
}
// 检测并设置Maven环境
try {
sh 'mvn -version'
echo '✅ Maven环境检测成功'
} catch (Exception e) {
echo '⚠️ Maven未检测到使用./mvnw'
env.MVN_CMD = './mvnw'
}
if (!env.MVN_CMD) {
env.MVN_CMD = 'mvn'
}
}
}
}
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 '🔍 检查构建环境...'
sh '''
echo "Java版本:"
java -version
echo "Maven版本:"
mvn -version
echo "Git版本:"
git --version
'''
}
}
stage('编译') {
steps {
echo '🔨 开始编译项目...'
sh "${env.MVN_CMD} clean compile -DskipTests=true"
}
}
stage('单元测试') {
steps {
echo '🧪 运行单元测试...'
sh "${env.MVN_CMD} test"
}
post {
always {
script {
// 发布测试结果
if (fileExists('target/surefire-reports/*.xml')) {
publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
}
// 发布代码覆盖率报告
if (fileExists('target/jacoco.exec')) {
step([$class: 'JacocoPublisher',
execPattern: 'target/jacoco.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java',
exclusionPattern: '**/*Test*.class'
])
}
}
}
}
}
stage('代码质量扫描') {
steps {
echo '🔍 运行SonarQube代码扫描...'
script {
try {
sh """
${env.MVN_CMD} sonar:sonar \
-Dsonar.projectKey=${SONAR_PROJECT_KEY} \
-Dsonar.host.url=${SONAR_HOST_URL} \
-Dsonar.login=${SONAR_TOKEN} \
-Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml
"""
echo "✅ SonarQube代码扫描完成"
} catch (Exception e) {
echo "⚠️ SonarQube扫描失败继续构建流程: ${e.getMessage()}"
}
}
}
}
stage('打包') {
steps {
echo '📦 开始打包应用程序...'
sh "${env.MVN_CMD} package -DskipTests=true"
}
post {
success {
script {
// 归档构建产物
if (fileExists('target/*.jar')) {
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
}
}
}
stage('构建Docker镜像') {
steps {
echo '🐳 构建Docker镜像...'
script {
def image = docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
// 也创建latest标签
sh "docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest"
echo "✅ Docker镜像构建完成: ${IMAGE_NAME}:${IMAGE_TAG}"
}
}
}
stage('传输Docker镜像到服务器') {
when {
anyOf {
branch 'main'
branch 'master'
branch 'develop'
}
}
steps {
echo '📤 传输Docker镜像到目标服务器...'
script {
// 将Docker镜像保存为tar文件并传输到目标服务器
withCredentials([usernamePassword(credentialsId: 'deploy-server-ssh', usernameVariable: 'SSH_USER', passwordVariable: 'SSH_PASS')]) {
sh '''
# 保存Docker镜像为tar文件
docker save ${IMAGE_NAME}:${IMAGE_TAG} -o ${IMAGE_NAME}-${IMAGE_TAG}.tar
# 安装sshpass如果未安装
command -v sshpass >/dev/null 2>&1 || {
echo "安装sshpass..."
apt-get update && apt-get install -y sshpass || yum install -y sshpass || true
}
# 传输镜像文件到目标服务器
sshpass -p "$SSH_PASS" scp -o StrictHostKeyChecking=no ${IMAGE_NAME}-${IMAGE_TAG}.tar ${SSH_USER}@${DEPLOY_SERVER}:/tmp/
# 在目标服务器上加载镜像
sshpass -p "$SSH_PASS" ssh -o StrictHostKeyChecking=no ${SSH_USER}@${DEPLOY_SERVER} << EOF
# 加载Docker镜像
docker load -i /tmp/${IMAGE_NAME}-${IMAGE_TAG}.tar
# 创建latest标签
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
# 清理临时文件
rm -f /tmp/${IMAGE_NAME}-${IMAGE_TAG}.tar
echo "✅ Docker镜像加载完成"
EOF
# 清理本地临时文件
rm -f ${IMAGE_NAME}-${IMAGE_TAG}.tar
'''
}
echo "✅ Docker镜像传输完成"
}
}
}
stage('部署到测试环境') {
when {
anyOf {
branch 'develop'
branch 'feature/*'
}
}
steps {
echo '🚀 部署到测试环境...'
script {
// 部署到测试服务器
withCredentials([usernamePassword(credentialsId: 'deploy-server-ssh', usernameVariable: 'SSH_USER', passwordVariable: 'SSH_PASS')]) {
sh '''
sshpass -p "$SSH_PASS" ssh -o StrictHostKeyChecking=no ${SSH_USER}@${DEPLOY_SERVER} << EOF
# 停止现有容器
docker stop jenkins-demo-test || true
docker rm jenkins-demo-test || true
# 运行新容器
docker run -d --name jenkins-demo-test \\
-p 8080:8080 \\
--restart unless-stopped \\
-e SPRING_PROFILES_ACTIVE=test \\
${IMAGE_NAME}:${IMAGE_TAG}
echo "✅ 测试环境部署完成"
EOF
'''
}
}
}
}
stage('部署到生产环境') {
when {
anyOf {
branch 'main'
branch 'master'
}
}
steps {
echo '🎯 部署到生产环境...'
script {
// 需要手动确认
input message: '确认部署到生产环境?', ok: '部署',
parameters: [choice(name: 'DEPLOY_ENV', choices: ['prod'], description: '选择部署环境')]
// 部署到生产服务器
withCredentials([usernamePassword(credentialsId: 'deploy-server-ssh', usernameVariable: 'SSH_USER', passwordVariable: 'SSH_PASS')]) {
sh '''
sshpass -p "$SSH_PASS" ssh -o StrictHostKeyChecking=no ${SSH_USER}@${DEPLOY_SERVER} << EOF
# 备份当前版本
docker tag ${IMAGE_NAME}:latest ${IMAGE_NAME}:backup-$(date +%Y%m%d-%H%M%S) || true
# 停止现有容器
docker stop jenkins-demo-prod || true
docker rm jenkins-demo-prod || true
# 运行新容器
docker run -d --name jenkins-demo-prod \\
-p 80:8080 \\
--restart unless-stopped \\
-e SPRING_PROFILES_ACTIVE=prod \\
-e JAVA_OPTS="-Xms512m -Xmx1024m" \\
${IMAGE_NAME}:${IMAGE_TAG}
# 更新latest标签
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
echo "✅ 生产环境部署完成"
EOF
'''
}
}
}
}
stage('健康检查') {
steps {
echo '🏥 执行应用健康检查...'
script {
// 等待应用启动
sleep(time: 30, unit: 'SECONDS')
try {
// 检查应用健康状态
def healthCheckUrl = "http://${DEPLOY_SERVER}/api/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 "🔄 应用可能还在启动中,这是正常现象"
}
} catch (Exception e) {
echo "⚠️ 健康检查异常: ${e.getMessage()}"
echo "🔄 这可能是网络问题或应用还在启动中"
}
}
}
}
}
post {
always {
script {
echo '🧹 清理工作空间...'
try {
// 清理Docker镜像
sh '''
docker image prune -f || true
docker system prune -f || true
'''
} catch (Exception e) {
echo "⚠️ Docker清理失败: ${e.getMessage()}"
}
}
}
success {
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}
"""
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
}
}
unstable {
echo '⚠️ 构建不稳定,可能存在测试失败'
}
cleanup {
script {
try {
// 清理工作空间
cleanWs()
} catch (Exception e) {
echo "⚠️ 工作空间清理失败: ${e.getMessage()}"
}
}
}
}
}