Jenkins 流水线多种使用场景详解(Jenkinsfile,多环境部署,多分支部署)

4,700 阅读6分钟

Jenkins是目前大多数中小公司使用的CI、CD工具,其中Jenkins的任务又分普通任务和流水线任务,普通任务的构建和部署在我之前的一篇文章中写过使用教程# 基于 Docker 安装 Jenkins,并配置使用 Jenkins 打包 Node 前后端服务部署到远程服务器,但其中流水线任务可实现我们更复杂的需求也更自由,不过上手难度也稍微高点。

一、安装Jenkins

推荐使用 Docker 来安装Jenkins,更方便后期的迁移部署等,具体安装步骤可参考

# 基于 Docker 安装 Jenkins,并配置使用 Jenkins 打包 Node 前后端服务部署到远程服务器

二、普通流水线

这里我将演示使用流水线来部署一个前端项目,其他项目也同样是这几个步骤

image.png

首先创建一个流水线任务

image.png 在流水线配置这有两种方式,第一种是直接把流水线脚本写在配置文本框这,第二种是把脚本写在项目根目录下,用Jenkinsfile文件来写入,图下面可以看到有个流水线语法的按钮,是可以把具体操作用可视化的方式生成脚本。

我们在文本框这写入以下脚本内容:

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                nodejs('node16') {
                    sh '''
                        if hash pnpm 2>/dev/null; 
                        then
                            echo "pnpm"
                        else
                            npm i pnpm -g --registry https://registry.npmmirror.com/
                        fi

                        pnpm i
                        pnpm run build
                    '''
                }
                echo '构建完成'
            }
        }
        stage('Zip') {
            steps {
                sh '''
                    tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
                    rm -rf ./dist/*
                    mv ${JOB_BASE_NAME}.tgz ./dist
                '''
                echo '打包完成'
            }
        }
        stage('Deploy') {
            steps {
                sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
}

Stages: 这个字段下分了几个单独的stage,会从上至下依次执行stage,如果是刚才说的第一种方式,应该还会比上面多个拉取代码的阶段。具体拉取代码的语法可以用上面的流水线语法页面可视化生成。

stage('Build'): 代码构建阶段,这里因为是前端项目,用到了node来构建,需要安装NodeJS插件,然后去全局工具配置里安装一下具体的node版本及设置下别名。

stage('Zip'): 压缩阶段,因为我们前端代码部署只需要部署dist目录,把这个目录tgz压缩一下发到目标服务器。

stage('Deploy'): 部署阶段,需要安装一个Publish Over SSH插件,然后通过上面的流水线语法去可视化配置部署到服务器的配置,最后把生成的脚本粘贴到这就行。

到这我们构建及部署代码到服务器的基本配置就完成了,大部分项目其实发版流程就是这几步,下面还有几种流水线进阶用法。

三、多环境部署流水线

有时候我们会遇到多环境部署的情况,如开发坏境,生产环境等,大概就是我们通过在流水线添加一个部署坏境的参数来控制,在每次构建前选择一下部署的坏境,具体脚本如下:

pipeline {
    agent any

    parameters {
        choice(
            description: '你需要哪个机器进行部署?',
            name: 'deploy_hostname',
            choices: ['tencent', 'dev01', 'tencent、dev01']
        )
    }

    stages {
        stage('Build') {
            steps {
                nodejs('node16') {
                    sh '''
                        if hash pnpm 2>/dev/null; 
                        then
                            echo "pnpm"
                        else
                            npm i pnpm -g --registry https://registry.npmmirror.com/
                        fi

                        pnpm i
                        pnpm run build
                    '''
                }
                echo '构建完成'
            }
        }
        stage('Zip') {
            steps {
                sh '''
                    tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
                    rm -rf ./dist/*
                    mv ${JOB_BASE_NAME}.tgz ./dist
                '''
            }
        }
        stage('Deploy to tencent'){
            when {
                expression { deploy_hostname == 'tencent' }
            }
            steps{
                sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
        stage('Deploy to dev01'){
            when {
                expression { deploy_hostname == 'dev01' }
            }
            steps{
                sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
        stage('Deploy to tencent、dev01'){
            when {
                expression { deploy_hostname == 'tencent、dev01' }
            }
            steps{
                sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
}

可以看到除了我们之前配置的agentstages还多了一个parameters的参数配置,添加了一个deploy_hostname的选择参数,有三个值'tencent', 'dev01', 'tencent、dev01'

在具体的stage里面也多了when的配置,就是根据我们选择的部署环境参数来执行相应坏境的部署流程,当when里面的条件不满足时,流水线会跳过里面的steps

四、多分支流水线

还有种情况是项目多分支的情况下,每个分支可能对应的部署坏境,或者执行条件不一样,就会用到Jenkins的多分支流水线

image.png

在新建Jenkins任务时选择多分支流水线

image.png

在分支源里配置对应的git项目地址认证凭据,保存后他会自动扫描项目里面的分支,我们需要在每个分支下创建一个Jenkinsfile文件,把我们的脚本写在这个文件里

image.png

具体的构建部署脚本可参考之前的普通流水线,如果需要WebHook自动触发的可参考下面脚本

pipeline {
    agent any

    triggers {
        GenericTrigger (
            causeString: 'Triggered', 
            genericVariables: [[key: 'ref', value: '$.ref']], 
            printContributedVariables: true, 
            printPostContent: true, 
            token: 'test01'
        )
    }

    stages {
        stage('Build') {
            steps {
                nodejs('node16') {
                    sh '''
                        if hash pnpm 2>/dev/null; 
                        then
                            echo "pnpm"
                        else
                            npm i pnpm -g --registry https://registry.npmmirror.com/
                        fi

                        pnpm i
                        pnpm run build
                    '''
                }
                echo '构建完成'
            }
        }
        stage('Zip') {
            steps {
                sh '''
                    tar -zcvf ${JOB_BASE_NAME}.tgz ./dist/*
                    rm -rf ./dist/*
                    mv ${JOB_BASE_NAME}.tgz ./dist
                '''
            }
        }
        stage('Deploy to tencent'){
            when {
                branch 'master'
            }
            steps{
                sshPublisher(publishers: [sshPublisherDesc(configName: 'tencent', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
        stage('Deploy to dev01'){
            when {
                branch 'dev'
            }
            steps{
                sshPublisher(publishers: [sshPublisherDesc(configName: 'dev01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: 'echo \'部署完成\'', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }
}

可以看到上面添加了一个triggers配置,这个需要安装Generic Webhook Trigger插件,然后按上面那样配置,其中的token配置可以随意改动,就是我们最终触发hooks的url最后面的参数。

配置完成后,就可以通知http://JENKINS_URL/generic-webhook-trigger/invoke?token=test01来触发我们的hook,一般我们需要在gitlab的Webhooks进行配置触发hooks

image.png

到此Jenkins多种流水线的配置介绍就完成了,具体细节有不了解的小伙伴可在下面评论区留言。关于流水线语法每个配置的详解可参考# Jenkinsfile声明式语法详解