jenkins Pipeline 企业级别的流水线构建 Docker 负责打包 最后上传到Nexus

全程使用docker构建,上文提供的案例不能说是企业级的因为还是在宿主机上面构建的,依赖性强,利用 Docker 插件或 K8s Agent,为每个 Stage 动态拉起一个包含所需工具的容器(Pod)。构建完即销毁。

优势:环境绝对干净,构建一致性高,不会因为"我本地是好的"这种问题扯皮。

当我们前后端仓库是分开的,那么企业级的构建流程是什么呢?

答案:子目录隔离(推荐)

为什么子目录隔离是企业级?

在工作空间下创建 frontend/backend/ 文件夹,分别存放代码。

  1. 物理隔离,逻辑统一
    • 虽然它们在同一个 Jenkins 工作空间(Workspace)下,但互不干扰。
    • 前端构建产生的 node_modules 和后端的 target 目录完全分开,不会误删或混淆。
  2. 符合微服务/模块化思维
    • 企业级项目通常是模块化的。即使现在是前后端分离,未来可能变成多后端服务。子目录结构清晰地表达了"这是两个独立的组件"。
  3. Docker 挂载更清晰
    • 当启动 Docker 容器时,容器内的 /workspace 对应宿主机的根目录。
    • 在容器内执行 cd backend && mvn ... 非常直观,不需要去猜测当前目录下到底有哪些文件。

这是结合了 Docker 构建Nexus 上传子目录隔离 的最终版本(一)。

