> 技术文档 > Jenkins 配置gitlab的 pipeline流水线任务_gitlab pipeline

Jenkins 配置gitlab的 pipeline流水线任务_gitlab pipeline


配置jenkins到gitlab仓库权限

1,jenkins拉取代码的3种方式

需要拉取gitlab仓库的权限。这里有三种方法,按环境自己选择。

  • 1, 在gitlab创建专属Jenkins的账号密码。最简单,但是不怎么安全。
  • 2,配置 Jenkins 的 SSH 公钥到 GitLab。主要用于 git 命令拉取 git@gitlab.com:… 仓库。安全
  • 3, 配置 GitLab Personal Access Token(PAT)到 Jenkins。HTTPS 访问 GitLab API。

在jenkins只拉取仓库的话,推荐第二种。安全,没有时间限制,不暴露其他信息。

注意这种方式,拉取代码仓库,只能用git用户 命令拉取git仓库名,如:git@198.19.249.92:project-1/xxl-job.git。

2,配置SSH密钥公钥

Jenkins >>. 系统管理 >> 凭证管理 >> 全局 >> add credentials

gitlab ssh key添加jenkins对应的公钥即可。

jenkins 全局工具配置

java项目,主要两个工具,jdk和maven。

1,JDK

jdk可以下载指定版本放在服务器,解压后,指定文件夹即可。

也可以选择自动安装,触发java命令就会自动安装。

2,maven

maven也可以下载配置,会自动安装。

小坑,创建 自由项目,并不会自动安装。maven任务 和pipline任务,测试可以自动安装。

流水线pipeline任务

这里以xxl job 项目测试,这个也是java项目,可以练练手。先将源码上传至gitlab仓库。

项目地址:https://github.com/xuxueli/xxl-job/

1,Pipeline script 基础语法

流水线,只需要这一个文件即可,相当于界面配置换成命令配置。

基础语法

pipeline {

    agent any

    stages {

        stage(\'Hello\') {

            steps {

                echo \'Hello World\'

            }

        }

    }

}

pipeline 定义一个流水线脚本

agent 指示 Jenkins 为整个流水线分配一个执行器(在 Jenkins 环境中的任何可用代理/节点上)和工作区。

stages 全部的工作都在这里执行

stage 每个工作开始

steps jenkinsfile 声明式脚本往这里面写

echo 写一个简单的字符串到控制台输出。

默认提供了示例,可自行参考。“流水线语法” 可以自动生成命令。如下,自动生成了,拉取代码仓库命令。

2,xxl job 构建部署的pipeline文件

四个步骤,拉取代码 >> 构建 >> 复制拷贝 >> 远程运行。

environment 环境参数,主要是服务器相关信息。jar包位置根据安装的方式可能有所不一样,注意一下。

tools 需要用到的工具,配置好了会自动调用或安装。

拉取代码 :git拉取

构建命令: mvn clean package -Dmaven.test.skip=true

拷贝: rsync命令拷贝

运行:远程运行服务器的start.sh脚本启动服务。

