Jenkins 流水線多種使用場景詳解(Jenkinsfile,多環境部署,多分支部署)

語言: CN / TW / HK

theme: smartblue

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 http://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 http://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 http://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宣告式語法詳解