bash 复制代码
pipeline {
    // 全局代理:使用 Jenkins Slave 节点
    agent {
        node {
            label 'jenkins-slave'
            customWorkspace '/home/jenkins/docker-all-nexus'
        }
    }

    // 全局选项
    options {
        // 在控制台输出时间戳,方便排查耗时
        timestamps()
        // 构建超时时间
        timeout(time: 1, unit: 'HOURS')
        // 保留最近 10 次构建记录
        buildDiscarder(logRotator(numToKeepStr: '10'))
        // 禁止并发构建,防止资源冲突
        disableConcurrentBuilds()
    }

    // 环境变量
    environment {
        // --- Nexus 配置 ---
        NEXUS_CREDENTIALS_ID = 'nexus-deploy-creds'
        // 注意:这里只写 IP:PORT,协议单独配置
        NEXUS_TOOL_URL = '172.20.10.6:8081'
        NEXUS_PROTOCOL = 'http'
        NEXUS_MAVEN_REPO = 'app-backend'
        NEXUS_RAW_REPO = 'app-frontend-raw'

        // --- Git 配置 ---
        FRONTEND_GIT_URL = 'git@gitee.com:testpm/frontend-docker.git'
        BACKEND_GIT_URL  = 'git@gitee.com:testpm/backend-docker.git'
        GIT_CREDENTIALS_ID = 'jenkins-slave-git'

        // --- 镜像配置 ---
        NODE_IMAGE = 'node:16'
        // 使用具体的 Maven 版本
        MAVEN_IMAGE = 'maven:3.9.9-eclipse-temurin-21'

        // --- 版本控制 ---
        // 使用构建号作为版本后缀,确保唯一性
        BUILD_VERSION = "${BUILD_NUMBER}"
        
        // --- 构建产物路径 实际工作中记得修改为自己的路径和包名---
        BACKEND_JAR_PATH = 'backend/target/*.jar'
    }

    stages {
        // ================== 阶段 1: 代码拉取 ==================
        stage('📥 代码拉取') {
            steps {
                script {
                    echo "🚀 开始拉取代码..."
                    
                    // 1. 拉取后端代码到 backend 目录
                    dir('backend') {
                        // 确保目录干净
                        deleteDir()
                        echo "📦 正在拉取后端代码..."
                        git branch: 'master',
                            credentialsId: "${GIT_CREDENTIALS_ID}",
                            url: "${BACKEND_GIT_URL}"
                    }

                    // 2. 拉取前端代码到 frontend 目录
                    dir('frontend') {
                        deleteDir()
                        echo "📦 正在拉取前端代码..."
                        git branch: 'master',
                            credentialsId: "${GIT_CREDENTIALS_ID}",
                            url: "${FRONTEND_GIT_URL}"
                    }
                }
            }
        }

        // ================== 阶段 2: 后端构建 (Maven) ==================
        stage('🔨 后端构建') {
            steps {
                script {
                    // 增加重试机制:如果Maven下载依赖超时,自动重试最多3次
                    retry(3) {
                    //使用 inside 语法,强制指定工作目录
                    docker.image("${MAVEN_IMAGE}").inside("-u root:root -w ${WORKSPACE} -v /data/maven-cache:/root/.m2 --entrypoint=''") {
                        // 进入 backend 目录执行构建
                        dir('backend') {
                            sh '''
                                echo "🚀 当前容器内路径: $(pwd)"
                                echo "🚀 开始 Maven 编译..."
                                mvn clean package -DskipTests -B -U
                            '''
                        }
                    }
                }
                    
                    // 将验证和暂存移出 docker.inside 块(在 Jenkins 节点上执行)
                    def jarFiles = findFiles(glob: "${env.BACKEND_JAR_PATH}")
                    if (jarFiles == null || jarFiles.length == 0) {
                        error "❌ 后端构建失败:未找到JAR文件"
                    }
                    // 构建完成后,将产物存入暂存区,同时保存pom文件,方便上传nexus时可以读到pom文件中的版本/id等信息
                    stash name: 'backend-artifact', includes: 'backend/target/*.jar, backend/pom.xml'
                }
            }
        }

        // ================== 阶段 3: 前端构建 (Node.js) ==================
        stage('🎨 前端构建') {
            steps {
                script {
                    // 增加重试机制:防止 npm install 网络波动
                    retry(3) {
                    //使用 inside 语法,强制指定工作目录,容器内执行npm打包命令
                    docker.image("${NODE_IMAGE}").inside("-u root:root -w ${WORKSPACE} -v /data/npm-cache:/root/.npm --entrypoint=''") {
                        // 进入frontend 目录
                        dir('frontend') {
                            sh '''
                                echo "🚀 开始 NPM 编译..."
                                npm config set registry https://registry.npmmirror.com
                                export NODE_OPTIONS=--max_old_space_size=1536
                                npm install --legacy-peer-deps
                                npm run build
                            '''
                        } // dir 块结束
                    } // docker.inside 块结束
                } //retry(3) 块结束

                    // 2. 宿主机 (Jenkins Slave):打包文件
                    // 直接使用默认源安装 zip
                    dir('frontend') {
                        sh '''
                            # 安装 zip 工具
                            # apt-get update && apt-get install -y zip
                            # yum install -y zip
                            
                            # 压缩构建产物
                            zip -r frontend-dist-${BUILD_VERSION}.zip dist/
                        '''
                    }

                    // 3. 验证并暂存
                    // 直接构造正确的文件路径,而不是使用环境变量
                    def expectedZipPath = "frontend/frontend-dist-${BUILD_VERSION}.zip"
                    def zipFiles = findFiles(glob: expectedZipPath)
                    
                    if (zipFiles == null || zipFiles.length == 0) {
                        error "❌ 前端构建失败:未找到ZIP文件 ${expectedZipPath}"
                    }
                    // 暂存时也使用构造好的路径
                    stash name: 'frontend-artifact', includes: expectedZipPath
                }
            }
        }

        // ================== 阶段 4: 上传到 Nexus ==================
        stage('☁️ 上传制品到 Nexus') {
// 回到主节点执行上传,因为 Nexus 插件通常在 Master/Agent 上运行,而不是 Docker 里
            agent {
                node {
                    label 'jenkins-slave'
                }
            }
            steps {
                script {
                    echo "🚀 开始上传制品..."

                    // --- 1. 上传后端 ---
                    // 恢复后端制品
                    unstash 'backend-artifact'
                    // 读取 pom.xml 获取 GroupId 和 ArtifactId
                    def pom = readMavenPom file: 'backend/pom.xml'
                    // 找到生成的 jar 文件
                    def jarFile = findFiles(glob: 'backend/target/*.jar')[0]

                    nexusArtifactUploader(
                        nexusVersion: 'nexus3',
                        protocol: "${NEXUS_PROTOCOL}",
                        nexusUrl: "${NEXUS_TOOL_URL}", // 注意:插件版本不同可能是 toolUrl 或 nexusUrl
                        groupId: "${pom.groupId}",
                        version: "${pom.version}",
                        repository: "${NEXUS_MAVEN_REPO}",
                        credentialsId: "${NEXUS_CREDENTIALS_ID}",
                        artifacts: [[
                            artifactId: "${pom.artifactId}",
                            classifier: '',
                            file: "${jarFile.path}",
                            type: 'jar'
                        ]]
                    )

                    // --- 2. 上传前端 ---
                    // 恢复前端制品
                    unstash 'frontend-artifact'
                    def zipFile = findFiles(glob: "frontend/frontend-dist-${BUILD_VERSION}.zip")[0]

                    nexusArtifactUploader(
                        nexusVersion: 'nexus3',
                        protocol: "${NEXUS_PROTOCOL}",
                        nexusUrl: "${NEXUS_TOOL_URL}",
                        // 前端通常作为 Raw 类型上传,这里手动指定 GroupId
                        groupId: 'com.company.frontend',
                        version: "${BUILD_VERSION}",
                        repository: "${NEXUS_RAW_REPO}",
                        credentialsId: "${NEXUS_CREDENTIALS_ID}",
                        artifacts: [[
                            artifactId: 'frontend-dist',
                            classifier: '',
                            file: "${zipFile.path}",
                            type: 'zip'
                        ]]
                    )
                }
            }
        }
    }

    post {
        success {
            echo "✅ 构建及上传成功!"
        }
        failure {
            echo "❌ 构建失败,请检查日志。"
        }
        always {
            cleanWs()
        }
    }
}