pipeline {

    agent any

    environment {

        APP_NAME = \'xxl-job-admin\'

        BUILD_NAME = \'xxl_job_pip\'

        REMOTE_IP_LIST = \'198.19.249.107\' //支持多个IP集群

        PORT = \'8080\'

        REMOTE_DIR = \"/app/${APP_NAME}\"

        JAR_PATTERN = \"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/${APP_NAME}-*.jar\"

    }

    tools {

        maven \"maven-3.9.6\"

        jdk \'jdk17\'

    }

    stages {

        stage(\'gitlab代码拉取\') {

            steps {

                git branch: \'dev\', credentialsId: \'25238080-093d-4e3c-884c-f70a027eb1c9\', url: \'git@198.19.249.92:project-1/xxl-job.git\'

                echo \'拉取dev分支成功\'

            }

        

        stage(\'mvn执行构建\') {

            steps {

                sh \"mvn -v\"

                sh \"java --version\"

                sh \"mvn clean package -Dmaven.test.skip=true\"

                echo \"构建完成\"

            }

        }

        stage(\'拷贝 JAR 到远程服务器\') {

            steps {

                script {

                    def jarFile = sh(script: \"ls ${JAR_PATTERN} 2>/dev/null | head -n 1\", returnStdout: true).trim()

                    if (!jarFile) {

                        error(\"未找到 JAR 包: ${JAR_PATTERN}\")

                    }

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"==> 拷贝到远程 IP: ${ip}\"

                        // 检查并创建目录

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'

                        \"\"\"

                        // 拷贝 jar 包

                        sh \"\"\"

                            rsync -az ${jarFile} root@${ip}:${REMOTE_DIR}/

                        \"\"\"

                    }

                    echo \"拷贝完成\"

                }

            }

        }

        stage(\'远程部署服务\') {

            steps {

                script {

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"==> 部署到远程 IP: ${ip}\"

                        // 生成并执行远程启动命令

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'

                        \"\"\"

                    }

                }

                echo \"服务部署完成\"

            }

        }

    }

}

流水线编写完成后,并不能马上构建,需要Approve script,同意脚本后才能生效,相当于多了个审核步骤。

3,构建pipeline

构建完成后,可以在Pipeline看到每个步骤命令,从而更精确的排查问题。

构建完成,也可以选择其中一个步骤重新运行,更加灵活

扩展

1,参数化选择部署模式

虽然pipeline流水构建完成后,可以选择其中一个步骤重新执行。但是如果想简单点,构建的时候就选择,比较方便。就用到参数化选择。

需求:可以把 DEPLOY_MODE 扩展为 3 种模式:

  • build_and_deploy:构建 + 拷贝 + 部署(完整流程)
  • only_deploy:仅拷贝 + 部署(跳过构建)
  • only_build:仅构建(不部署)

修改后流程如下(以xxl job演示为例) :

pipeline {

    agent any

    parameters {

        choice(

            name: \'DEPLOY_MODE\',

            choices: [\'build_and_deploy\', \'only_deploy\', \'only_build\'],

            description: \'选择执行模式\'

        )

    }

    environment {

        APP_NAME = \'xxl-job-admin\'

        BUILD_NAME = \'xxl_job_pip\'

        REMOTE_IP_LIST = \'198.19.249.107\'

        PORT = \'8080\'

        REMOTE_DIR = \"/app/${APP_NAME}\"

        JAR_PATTERN = \"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/${APP_NAME}-*.jar\"

    }

    tools {

        maven \"maven-3.9.6\"

        jdk \'jdk17\'

    }

    stages {

        stage(\'gitlab代码拉取\') {

            when {

                expression { params.DEPLOY_MODE != \'only_deploy\' }

            }

            steps {

                git branch: \'dev\', credentialsId: \'25238080-093d-4e3c-884c-f70a027eb1c9\', url: \'git@198.19.249.92:project-1/xxl-job.git\'

                echo \'拉取dev分支成功\'

            }

        

        stage(\'mvn执行构建\') {

            when {

                expression { params.DEPLOY_MODE != \'only_deploy\' }

            }

            steps {

                sh \"mvn -v\"

                sh \"java --version\"

                sh \"mvn clean package -Dmaven.test.skip=true\"

                echo \"构建完成\"

            }

        }

        stage(\'拷贝 JAR 到远程服务器\') {

            when {

                expression { params.DEPLOY_MODE != \'only_build\' }

            }

            steps {

                script {

                    def jarFile = sh(script: \"ls ${JAR_PATTERN} 2>/dev/null | head -n 1\", returnStdout: true).trim()

                    if (!jarFile) {

                        error(\"未找到 JAR 包: ${JAR_PATTERN}\")

                    }

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"==> 拷贝到远程 IP: ${ip}\"

                        // 检查并创建目录

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'

                        \"\"\"

                        // 拷贝 jar 包

                        sh \"\"\"

                            rsync -az ${jarFile} root@${ip}:${REMOTE_DIR}/

                        \"\"\"

                    }

                    echo \"拷贝完成\"

                }

            }

        }

        stage(\'远程部署服务\') {

            when {

                expression { params.DEPLOY_MODE == \'build_and_deploy\' || params.DEPLOY_MODE == \'only_deploy\' }

            }

            steps {

                script {

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"==> 部署到远程 IP: ${ip}\"

                        // 生成并执行远程启动命令

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'

                        \"\"\"

                    }

                }

                echo \"服务部署完成\"

            }

        }

    }

}

保存后,修改下描述就可以构建了

2,参数化选择部署环境

需求:手动选择分支(如 devtestmain),让每个分支构建时部署到不同服务器。

每个分支构建后部署到指定环境(如测试、预发布、生产)。

//定义全局变量,不能定义environment中,因为env变量的赋值是只读的,scripe块中修改env值不会生效。

def REMOTE_IP_LIST = \'\'

pipeline {

    agent any

    parameters {

        choice(name: \'BRANCH_NAME\', choices: [\'dev\', \'test\', \'main\'], description: \'请选择构建分支\')

    }

    environment {

        APP_NAME = \'xxl-job-admin\'

        BUILD_NAME = \'xxl_job_pip\'

        PORT = \'8080\'

        REMOTE_DIR = \"/app/${APP_NAME}\"

        JAR_PATTERN = \"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/${APP_NAME}-*.jar\"

        // 各环境服务器 IP(后期只改这里)

        DEV_IPS = \'198.19.249.107\'

        TEST_IPS = \'198.19.249.102\'

        MAIN_IPS = \'198.19.249.103 198.19.249.104\'

    }

    tools {

        maven \"maven-3.9.6\"

        jdk \'jdk17\'

    }

    stages {

        stage(\'设置部署服务器\') {

            steps {

                script {

                    switch (params.BRANCH_NAME) {

                        case \'dev\':

                            REMOTE_IP_LIST = env.DEV_IPS

                            break

                        case \'test\':

                            REMOTE_IP_LIST = env.TEST_IPS

                            break

                        case \'main\':

                            REMOTE_IP_LIST = env.MAIN_IPS

                            break

                        default:

                            error \"未知分支:${params.BRANCH_NAME}\"

                    }

                    echo \"当前分支:${params.BRANCH_NAME}\"

                    echo \"部署服务器:${REMOTE_IP_LIST}\"

                }

            }

        }

        stage(\'gitlab代码拉取\') {

            steps {

                git branch: \"${params.BRANCH_NAME}\", credentialsId: \'25238080-093d-4e3c-884c-f70a027eb1c9\', url: \'git@198.19.249.92:project-1/xxl-job.git\'

                echo \'拉取dev分支成功\'

            }

        

        stage(\'mvn执行构建\') {

            steps {

                sh \"mvn -v\"

                sh \"java --version\"

                sh \"mvn clean package -Dmaven.test.skip=true\"

                echo \"构建完成\"

            }

        }

        stage(\'拷贝 JAR 到远程服务器\') {

            steps {

                script {

                    def jarFile = sh(script: \"ls ${JAR_PATTERN} 2>/dev/null | head -n 1\", returnStdout: true).trim()

                    if (!jarFile) {

                        error(\"未找到 JAR 包: ${JAR_PATTERN}\")

                    }

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"==> 拷贝到远程 IP: ${ip}\"

                        // 检查并创建目录

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'

                        \"\"\"

                        // 拷贝 jar 包

                        sh \"\"\"

                            rsync -az ${jarFile} root@${ip}:${REMOTE_DIR}/

                        \"\"\"

                    }

                    echo \"拷贝完成\"

                }

            }

        }

        stage(\'远程部署服务\') {

            steps {

                script {

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"==> 部署到远程 IP: ${ip}\"

                        // 生成并执行远程启动命令,根据脚本选择参数,我个人脚本运行需要传2个参数

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'

                        \"\"\"

                    }

                }

                echo \"服务部署完成\"

            }

        }

    }

}

3,版本控制选择发布

对于以往打包过的jar 保存好,然后动态获取所有的包,选择发布。

Active Choices Plugin 是在 Jenkins 中动态生成参数(如版本号列表)最推荐、最灵活的方式之一,特别适合“从已构建产物目录中选择版本进行部署”的场景。

系统管理 >> 插件管理 >> Active Choices Plugin 安装即可。

在任务配置“参数化构建过程”,多出三个参数:

参数类型 用途 典型表现形式 Active Choices Parameter 独立生成选项 如动态下拉框,构建版本号列表 Active Choices Reactive Parameter 依赖其他参数生成选项 如根据“分支”选择对应“模块”列表 Active Choices Reactive Referenced Parameter 依赖其他参数生成一段 只读文本(非选项) 如动态提示用户选择的版本描述或告警

一般用得多的是第1个和第2个。

不需要根据其他参数来匹配就用第1个,参数化选择Active Choices Parameter,然后Groovy script 写脚本即可。

示例Groovy脚本:

#获取目标文件夹的jar包,并以版本号,只显示数字:3.1.0

def dir = new File(\"/var/lib/jenkins/workspace/xxl_job_pip/xxl-job-admin/target\")

if (!dir.exists()) return [\"无可用版本\"]

def files = dir.listFiles().findAll { it.name.endsWith(\".jar\") }

def versions = files.collect { it.name.replaceAll(\"xxl-job-admin-(.*)\\\\.jar\", \"\\$1\") }

return versions.sort().reverse()

#获取目标文件夹的jar包,并以整个包名显示:xxl-job-admin-3.1.0.jar

def dir = new File(\"/var/lib/jenkins/workspace/xxl_job_pip/xxl-job-admin/target\")

if (!dir.exists()) return [\"无可用版本\"]

def files = dir.listFiles().findAll { it.name.endsWith(\".jar\") }

def filenames = files*.name

return filenames.sort().reverse()

这里介绍主要使用第二个参数Active Choices Reactive Parameter

需求:根据其他参数选择显示版本号。

1 :选择“全新构建发布”,显示 “不适用”或者“空”

2:选择“以往版本发布”,显示所有包名。

添加一个普通 选项参数

名称:RELEASE_TYPE,选项:全新构建发布 以往版本发布

添加另一个Active Choices Reactive Parameter参数:

name: APP_VERSION

Script: Groovy Script

// 判断是否为“以往版本发布”

if (RELEASE_TYPE != \"以往版本发布\") {

    return [\"不适用\"]

}

// 读取存储目录下的所有 jar 文件

def dir = new File(\"/opt/jar/xxl-job-admin\")

if (!dir.exists()) return [\"目录不存在\"]

def files = dir.listFiles().findAll { it.name.endsWith(\".jar\") }

if (files.isEmpty()) return [\"无可用版本\"]

return files*.name.sort().reverse()

Fallback Script ,只有在脚本执行失败或者返回的类型不是上面提到的类型时执行,可以填可以空着。

Choice Type: Single Select (单选即可)

Referenced Parameters:填写 RELEASE_TYPE(关键!表示依赖此参数)

配置完成后,可以选择 Build with Parameters,查看是否显示成功.

Pipeline script :

最新流程为: 部署环境 >> Git 拉取代码 >> Maven 构建 >> 存储备份 JAR 包 >> 拷贝 JAR 到远程服务器 >> 远程启动服务

相对比前面的脚本,增加了 存储备份 步骤,备份以时间戳重命名(如果构建版本带版本号,可以不要时间戳)。

将构建的包复制到指定文件夹,都从此文件夹部署相应的包至服务器。

def REMOTE_IP_LIST = \'\'

pipeline {

    agent any

    parameters {

        choice(name: \'BRANCH_NAME\', choices: [\'dev\', \'test\', \'main\'], description: \'请选择构建分支\')

        choice(name: \'RELEASE_TYPE\', choices: [\'全新构建发布\', \'以往版本发布\'], description: \'发布方式\')

    }

    environment {

        APP_NAME = \'xxl-job-admin\'

        BUILD_NAME = \'xxl_job_pip\'

        PORT = \'8080\'

        JAR_PATTERN = \"${JENKINS_HOME}/workspace/${BUILD_NAME}/${APP_NAME}/target/\"

        JAR_PATH = \"/opt/jar/${APP_NAME}\"

        REMOTE_DIR = \"/app/${APP_NAME}\"

        DEV_IPS = \'198.19.249.107\'

        TEST_IPS = \'198.19.249.102\'

        MAIN_IPS = \'198.19.249.103 198.19.249.104\'

    }

    tools {

        maven \"maven-3.9.6\"

        jdk \'jdk17\'

    }

    stages {

        stage(\'部署环境\') {

            steps {

                script {

                    switch (params.BRANCH_NAME) {

                        case \'dev\':  REMOTE_IP_LIST = env.DEV_IPS; break

                        case \'test\': REMOTE_IP_LIST = env.TEST_IPS; break

                        case \'main\': REMOTE_IP_LIST = env.MAIN_IPS; break

                        default:     error \"未知分支:${params.BRANCH_NAME}\"

                    }

                    echo \"部署环境分支:${params.BRANCH_NAME}\"

                    echo \"部署方式:${params.RELEASE_TYPE}\"

                    if (params.RELEASE_TYPE != \'全新构建发布\') {

                        echo \"部署版本包: ${params.APP_VERSION}\"

                    }

                    echo \"部署服务器:${REMOTE_IP_LIST}\"

                }

            }

        }

        stage(\'Git 拉取代码\') {

            when {

                expression { return !params.APP_VERSION } // 如果是空,表示全新构建

            }

            steps {

                git branch: \"${params.BRANCH_NAME}\",

                    credentialsId: \'25238080-093d-4e3c-884c-f70a027eb1c9\',

                    url: \'git@198.19.249.92:project-1/xxl-job.git\'

                echo \"拉取 ${params.BRANCH_NAME} 分支成功\"

            }

        }

        stage(\'Maven 构建\') {

            when {

                expression { return !params.APP_VERSION } // 只在构建新版本时执行

            }

            steps {

                sh \"mvn -v\"

                sh \"java --version\"

                sh \"mvn clean package -Dmaven.test.skip=true\"

                echo \"构建完成\"

            }

        }

        stage(\'存储备份 JAR 包\') {

            when {

                expression { return !params.APP_VERSION } // 只在构建新版本时执行

            }

            steps {

                script {

                    def regex = \"^${env.APP_NAME}(-[0-9.]+)?\\\\.jar\\$\"

                    def jarFile = sh(

                        script: \"ls -t ${env.JAR_PATTERN} | grep -E \'${regex}\' | head -n 1\",

                        returnStdout: true

                    ).trim()

                    if (!jarFile) {

                        error(\"未找到构建生成的 jar 包\")

                    }

                    // 获取当前时间戳

                    def timestamp = sh(script: \"date +%Y%m%d%H%M\", returnStdout: true).trim()

                    // 生成带时间的备份文件名

                    def newFileName = jarFile.replace(\".jar\", \"-${timestamp}.jar\")

                    sh \"\"\"

                        mkdir -p ${env.JAR_PATH}

                        cp ${env.JAR_PATTERN}${jarFile} ${env.JAR_PATH}/${newFileName}

                        ls -tp ${env.JAR_PATH}/*.jar | grep -v \'/\\$\' | tail -n +11 | xargs -r rm --

                    \"\"\"

                    echo \"备份完成,保留最新 10 个版本:${jarFile}\"

                }

            }

        }

        stage(\'拷贝 JAR 到远程服务器\') {

            steps {

                script {

                    def jarFileName

                    def jarFilePath

                    if (params.APP_VERSION?.trim()) {

                        // 指定了版本:用选定的版本文件名

                        jarFileName = params.APP_VERSION.trim()

                        jarFilePath = \"${env.JAR_PATH}/${jarFileName}\"

                    } else {

                        // 未指定版本:自动找最新的

                        jarFileName = sh(script: \"ls -t ${env.JAR_PATH}/*.jar | head -n 1\", returnStdout: true).trim().tokenize(\"/\").last()

                        jarFilePath = \"${env.JAR_PATH}/${jarFileName}\"

                    }

                    if (!jarFileName || !fileExists(jarFilePath)) {

                        error(\"未找到 JAR 包用于部署: ${jarFilePath}\")

                    }

                    echo \"部署使用 JAR 包:${jarFilePath}\"

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"拷贝到远程 IP: ${ip}\"

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'mkdir -p ${REMOTE_DIR} /app/logs /app/shell\'

                            rsync -az ${jarFilePath} root@${ip}:${REMOTE_DIR}/

                        \"\"\"

                    }

                }

            }

        }

        stage(\'远程启动服务\') {

            steps {

                script {

                    for (ip in REMOTE_IP_LIST.split()) {

                        echo \"部署到远程 IP: ${ip}\"

                        //

                        sh \"\"\"

                            ssh -o StrictHostKeyChecking=no root@${ip} \'/bin/bash /app/shell/start.sh ${APP_NAME} ${PORT}\'

                        \"\"\"

                    }

                }

                echo \"${env.APP_NAME}服务部署完成\"

            }

        }

    }

}

百科名医视频网