java_demo/Jenkinsfile
wangtianqi 7b8bd55554 feat: 完善Jenkins流水线配置
主要更新:
- 使用SSH私钥认证替代用户名密码认证
- 配置deploy-server-ssh-key凭据ID
- 修复Jenkinsfile格式和缩进问题
- 添加SSH配置指南和企业级部署文档

 技术改进:
- 使用sshagent进行安全的SSH连接
- 移除sshpass依赖,提升安全性
- 统一使用root@116.62.163.84进行部署
- 优化Docker镜像传输和部署流程

 新增文档:
- SSH_CONFIG_GUIDE.md - SSH私钥配置指南
- ENTERPRISE_JENKINS_GUIDE.md - 企业级Jenkins部署
- jenkins-docker/ - Jenkins Docker化部署方案
2025-06-23 21:14:09 +08:00

377 lines
14 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'
}
// 使用Jenkins中配置的工具自动安装
tools {
maven 'Maven-3.9.6' // 使用您在Jenkins中配置的Maven名称
// JDK使用容器中已有的不需要额外配置
}
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 "=== Git版本 ==="
git --version
echo "=== 工作目录 ==="
pwd && ls -la
'''
// 设置Maven命令
env.MVN_CMD = 'mvn'
echo "✅ 构建环境检查完成Maven命令: ${env.MVN_CMD}"
}
} }
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/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 """
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 {
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文件并传输到目标服务器
sshagent(['deploy-server-ssh-key']) {
sh '''
# 保存Docker镜像为tar文件
docker save ${IMAGE_NAME}:${IMAGE_TAG} -o ${IMAGE_NAME}-${IMAGE_TAG}.tar
# 传输镜像文件到目标服务器
scp -o StrictHostKeyChecking=no ${IMAGE_NAME}-${IMAGE_TAG}.tar root@${DEPLOY_SERVER}:/tmp/
# 在目标服务器上加载镜像
ssh -o StrictHostKeyChecking=no root@${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 {
// 部署到测试服务器
sshagent(['deploy-server-ssh-key']) {
sh '''
ssh -o StrictHostKeyChecking=no root@${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: '选择部署环境')]
// 部署到生产服务器
sshagent(['deploy-server-ssh-key']) {
sh '''
ssh -o StrictHostKeyChecking=no root@${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()}"
}
}
}
}
}