这是结合了 Docker 构建Nexus 上传子目录隔离 的最终版本(二)。

压缩也放到容器内

ini 复制代码
pipeline {
    // 全局代理:使用 Jenkins Slave 节点
    agent {
        node {
            label 'jenkins-slave'
            customWorkspace '/home/jenkins/docker-all-nexus'
        }
    }

    // 全局选项
    options {
        // 在控制台输出时间戳,方便排查耗时
        timestamps()
        // 构建超时时间
        timeout(time: 1, unit: 'HOURS')
        // 保留最近 10 次构建记录
        buildDiscarder(logRotator(numToKeepStr: '10'))
        // 禁止并发构建,防止资源冲突
        disableConcurrentBuilds()
    }

    // 环境变量
    environment {
        // --- Nexus 配置 ---
        NEXUS_CREDENTIALS_ID = 'nexus-deploy-creds'
        // 注意:这里只写 IP:PORT,协议单独配置
        NEXUS_TOOL_URL = '172.20.10.6:8081'
        NEXUS_PROTOCOL = 'http'
        NEXUS_MAVEN_REPO = 'app-backend'
        NEXUS_RAW_REPO = 'app-frontend-raw'

        // --- Git 配置 ---
        FRONTEND_GIT_URL = 'git@gitee.com:testpm/frontend-docker.git'
        BACKEND_GIT_URL  = 'git@gitee.com:testpm/backend-docker.git'
        GIT_CREDENTIALS_ID = 'jenkins-slave-git'

        // --- 版本控制 ---
        // 使用构建号作为版本后缀,确保唯一性
        BUILD_VERSION = "${BUILD_NUMBER}"
        
        // --- 镜像配置 ---
        NODE_IMAGE = 'node:20'
        // 使用具体的 Maven 版本
        MAVEN_IMAGE = 'maven:3.9.9-eclipse-temurin-21'

        // 明确定义产物名称,避免路径拼接错误
        BACKEND_JAR_GLOB = 'backend/target/*.jar'
    }

    stages {

        // ================== 阶段 1: 代码拉取 (手动控制目录) ==================
        stage('📥 代码拉取') {
            steps {
                script {
                    echo "🚀 开始拉取代码..."

                    // 1. 拉取后端代码到 backend 目录
                    dir('backend') {
                        // 确保目录干净
                        deleteDir()
                        echo "正在拉取后端代码..."
                        git branch: 'master',
                            credentialsId: "${GIT_CREDENTIALS_ID}",
                            url: "${BACKEND_GIT_URL}"
                    }

                    // 2. 拉取前端代码到 frontend 目录
                    dir('frontend') {
                        deleteDir()
                        echo "正在拉取前端代码..."
                        git branch: 'master',
                            credentialsId: "${GIT_CREDENTIALS_ID}",
                            url: "${FRONTEND_GIT_URL}"
                    }
                }
            }
        }

        // ================== 阶段 2: 后端构建 (Maven) ==================
        stage('🔨 后端构建') {
            steps {
                script {
                    // 针对 Maven 的网络波动,单独加重试
                    retry(3) {
                    // 使用 inside 语法,强制指定工作目录
                    docker.image("${MAVEN_IMAGE}").inside("-u root:root -w ${WORKSPACE} -v /data/maven-cache:/root/.m2 --rm --entrypoint=''") {
                        // 进入 backend 目录执行构建
                        dir('backend') {
                                sh '''
                                    echo "🚀 当前容器内路径: $(pwd)"
                                    echo "🚀 开始 Maven 编译..."
                                    mvn clean package -DskipTests -B -U
                                '''
                            }
                        }
                    }
                
                // 验证产物
                def jars = findFiles(glob: "${BACKEND_JAR_GLOB}")
                if (!jars) error "❌ 后端构建失败:未找到 Jar 包"
                
                // 构建完成后,将产物存入暂存区 (加入构建号防止冲突)
                stash name: "backend-artifact-${BUILD_NUMBER}", includes: 'backend/target/*.jar, backend/pom.xml'
            }
        }
    }

        // ================== 阶段 3: 前端构建 (Node.js) ==================
        stage('🎨 前端构建') {
            steps {
                script {
                    // 使用 inside 语法,强制指定工作目录
                    docker.image("${NODE_IMAGE}").inside("-u root:root -w ${WORKSPACE} -v /data/npm-cache:/root/.npm --rm --entrypoint=''") {
                        dir('frontend') {
                            // 1. 下载依赖(网络不稳定,单独重试)
                            retry(3) {
                                sh '''
                                    echo "📦 安装依赖..."
                                    npm config set registry https://registry.npmmirror.com
                                    export NODE_OPTIONS=--max_old_space_size=1536
                                    npm install --legacy-peer-deps
                                '''
                            }
                            
                            // 2. 编译
                            sh '''
                                echo "🚀 开始编译..."
                                npm run build
                            '''
                            
                            // 3. 打包
                            sh '''
                                echo "📦 正在压缩前端产物..."
                                # 更新源并安装 zip
                                apt-get update || echo "警告: apt-get update 失败,尝试继续..."
                                apt-get install -y zip || echo "警告: zip 安装失败"
                                
                                # 打包
                                zip -r frontend-dist-${BUILD_VERSION}.zip dist/
                            '''
                        }
                    }
                }
                
                // 构建完成后,将产物存入暂存区
                stash name: "frontend-artifact-${BUILD_VERSION}", includes: "frontend/frontend-dist-${BUILD_VERSION}.zip"
            }
        }

        // ================== 阶段 4: 上传到 Nexus ==================
        stage('☁️ 上传制品到 Nexus') {
            // 回到主节点执行上传
            agent {
                node {
                    label 'jenkins-slave'
                }
            }
            steps {
                script {
                    echo "🚀 开始上传制品..."

                    // --- 1. 处理并上传后端 ---
                    echo "📦 上传后端 Jar..."
                    // 恢复后端制品 (注意名字要对应)
                    unstash "backend-artifact-${BUILD_NUMBER}"

                    // 读取 pom.xml 获取 GroupId 和 ArtifactId
                    def pom = readMavenPom file: 'backend/pom.xml'
                    // 找到生成的 jar 文件
                    def jarFile = findFiles(glob: 'backend/target/*.jar')[0]

                    nexusArtifactUploader(
                        nexusVersion: 'nexus3',
                        protocol: "${NEXUS_PROTOCOL}",
                        nexusUrl: "${NEXUS_TOOL_URL}",
                        groupId: "${pom.groupId}",
                        version: "${pom.version}",
                        repository: "${NEXUS_MAVEN_REPO}",
                        credentialsId: "${NEXUS_CREDENTIALS_ID}",
                        artifacts: [
                            [
                                artifactId: "${pom.artifactId}",
                                classifier: '',
                                file: "${jarFile.path}",
                                type: 'jar'
                            ]
                        ]
                    )

                    // --- 2. 处理并上传前端 ---
                    echo "📦 上传前端 Zip..."
                    // 恢复前端制品
                    unstash "frontend-artifact-${BUILD_VERSION}"

                    def zipFile = findFiles(glob: "frontend/frontend-dist-${BUILD_VERSION}.zip")[0]

                    nexusArtifactUploader(
                        nexusVersion: 'nexus3',
                        protocol: "${NEXUS_PROTOCOL}",
                        nexusUrl: "${NEXUS_TOOL_URL}",
                        // 前端通常作为 Raw 类型上传,这里手动指定 GroupId
                        groupId: 'com.company.frontend',
                        version: "${BUILD_VERSION}",
                        repository: "${NEXUS_RAW_REPO}",
                        credentialsId: "${NEXUS_CREDENTIALS_ID}",
                        artifacts: [
                            [
                                artifactId: 'frontend-dist',
                                classifier: '',
                                file: "${zipFile.path}",
                                type: 'zip'
                            ]
                        ]
                    )
                }
            }
        }
    }

    // 后置处理
    post {
        success {
            echo "✅ 构建及上传成功!"
        }
        failure {
            echo "❌ 构建失败,请检查日志。"
        }
        always {
            // 清理工作空间,防止磁盘爆满
            cleanWs()
        }
    }
}

未完待续

相关推荐
酷道11 小时前
CentOS 7 安装 Docker
linux·docker·centos
最后一个bug11 小时前
ubuntu24.04在docker下迁移gitlab16
linux·运维·docker
木雷坞11 小时前
csdn-enterpriseGitLab Runner docker pull 慢:并行流水线镜像拉取排查
运维·docker·容器·gitlab
Achou.Wang12 小时前
Docker 多阶段构建:优化 Go 应用镜像大小的最佳实践
elasticsearch·docker·golang
酷道12 小时前
获取Docker阿里云专属镜像加速地址
阿里云·docker·容器·云计算
x-cmd12 小时前
[260520] x-cmd v0.9.5:x install 支持 skill 安装,新增 git ci 命令让 AI 帮你写 commit
人工智能·git·ci/cd·agent·install·x-cmd
饭后一颗花生米12 小时前
2026年,Docker已死?Containerd、Podman与Nix的容器新战争
docker·容器·podman
认真的薛薛12 小时前
Linux运维:Jenkins+Argocd
linux·运维·jenkins
亚空间仓鼠13 小时前
Docker容器化高可用架构部署方案(十一)
android·docker·架构