java_demo/Jenkinsfile

364 lines
14 KiB
Groovy
Raw Permalink 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: 60, unit: 'MINUTES')
timestamps()
}
environment {
// 目标服务器配置
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 {
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 "=== Java版本 ==="
java -version
echo "=== Maven版本 ==="
mvn -version
echo "=== Docker版本 ==="
docker --version
echo "=== 工作目录 ==="
pwd && ls -la
'''
env.MVN_CMD = 'mvn'
echo "✅ 构建环境检查完成"
}
}
}
stage('编译') {
steps {
echo '🔨 开始编译项目...'
sh "mvn clean compile -DskipTests=true"
}
}
stage('单元测试') {
steps {
echo '🧪 运行单元测试...'
sh "mvn test"
}
post {
always {
script {
if (fileExists('target/surefire-reports/*.xml')) {
publishTestResults testResultsPattern: 'target/surefire-reports/*.xml'
}
if (fileExists('target/site/jacoco/jacoco.xml')) {
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/site/jacoco',
reportFiles: 'index.html',
reportName: 'JaCoCo Coverage Report'
])
echo '✅ JaCoCo覆盖率报告已发布'
}
}
}
}
}
stage('代码质量扫描') {
steps {
echo '🔍 运行SonarQube代码扫描...'
script {
try {
sh """
mvn 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 "mvn package -DskipTests=true"
}
post {
success {
script {
if (fileExists('target/*.jar')) {
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
}
}
}
stage('构建Docker镜像') {
steps {
echo '🐳 构建Docker镜像...'
script {
try {
// 清理旧镜像
sh 'docker image prune -f || true'
echo "开始构建Docker镜像: ${IMAGE_NAME}:${IMAGE_TAG}"
// 使用传统Docker构建避免buildx的复杂性
timeout(time: 20, unit: 'MINUTES') {
sh """
# 使用传统Docker构建
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} -t ${IMAGE_NAME}:latest .
echo "✅ Docker镜像构建完成"
# 验证镜像
docker images ${IMAGE_NAME}:${IMAGE_TAG}
"""
}
} catch (Exception e) {
echo "❌ Docker构建失败: ${e.getMessage()}"
// 显示更多调试信息
sh '''
echo "=== Docker系统信息 ==="
docker system info
echo "=== Docker磁盘使用情况 ==="
docker system df
echo "=== 当前镜像列表 ==="
docker images
'''
throw e
}
}
}
}
stage('部署应用') {
steps {
echo '🚀 部署应用到服务器...'
script {
// 根据分支决定部署端口和配置
def deployPort = '15020'
def containerName = 'jenkins-demo'
def springProfile = 'prod'
if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) {
deployPort = '15021' // 测试环境使用不同端口
containerName = 'jenkins-demo-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 tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
# 停止并删除现有容器
docker stop ${containerName} || true
docker rm ${containerName} || true
# 运行新容器
docker run -d --name ${containerName} \\
-p ${deployPort}:8080 \\
--restart unless-stopped \\
-e SPRING_PROFILES_ACTIVE=${springProfile} \\
-e JAVA_OPTS="-Xms256m -Xmx512m" \\
${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 = '15020'
if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) {
deployPort = '15021'
}
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 image prune -f || true
# 清理构建缓存
docker builder prune -f || true
'''
} catch (Exception e) {
echo "⚠️ Docker清理失败: ${e.getMessage()}"
}
}
}
success {
script {
echo '✅ 流水线执行成功!'
def deployPort = '15020'
if (env.BRANCH_NAME == 'develop' || env.BRANCH_NAME?.startsWith('feature/')) {
deployPort = '15021'
}
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()}"
}
}
}
}
}