316 lines
10 KiB
Plaintext
316 lines
10 KiB
Plaintext
pipeline {
|
||
agent any
|
||
options {
|
||
// 构建记录保留策略
|
||
buildDiscarder(logRotator(
|
||
artifactDaysToKeepStr: '',
|
||
artifactNumToKeepStr: '',
|
||
daysToKeepStr: '2',
|
||
numToKeepStr: '2'
|
||
))
|
||
// 防止并发构建冲突
|
||
disableConcurrentBuilds()
|
||
// 设置构建超时时间(30分钟)
|
||
timeout(time: 30, unit: 'MINUTES')
|
||
}
|
||
|
||
// 定义构建参数
|
||
parameters {
|
||
string(
|
||
name: 'PROFILES',
|
||
defaultValue: getDefaultProfile(), // 根据分支自动设置默认值
|
||
description: '编译环境参数(dev/test/prod)',
|
||
trim: true
|
||
)
|
||
}
|
||
|
||
stages {
|
||
// 阶段1:环境配置和参数设置
|
||
stage('Setup Parameters & ENV') {
|
||
steps {
|
||
// 从Jenkins配置管理获取配置文件
|
||
configFileProvider([configFile(fileId: 'xiaomayi-web', variable: 'settingsFile')]) {
|
||
script {
|
||
setupEnvironment() // 调用环境设置函数
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 阶段2:代码编译构建
|
||
stage('Build') {
|
||
agent {
|
||
label {
|
||
label "${env.JENKINS_AGENT}" // 使用动态指定的Jenkins agent
|
||
}
|
||
}
|
||
steps {
|
||
// 使用指定的Maven配置进行构建
|
||
configFileProvider([configFile(fileId: "${env.MAVEN_SET}", variable: 'MAVEN_SETTINGS_XML')]) {
|
||
sh """
|
||
# 使用Maven进行依赖清理和项目构建
|
||
/usr/local/maven/bin/mvn --settings ${MAVEN_SETTINGS_XML} \
|
||
dependency:purge-local-repository -Dinclude=com.xiaomayi \
|
||
-f pom.xml clean package -P${params.PROFILES} \
|
||
-Dmaven.test.skip=true
|
||
"""
|
||
// 验证构建产物
|
||
sh "ls -lsh ${env.JAR_FILE_PATH}/${env.JAR_FILE}"
|
||
sh "md5sum ${env.JAR_FILE_PATH}/${env.JAR_FILE}"
|
||
}
|
||
}
|
||
post {
|
||
always {
|
||
// 归档构建产物以便后续使用和审计
|
||
archiveArtifacts artifacts: "${env.JAR_FILE_PATH}/${env.JAR_FILE}", fingerprint: true
|
||
}
|
||
}
|
||
}
|
||
|
||
// 阶段3:Docker镜像构建和推送
|
||
stage('Container Build') {
|
||
agent {
|
||
label {
|
||
label "${env.JENKINS_AGENT}"
|
||
}
|
||
}
|
||
steps {
|
||
script {
|
||
buildAndPushDockerImage() // 调用镜像构建函数
|
||
}
|
||
}
|
||
post {
|
||
always {
|
||
cleanDockerImages() // 清理本地Docker镜像节省空间
|
||
}
|
||
}
|
||
}
|
||
|
||
// 阶段4:部署到目标服务器
|
||
stage('Deploy') {
|
||
agent {
|
||
label {
|
||
label "${env.JENKINS_AGENT}"
|
||
}
|
||
}
|
||
steps {
|
||
script {
|
||
deployToServer() // 调用部署函数
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 构建后处理
|
||
post {
|
||
success {
|
||
echo "✅ Pipeline执行成功 - 分支: ${env.BRANCH_NAME} - 构建ID: ${env.BUILD_ID} - 环境: ${params.PROFILES}"
|
||
}
|
||
failure {
|
||
echo "❌ Pipeline执行失败 - 分支: ${env.BRANCH_NAME} - 构建ID: ${env.BUILD_ID}"
|
||
// 此处可以添加邮件通知、钉钉通知等
|
||
}
|
||
aborted {
|
||
echo "⚠️ Pipeline被中止 - 分支: ${env.BRANCH_NAME} - 构建ID: ${env.BUILD_ID}"
|
||
}
|
||
cleanup {
|
||
cleanWs() // 清理工作空间,释放磁盘空间
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 根据当前分支获取默认的Profile参数
|
||
* @return 对应的环境参数(dev/test/prod)
|
||
*/
|
||
def getDefaultProfile() {
|
||
def branch = env.BRANCH_NAME ?: 'dev'
|
||
switch(branch) {
|
||
case 'dev':
|
||
return 'dev'
|
||
case 'test':
|
||
return 'test'
|
||
case 'master':
|
||
return 'prod'
|
||
default:
|
||
return 'dev' // 默认使用dev环境
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置构建环境变量和配置参数
|
||
* 从配置文件中读取分支和服务的具体配置
|
||
*/
|
||
def setupEnvironment() {
|
||
// 读取JSON配置文件
|
||
def config = readJSON file: "${settingsFile}"
|
||
|
||
// 解析任务名称(处理多级目录结构)
|
||
def jobNameStr = env.JOB_NAME.tokenize('/')
|
||
def jobName = jobNameStr[0]
|
||
|
||
// 获取分支配置和服务配置
|
||
def branchConfig = config.branchConfig."${env.BRANCH_NAME}"
|
||
def serviceConfig = config.serviceConfig."${jobName}"
|
||
|
||
// 验证配置是否存在
|
||
if (!branchConfig || !serviceConfig) {
|
||
error("❌ 无法获取分支 ${env.BRANCH_NAME} 或服务 ${jobName} 的配置")
|
||
}
|
||
|
||
// 设置分支相关环境变量
|
||
env.MAVEN_SET = branchConfig.MAVEN_SET
|
||
env.REGISTRY = branchConfig.registry
|
||
env.REGISTRY_CREDENTIAL = branchConfig.registryCredential
|
||
env.NAMESPACE = branchConfig.namespace
|
||
env.JENKINS_AGENT = branchConfig.jenkinsAgent
|
||
env.SERVER_PORT = branchConfig.serverPort
|
||
|
||
// 设置服务相关环境变量
|
||
env.PROJECT_NAME = serviceConfig.containerName
|
||
env.SERVICE_PATH = serviceConfig.servicePath
|
||
env.JAR_PACKAGE_NAME = serviceConfig.jarPackageName
|
||
env.BASE_IMAGE = "${env.REGISTRY}/xiaomayi-base/jdk:17.0.16-rocky8-dos"
|
||
env.PORT = serviceConfig.port
|
||
env.JAR_FILE = "${env.JAR_PACKAGE_NAME}"
|
||
env.JAR_FILE_PATH = "${env.SERVICE_PATH}/target"
|
||
env.SERVER_HOST_01 = serviceConfig.serverHost01
|
||
|
||
// 根据分支设置服务器IP地址
|
||
if (['dev', 'test', 'master'].contains(env.BRANCH_NAME)) {
|
||
env.SERVER_IP_01 = branchConfig."${env.SERVER_HOST_01}"
|
||
if (!env.SERVER_IP_01) {
|
||
error("❌ 无法获取服务器IP地址配置: ${env.SERVER_HOST_01}")
|
||
}
|
||
} else {
|
||
error("❌ 不支持的分支: ${env.BRANCH_NAME}")
|
||
}
|
||
|
||
// 打印环境配置信息用于调试和验证
|
||
echo """
|
||
📋 ============ 环境配置信息 ============
|
||
🔹 分支: ${env.BRANCH_NAME}
|
||
🔹 项目: ${jobName}
|
||
🔹 环境参数: ${params.PROFILES}
|
||
🔹 Maven配置: ${env.MAVEN_SET}
|
||
🔹 镜像仓库: ${env.REGISTRY}
|
||
🔹 命名空间: ${env.NAMESPACE}
|
||
🔹 项目名称: ${env.PROJECT_NAME}
|
||
🔹 JAR文件: ${env.JAR_FILE}
|
||
🔹 目标服务器: ${env.SERVER_IP_01}:${env.SERVER_PORT}
|
||
🔹 Jenkins Agent: ${env.JENKINS_AGENT}
|
||
====================================
|
||
"""
|
||
}
|
||
|
||
/**
|
||
* 构建并推送Docker镜像到镜像仓库
|
||
*/
|
||
def buildAndPushDockerImage() {
|
||
// 打印镜像构建参数
|
||
echo """
|
||
🐳 ============ 镜像构建参数 ============
|
||
🔹 基础镜像: ${env.BASE_IMAGE}
|
||
🔹 暴露端口: ${env.PORT}
|
||
🔹 JAR文件: ${env.JAR_FILE}
|
||
🔹 JAR文件路径: ${env.JAR_FILE_PATH}
|
||
🔹 镜像标签: ${env.NAMESPACE}/${env.PROJECT_NAME}:${params.PROFILES}-${env.BUILD_ID}
|
||
====================================
|
||
"""
|
||
|
||
// 使用Docker凭证连接到镜像仓库
|
||
docker.withRegistry("https://${env.REGISTRY}", env.REGISTRY_CREDENTIAL) {
|
||
def dockerImage = docker.build(
|
||
"${env.NAMESPACE}/${env.PROJECT_NAME}:${params.PROFILES}-${env.BUILD_ID}",
|
||
// 构建参数传递
|
||
"--build-arg VAR_PROFILES=${params.PROFILES} " +
|
||
"--build-arg VAR_BASE_IMAGE=${env.BASE_IMAGE} " +
|
||
"--build-arg VAR_PORT=${env.PORT} " +
|
||
"--build-arg VAR_JARFILE=${env.JAR_FILE} " +
|
||
"--build-arg VAR_JARFILE_PATH=${env.JAR_FILE_PATH} " +
|
||
"-f .jenkinsfile/Dockerfile ./" // 指定Dockerfile路径
|
||
)
|
||
// 推送镜像到仓库
|
||
dockerImage.push()
|
||
echo "✅ Docker镜像推送成功: ${env.NAMESPACE}/${env.PROJECT_NAME}:${params.PROFILES}-${env.BUILD_ID}"
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理本地Docker镜像,释放磁盘空间
|
||
*/
|
||
def cleanDockerImages() {
|
||
echo "🧹 开始清理本地Docker镜像..."
|
||
|
||
try {
|
||
// 删除本地构建的镜像
|
||
sh """
|
||
docker rmi -f ${env.NAMESPACE}/${env.PROJECT_NAME}:${params.PROFILES}-${env.BUILD_ID} || true
|
||
docker rmi -f ${env.REGISTRY}/${env.NAMESPACE}/${env.PROJECT_NAME}:${params.PROFILES}-${env.BUILD_ID} || true
|
||
"""
|
||
// 清理悬空镜像(不再被任何容器引用的镜像)
|
||
sh "docker image prune -f || true"
|
||
echo "✅ Docker镜像清理完成"
|
||
} catch (Exception e) {
|
||
echo "⚠️ 镜像清理过程中出现警告: ${e.message}"
|
||
// 镜像清理失败不影响主要流程,继续执行
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 部署应用到目标服务器
|
||
* 通过SSH连接到服务器执行部署脚本
|
||
*/
|
||
def deployToServer() {
|
||
// 构建部署命令
|
||
def deployCmd = """
|
||
ssh -o StrictHostKeyChecking=no \
|
||
-o ConnectTimeout=30 \
|
||
-o ServerAliveInterval=60 \
|
||
esxi@${env.SERVER_IP_01} -p ${env.SERVER_PORT} \
|
||
'cd /usr/local/docker/xiaomayi-web/; ./deploy.sh deploy ${env.PROJECT_NAME} ${params.PROFILES}-${env.BUILD_ID}'
|
||
"""
|
||
|
||
echo """
|
||
🚀 ============ 开始部署 ============
|
||
🔹 目标服务器: ${env.SERVER_IP_01}:${env.SERVER_PORT}
|
||
🔹 服务名称: ${env.PROJECT_NAME}
|
||
🔹 镜像版本: ${params.PROFILES}-${env.BUILD_ID}
|
||
====================================
|
||
"""
|
||
|
||
echo "执行部署命令: ${deployCmd}"
|
||
|
||
// 部署过程添加重试机制和超时控制
|
||
try {
|
||
retry(3) { // 最多重试3次
|
||
timeout(time: 10, unit: 'MINUTES') { // 部署超时时间10分钟
|
||
sh deployCmd
|
||
}
|
||
}
|
||
echo "✅ 部署成功完成"
|
||
} catch (Exception e) {
|
||
error("❌ 部署失败: ${e.message}")
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 验证必要环境变量是否设置(可选,可用于前置检查)
|
||
*/
|
||
def validateEnvironment() {
|
||
def requiredEnvVars = [
|
||
'BRANCH_NAME', 'MAVEN_SET', 'REGISTRY', 'REGISTRY_CREDENTIAL',
|
||
'NAMESPACE', 'JENKINS_AGENT', 'PROJECT_NAME', 'SERVER_IP_01'
|
||
]
|
||
|
||
def missingVars = []
|
||
requiredEnvVars.each { var ->
|
||
if (!env."${var}") {
|
||
missingVars.add(var)
|
||
}
|
||
}
|
||
|
||
if (missingVars) {
|
||
error("❌ 缺少必要环境变量: ${missingVars.join(', ')}")
|
||
}
|
||
} |