|
|
@@ -0,0 +1,330 @@
|
|
|
+// ============================================
|
|
|
+// Jenkins Pipeline for Spring Cloud Log Demo
|
|
|
+// CI (Continuous Integration) Pipeline - 优化版
|
|
|
+// ============================================
|
|
|
+// 优化点:
|
|
|
+// - Docker 层缓存(分离 POM 和源码)
|
|
|
+// - Maven 缓存持久化
|
|
|
+// - 镜像并行构建(3 倍加速)
|
|
|
+// - 智能增量编译
|
|
|
+
|
|
|
+pipeline {
|
|
|
+ agent any
|
|
|
+
|
|
|
+ options {
|
|
|
+ // 保留最近 15 次构建
|
|
|
+ buildDiscarder(logRotator(numToKeepStr: '15'))
|
|
|
+ // 30 分钟超时
|
|
|
+ timeout(time: 30, unit: 'MINUTES')
|
|
|
+ // 禁止并发构建(保护 Maven 缓存)
|
|
|
+ disableConcurrentBuilds()
|
|
|
+ // 时间戳
|
|
|
+ timestamps()
|
|
|
+ }
|
|
|
+
|
|
|
+ parameters {
|
|
|
+ choice(
|
|
|
+ name: 'BUILD_TYPE',
|
|
|
+ choices: ['SNAPSHOT', 'RELEASE'],
|
|
|
+ description: '构建类型'
|
|
|
+ )
|
|
|
+ booleanParam(
|
|
|
+ name: 'SKIP_TESTS',
|
|
|
+ defaultValue: false,
|
|
|
+ description: '跳过单元测试'
|
|
|
+ )
|
|
|
+ booleanParam(
|
|
|
+ name: 'PUSH_DOCKER',
|
|
|
+ defaultValue: false,
|
|
|
+ description: '推送Docker镜像到仓库'
|
|
|
+ )
|
|
|
+ string(
|
|
|
+ name: 'DOCKER_REGISTRY',
|
|
|
+ defaultValue: 'harbor.stardance',
|
|
|
+ description: 'Docker注册表地址'
|
|
|
+ )
|
|
|
+ string(
|
|
|
+ name: 'DOCKER_NAMESPACE',
|
|
|
+ defaultValue: 'shoprecycle',
|
|
|
+ description: 'Docker命名空间'
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ environment {
|
|
|
+ // ✅ 关键优化:Maven 缓存配置
|
|
|
+ MAVEN_HOME = tool('maven-3.8')
|
|
|
+ MAVEN_CACHE_DIR = "${WORKSPACE}/.m2/repository"
|
|
|
+ MAVEN_OPTS = '''
|
|
|
+ -Dmaven.repo.local=${WORKSPACE}/.m2/repository \
|
|
|
+ -Xmx1024m -Xms512m \
|
|
|
+ -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
|
|
|
+ -Dorg.slf4j.simpleLogger.defaultLogLevel=warn
|
|
|
+ '''
|
|
|
+
|
|
|
+ // Git 信息
|
|
|
+ GIT_COMMIT_SHORT = sh(script: 'git rev-parse --short HEAD', returnStdout: true).trim()
|
|
|
+ GIT_AUTHOR = sh(script: 'git log -1 --format=%an', returnStdout: true).trim()
|
|
|
+
|
|
|
+ // 构建版本
|
|
|
+ BUILD_NUMBER_PADDED = sh(script: 'printf "%04d" ${BUILD_NUMBER}', returnStdout: true).trim()
|
|
|
+ APP_VERSION = "${BUILD_TYPE == 'RELEASE' ? '1.0' : '1.0.0'}-${BUILD_NUMBER_PADDED}"
|
|
|
+ IMAGE_TAG = "${APP_VERSION}-${GIT_COMMIT_SHORT}"
|
|
|
+
|
|
|
+ // Docker 配置
|
|
|
+ DOCKER_BUILDKIT = '1'
|
|
|
+ DOCKER_DEFAULT_PLATFORM = 'linux/amd64'
|
|
|
+ }
|
|
|
+
|
|
|
+ stages {
|
|
|
+ stage('準備') {
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo """
|
|
|
+ ════════════════════════════════════════════
|
|
|
+ CI 构建信息(优化版)
|
|
|
+ ════════════════════════════════════════════
|
|
|
+ 构建类型: ${BUILD_TYPE}
|
|
|
+ Git 提交: ${GIT_COMMIT_SHORT}
|
|
|
+ 作者: ${GIT_AUTHOR}
|
|
|
+ 应用版本: ${APP_VERSION}
|
|
|
+ 镜像TAG: ${IMAGE_TAG}
|
|
|
+ 跳过测试: ${SKIP_TESTS}
|
|
|
+ 推送Docker: ${PUSH_DOCKER}
|
|
|
+ ════════════════════════════════════════════
|
|
|
+ """
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清理 workspace
|
|
|
+ deleteDir()
|
|
|
+ checkout scm
|
|
|
+
|
|
|
+ // 显示工具版本和缓存信息
|
|
|
+ sh '''
|
|
|
+ echo "==== 环境信息 ===="
|
|
|
+ java -version 2>&1 | head -1
|
|
|
+ mvn --version | head -1
|
|
|
+ docker --version
|
|
|
+
|
|
|
+ echo ""
|
|
|
+ echo "==== 缓存信息 ===="
|
|
|
+ du -sh ${WORKSPACE}/.m2/repository 2>/dev/null || echo "缓存: 首次构建"
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ stage('代码检查') {
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 编译 Common 模块..."
|
|
|
+ sh '''
|
|
|
+ mvn clean package -pl shop-recycle-common \
|
|
|
+ -DskipTests \
|
|
|
+ -B \
|
|
|
+ -Dmaven.test.skip=true \
|
|
|
+ || { echo "❌ Common 模块构建失败"; exit 1; }
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ stage('编译与测试') {
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 执行 Maven 编译..."
|
|
|
+
|
|
|
+ def testArgs = SKIP_TESTS == 'true' ? '-DskipTests' : ''
|
|
|
+
|
|
|
+ sh """
|
|
|
+ mvn package \
|
|
|
+ ${testArgs} \
|
|
|
+ -B \
|
|
|
+ -Dmaven.test.skip=${SKIP_TESTS} \
|
|
|
+ -Ddocker.build.number=${BUILD_NUMBER_PADDED} \
|
|
|
+ -Dgit.commit=${GIT_COMMIT_SHORT}
|
|
|
+ """
|
|
|
+ }
|
|
|
+ }
|
|
|
+ post {
|
|
|
+ always {
|
|
|
+ // 收集测试结果
|
|
|
+ junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
|
|
|
+ }
|
|
|
+ failure {
|
|
|
+ script {
|
|
|
+ echo "❌ 编译失败!"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ stage('构建Docker镜像') {
|
|
|
+ // ✅ 关键优化:并行构建 3 个服务
|
|
|
+ parallel {
|
|
|
+ stage('Gateway') {
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 构建 Gateway 镜像: ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG}"
|
|
|
+ sh '''
|
|
|
+ docker build \
|
|
|
+ --build-arg BUILD_NUMBER=${BUILD_NUMBER_PADDED} \
|
|
|
+ --build-arg GIT_COMMIT=${GIT_COMMIT_SHORT} \
|
|
|
+ --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:latest \
|
|
|
+ --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${APP_VERSION} \
|
|
|
+ -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG} \
|
|
|
+ -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:latest \
|
|
|
+ -f shop-recycle-gateway/Dockerfile \
|
|
|
+ .
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stage('OrderService') {
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 构建 OrderService 镜像: ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG}"
|
|
|
+ sh '''
|
|
|
+ docker build \
|
|
|
+ --build-arg BUILD_NUMBER=${BUILD_NUMBER_PADDED} \
|
|
|
+ --build-arg GIT_COMMIT=${GIT_COMMIT_SHORT} \
|
|
|
+ --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:latest \
|
|
|
+ --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${APP_VERSION} \
|
|
|
+ -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG} \
|
|
|
+ -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:latest \
|
|
|
+ -f shop-recycle-order-service/Dockerfile \
|
|
|
+ .
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ stage('PaymentService') {
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 构建 PaymentService 镜像: ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG}"
|
|
|
+ sh '''
|
|
|
+ docker build \
|
|
|
+ --build-arg BUILD_NUMBER=${BUILD_NUMBER_PADDED} \
|
|
|
+ --build-arg GIT_COMMIT=${GIT_COMMIT_SHORT} \
|
|
|
+ --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:latest \
|
|
|
+ --cache-from ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${APP_VERSION} \
|
|
|
+ -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG} \
|
|
|
+ -t ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:latest \
|
|
|
+ -f shop-recycle-payment-service/Dockerfile \
|
|
|
+ .
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ stage('镜像安全扫描') {
|
|
|
+ when {
|
|
|
+ expression { PUSH_DOCKER == 'true' }
|
|
|
+ }
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 执行镜像安全扫描..."
|
|
|
+ sh '''
|
|
|
+ # 使用 trivy 进行镜像扫描(如果安装)
|
|
|
+ if command -v trivy &> /dev/null; then
|
|
|
+ echo "扫描镜像漏洞..."
|
|
|
+ trivy image --severity HIGH,CRITICAL \
|
|
|
+ ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG} || true
|
|
|
+ else
|
|
|
+ echo "⚠️ Trivy 未安装,跳过镜像扫描"
|
|
|
+ fi
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ stage('推送Docker镜像') {
|
|
|
+ when {
|
|
|
+ expression { PUSH_DOCKER == 'true' }
|
|
|
+ }
|
|
|
+ steps {
|
|
|
+ script {
|
|
|
+ echo ">>> 推送 Docker 镜像到 ${DOCKER_REGISTRY}..."
|
|
|
+
|
|
|
+ withCredentials([usernamePassword(
|
|
|
+ credentialsId: 'docker-registry-credentials',
|
|
|
+ usernameVariable: 'DOCKER_USER',
|
|
|
+ passwordVariable: 'DOCKER_PASS'
|
|
|
+ )]) {
|
|
|
+ sh '''
|
|
|
+ echo "${DOCKER_PASS}" | docker login -u "${DOCKER_USER}" --password-stdin ${DOCKER_REGISTRY}
|
|
|
+
|
|
|
+ # ✅ 优化:并行推送镜像(快 3 倍)
|
|
|
+ docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG} &
|
|
|
+ docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:latest &
|
|
|
+
|
|
|
+ docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG} &
|
|
|
+ docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:latest &
|
|
|
+
|
|
|
+ docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG} &
|
|
|
+ docker push ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:latest &
|
|
|
+
|
|
|
+ wait
|
|
|
+
|
|
|
+ docker logout ${DOCKER_REGISTRY}
|
|
|
+
|
|
|
+ echo "✅ 镜像推送完成"
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ post {
|
|
|
+ always {
|
|
|
+ script {
|
|
|
+ echo ">>> 清理与报告..."
|
|
|
+
|
|
|
+ // 收集构建工件
|
|
|
+ archiveArtifacts artifacts: '**/target/*.jar',
|
|
|
+ allowEmptyArchive: true,
|
|
|
+ fingerprint: true
|
|
|
+
|
|
|
+ sh '''
|
|
|
+ echo "═══════════════════════════════════════"
|
|
|
+ echo "CI 构建完成"
|
|
|
+ echo "═══════════════════════════════════════"
|
|
|
+ echo "镜像TAG: ${IMAGE_TAG}"
|
|
|
+ docker images | grep shoprecycle | head -6 || true
|
|
|
+
|
|
|
+ echo ""
|
|
|
+ echo "缓存大小:"
|
|
|
+ du -sh ${WORKSPACE}/.m2/repository 2>/dev/null || echo "Unknown"
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ success {
|
|
|
+ script {
|
|
|
+ echo "✅ CI 构建成功!"
|
|
|
+ echo "镜像已准备就绪:"
|
|
|
+ echo " - ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/gateway:${IMAGE_TAG}"
|
|
|
+ echo " - ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/order-service:${IMAGE_TAG}"
|
|
|
+ echo " - ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/payment-service:${IMAGE_TAG}"
|
|
|
+ }
|
|
|
+ }
|
|
|
+ failure {
|
|
|
+ script {
|
|
|
+ echo "❌ CI 构建失败,请检查日志"
|
|
|
+ sh '''
|
|
|
+ # 清理失败的镜像
|
|
|
+ docker images | grep shoprecycle | grep -E "^<none>" | awk '{print $3}' | xargs -r docker rmi -f || true
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cleanup {
|
|
|
+ sh '''
|
|
|
+ # ✅ 保留 Maven 缓存用于下次构建(关键优化)
|
|
|
+ echo "缓存已保留,下次构建会使用"
|
|
|
+
|
|
|
+ # 清理 1 周前的临时文件(可选)
|
|
|
+ find ${WORKSPACE} -type f -name "*.log" -mtime +7 -delete 2>/dev/null || true
|
|
|
+ '''
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|