// ============================================ // AIOT Platform - Jenkins Pipeline // 智能增量构建 + Docker 容器化部署 // ============================================ pipeline { agent any options { // 保留最近 10 次构建 buildDiscarder(logRotator(numToKeepStr: '10')) // 禁止并发构建 disableConcurrentBuilds() // 超时设置 timeout(time: 60, unit: 'MINUTES') } environment { // Gitea 仓库配置 GIT_REPO = 'http://124.221.55.225:3000/XW-AIOT/aiot-platform-cloud.git' // Docker Registry 配置 REGISTRY = 'localhost:5000' // Maven 配置 MAVEN_OPTS = '-Xmx2048m -Dmaven.repo.local=.m2/repository' // 镜像标签 IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}-${env.GIT_COMMIT?.take(8)}" // 服务列表(核心服务) CORE_SERVICES = 'viewsh-gateway,viewsh-module-system-server,viewsh-module-infra-server,viewsh-module-iot-server,viewsh-module-iot-gateway,viewsh-module-ops-server' } stages { stage('Checkout') { steps { script { echo "=== 检出代码 ===" checkout scm // 获取 Git 提交信息 env.GIT_COMMIT_MSG = sh( script: 'git log -1 --pretty=%B', returnStdout: true ).trim() echo "Commit: ${env.GIT_COMMIT}" echo "Message: ${env.GIT_COMMIT_MSG}" } } } stage('Detect Changes') { steps { script { echo "=== 检测变更的服务 ===" // 获取变更的文件列表 def changedFiles = sh( script: ''' if [ "${GIT_PREVIOUS_COMMIT}" = "" ]; then # 首次构建,构建所有核心服务 echo "all" else # 检测变更的文件 git diff --name-only ${GIT_PREVIOUS_COMMIT} ${GIT_COMMIT} fi ''', returnStdout: true ).trim() echo "Changed files:\n${changedFiles}" // 分析需要构建的服务 def servicesToBuild = [] if (changedFiles == 'all') { // 首次构建或强制全量构建 servicesToBuild = CORE_SERVICES.split(',') } else { // 检测每个服务是否有变更 CORE_SERVICES.split(',').each { service -> def modulePath = service.replace('-server', '').replace('viewsh-module-', 'viewsh-module-').replace('viewsh-', '') if (changedFiles.contains(modulePath) || changedFiles.contains('pom.xml') || changedFiles.contains('viewsh-framework') || changedFiles.contains('viewsh-dependencies')) { servicesToBuild.add(service) } } // 如果没有检测到变更,但有代码提交,构建所有服务 if (servicesToBuild.isEmpty() && !changedFiles.isEmpty()) { servicesToBuild = CORE_SERVICES.split(',') } } env.SERVICES_TO_BUILD = servicesToBuild.join(',') echo "Services to build: ${env.SERVICES_TO_BUILD}" if (servicesToBuild.isEmpty()) { echo "No services need to be built. Skipping build." currentBuild.result = 'SUCCESS' return } } } } stage('Maven Build') { when { expression { env.SERVICES_TO_BUILD != '' } } steps { script { echo "=== Maven 构建 ===" def services = env.SERVICES_TO_BUILD.split(',') // 并行构建(最多 3 个并行,避免内存溢出) def parallelBuilds = [:] def batchSize = 3 for (int i = 0; i < services.size(); i += batchSize) { def batch = services[i..Math.min(i + batchSize - 1, services.size() - 1)] batch.each { service -> def modulePath = getModulePath(service) parallelBuilds["Build ${service}"] = { stage("Build ${service}") { echo "Building ${service}..." sh """ mvn clean package -pl ${modulePath} -am -DskipTests -B """ } } } // 执行当前批次 parallel parallelBuilds parallelBuilds = [:] } } } } stage('Docker Build & Push') { when { expression { env.SERVICES_TO_BUILD != '' } } steps { script { echo "=== Docker 镜像构建与推送 ===" def services = env.SERVICES_TO_BUILD.split(',') def parallelBuilds = [:] services.each { service -> parallelBuilds["Docker ${service}"] = { stage("Docker ${service}") { def modulePath = getModulePath(service) def jarName = service echo "Building Docker image for ${service}..." sh """ docker build \ -f docker/Dockerfile.template \ --build-arg MODULE_NAME=${modulePath} \ --build-arg JAR_NAME=${jarName} \ --build-arg SKIP_TESTS=true \ -t ${REGISTRY}/${service}:${IMAGE_TAG} \ -t ${REGISTRY}/${service}:latest \ . """ echo "Pushing Docker image for ${service}..." sh """ docker push ${REGISTRY}/${service}:${IMAGE_TAG} docker push ${REGISTRY}/${service}:latest """ echo "Cleaning up old images for ${service}..." sh """ docker images ${REGISTRY}/${service} --format '{{.ID}} {{.Tag}}' | \ grep -v '${IMAGE_TAG}' | grep -v 'latest' | \ awk '{print \$1}' | head -n -2 | xargs -r docker rmi -f || true """ } } } // 并行构建镜像(最多 3 个并行) def batchSize = 3 for (int i = 0; i < services.size(); i += batchSize) { def batch = services[i..Math.min(i + batchSize - 1, services.size() - 1)] def batchBuilds = [:] batch.each { service -> batchBuilds["Docker ${service}"] = parallelBuilds["Docker ${service}"] } parallel batchBuilds } } } } stage('Deploy') { when { expression { env.SERVICES_TO_BUILD != '' && env.BRANCH_NAME == 'master' } } steps { script { echo "=== 部署到生产环境 ===" def services = env.SERVICES_TO_BUILD.split(',') services.each { service -> echo "Deploying ${service}..." sh """ docker-compose -f docker-compose.core.yml pull ${service} docker-compose -f docker-compose.core.yml up -d ${service} """ // 等待服务健康检查 echo "Waiting for ${service} to be healthy..." sh """ timeout 120 sh -c 'until docker inspect --format="{{.State.Health.Status}}" aiot-${service} 2>/dev/null | grep -q healthy; do sleep 5; done' || true """ } echo "Deployment completed!" } } } } post { success { echo "=== 构建成功 ===" echo "Built services: ${env.SERVICES_TO_BUILD}" echo "Image tag: ${IMAGE_TAG}" } failure { echo "=== 构建失败 ===" echo "Please check the logs for details." } always { // 清理工作空间(可选) // cleanWs() // 显示磁盘使用情况 sh 'df -h' sh 'docker system df' } } } // ============================================ // 辅助函数 // ============================================ def getModulePath(String service) { // 根据服务名获取 Maven 模块路径 def moduleMap = [ 'viewsh-gateway': 'viewsh-gateway', 'viewsh-module-system-server': 'viewsh-module-system/viewsh-module-system-server', 'viewsh-module-infra-server': 'viewsh-module-infra/viewsh-module-infra-server', 'viewsh-module-iot-server': 'viewsh-module-iot/viewsh-module-iot-server', 'viewsh-module-iot-gateway': 'viewsh-module-iot/viewsh-module-iot-gateway', 'viewsh-module-ops-server': 'viewsh-module-ops/viewsh-module-ops-server' ] return moduleMap[service] ?: service }