利用Jenkins Shared Libraries隱藏你的Jenkinsfile

什麼是shared libraries

Warning: 本文不是面對初學者的,若是你看不懂pipeline,那我也沒辦法.若是許多概念不明白的話,能夠點下面的官方文檔多看看.node

簡單的翻譯一下 官方文檔 的原話: 在多個流水線之間共享步驟以此來減小代碼的冗餘. git

那麼這究竟是什麼意思呢? 咱們知道當咱們使用聲明式的流水線的時候,必需要在項目的代碼倉庫中放一個 Jenkinsfile 文件, 當咱們項目愈來愈多的時候, Jenkinsfile也會愈來愈多,而後構建的過程也大同小異,這時候你就會發現一些問題:github

  • 每一個Jenkinsfile幾乎都長一個樣,代碼冗餘.
  • 項目太多,每一個Jenkinsfile都要跟着倉庫走,很差管理
  • Jenkinsfile是在倉庫裏的,也就意味着開發人員能夠更改,改錯了沒法進行構建了
  • 倉庫多分支時,每一個分支對應的發佈環境不同,Jenkinsfile也不同,開發合併分支時Jenkinfile會衝突

共享庫的最原始的用法就是解決代碼冗餘的問題的,那麼咱們來看一個比較官方的用法docker

文中的全部內容均在: https://github.com/jinyunboss/jenkins-libraries服務器


stage("配置共享庫")

  • 先整個倉庫按照規範創建目錄
# 基本的結構就是這樣的
.
├── src
│   └── com
│       └── lotbrick
│           └── jenkins
└── vars
  • 在vars目錄下新建個execShell.groovy文件
// vars/execShell.groovy

def call(params) {
    // 讓咱們來執行個echo命令
    sh "echo ${params}"
}
  • 將代碼傳到git,而後去jenkins配置全局共享庫

至於爲何是配置全局共享庫,請看這裏: https://www.jenkins.io/doc/book/pipeline/shared-libraries/#global-shared-librarieside

Jenkins > 系統配置 > Global Pipeline Libraries中添加一個共享庫,名爲 lotbrick函數

利用Jenkins Shared Libraries隱藏你的Jenkinsfile

stage(「第一個例子」)

  • 你的Jenkinsfile應該是這樣
@Library('lotbrick') _
pipeline {
    agent any
    stages {
        stage("第一步") {
            steps {
                script {
                    execShell("我是一個執行sh命令的例子")
                }
            }
        }
    }
}
  • 新建個多分支流水線的Job並運行

利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile

好像看起來這個例子並無怎麼簡化Jenkinsfile呀,沒事,來再看一個ui

stage(「第二個例子,來個input」)

  • 在vars目錄下新建個inputMsg.groovy文件
// vars/inputMsg.groovy

def call(params){
    try {
        input(
            id: "${params.id}", message: "${params.msg}"
        )
    } catch(err) { 
        // 獲取執行Input的用戶
        env.user = err.getCauses()[0].getUser().toString()
        userInput = false
        env.QATEST_TEST = false
        echo "Aborted by: [${user}]"
        // 拋出異常確保流程終止
        throw err
    } 
}
  • 這回Jenkinsfile變成了這樣
@Library('lotbrick') _
pipeline {
    agent any
    stages {
        stage("第一步") {
            steps {
                script {
                    execShell("我是一個執行sh命令的例子")
                }
            }
        }
        stage("第一步") {
            steps {
                script {
                    inputMsg([id:"input1",msg:"我是一個input步驟"])
                }
            }
        }
    }
}
  • 運行Job

利用Jenkins Shared Libraries隱藏你的Jenkinsfile

這回咱們獲得了一個input的步驟,雖然看起來也沒簡化多少,可是,咱這只是一個input,若是是比較長的執行ansible的步驟呢?
咱們來看一下調用ansible playbook須要的代碼this

ansiColor('xterm') {
    ansiblePlaybook(
        playbook: 'lotbrick.yml',
        inventory: 'production',
        disableHostKeyChecking: true,
        //inventoryContent: 'master',
        credentialsId: 'lotbrick-test1-46a8-91cd-185a08255a53',
        colorized: true,
    )
}

執行一個playbook就須要這些代碼,若是執行十個呢?量變就會引發質變,其餘的就要靠本身去發掘了.lua


stage("來個正經的腳本式pipeline")

剛纔咱們簡單的瞭解了一下共享庫是怎麼簡化Jenkinsfile的,那麼我們來點正經的.
官方給的例子基本都是腳本式的流水線,那我們也來一個正點的腳本式的流水線

  • 在vars目錄下新建個build.groovy文件,而後在build中寫pipeline
// vars/build.groovy

def call(params) {
    node {
        stage("拉一個代碼") {
            git url: "${params.git_url}"
        }
        stage("執行一個命令"){
            execShell("${params.cmd}")
        }
        stage("打印如今的時間"){
            sh "date"
        }
        stage("input步驟"){
            inputMsg([id:"input1",msg:"我是一個input步驟"])
        }
    }
}
  • Jenkinsfile
@Library('lotbrick') _
build([
    git_url:"https://github.com/jinyunboss/docker-compose.git",
    cmd:"ls -al"
    ])
  • 運行

利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile

stage("來個聲明式的pipeline")

上面咱們看到了腳本式的pipeline,固然聲明式的也能夠.來瞅瞅

  • 在vars目錄下新建個buildDeclarative.groovy文件,而後在build中寫pipeline
// vars/buildDeclarative.groovy

def call(params) {

pipeline {
    agent any
    options {
        // 禁止同時運行多個流水線
        disableConcurrentBuilds()
    }
    environment {
        examples_var1 = sh(script: 'echo "當前的時間是: `date`"', returnStdout: true).trim()
    }
    stages{
        stage("聲明式流水線: 拉一個代碼") {
            steps {
                git url: "${params.git_url}"
            }
        }
        stage("聲明式流水線: 執行一個命令"){
            steps {
                script {
                    sh "${params.cmd}"
                }
            }
        }
        stage("聲明式流水線: 打印如今的時間"){
            steps {
                script {
                    inputMsg([id:"${params.input_id}",msg:"${params.input_msg}"])
                }

            }
        }   
    }
}

}
  • Jenkinsfile
@Library('lotbrick') _
buildDeclarative([
    git_url:"https://github.com/jinyunboss/docker-compose.git",
    cmd:"ls -al",
    input_id: "input1",
    input_msg: "聲明式流水線的input"
    ])
  • 運行

利用Jenkins Shared Libraries隱藏你的Jenkinsfile

stage("hide your Jenkinsfile")

經過上面的例子,我以爲應該瞭解來基本的共享庫的用法了.
那麼來點騷操做來解決咱們開頭的幾個問題,
首先,既然是多分支的倉庫,那麼確定要兩個Jenkinsfile,對應不一樣的環境和發佈步驟
其次,在項目倉庫中全部分支的Jenkinsfile內容要一致,解決合併衝突的問題
綜合上面的問題來看,咱們須要的解決的問題只有一個,隱藏咱們的Jenkinsfile

  • 再創建一個倉庫,用來存放咱們的構建步驟的groovy,也就是實際的Jenkinsfile
# 由於咱們要用多分支的Job,因此,咱們的目錄結構要這樣
# 
# 目錄結構以下:

├── jenkins.lotbrick.com
│   ├── dev.groovy
│   └── master.groovy
└── www.lotbrick.com
    ├── dev.groovy
    └── master.groovy
  • 修改www.lotbrick.com/master.groovy
def call(params) {

pipeline {
    agent any
    options {
        // 禁止同時運行多個流水線
        disableConcurrentBuilds()
    }
    environment {
        examples_var1 = sh(script: 'echo "當前的時間是: `date`"', returnStdout: true).trim()
    }
    stages{
        stage("master: 執行一個命令"){
            steps {
                script {
                    sh "ls -al"
                }
            }
        }
        stage("master: 打印如今的時間"){
            steps {
                echo "${examples_var1}"
            }
        }
        stage("master: 打印JOB_NAME"){
            steps {
                echo "${JOB_NAME}"
            }
        }      
    }
}

}

// 記得要return喲,由於咱們是調用這個文件
return this
  • 在vars目錄下新建個run.groovy文件
// vars/run.groovy

def call(params){

    // 定義jenkinsfile的路徑
    def jenkinsfile_dir = "/data/jenkins-jenkinsfile/"

    node {
        // echo "${jenkinsfile_dir}"
        // echo "${params.env.JOB_NAME}"
        // 加載Jenkinsfile的groovy文件
        def jenkinsfile = load("${jenkinsfile_dir}/${params.env.JOB_NAME}.groovy")

        // 調用groovy中的call函數,將params也就是job運行時的全部公開的函數和變量傳進去                   
        jenkinsfile.call(params)

        // "${name}"(params)
        //evaluate('aaa(params)')

    }
}
  • 將這個存有實際groovy的倉庫放到Jenkins服務器上的/data/jenkins-jenkinsfile/路徑
[root@jenkins jenkins-jenkinsfile]# ll
total 8
drwxr-xr-x 2 root root 4096 Jun 12 18:07 jenkins.lotbrick.com
drwxr-xr-x 2 root root 4096 Jun 12 18:07 www.lotbrick.com
[root@jenkins jenkins-jenkinsfile]# tree
.
├── jenkins.lotbrick.com
│   ├── dev.groovy
│   └── master.groovy
└── www.lotbrick.com
    ├── dev.groovy
    └── master.groovy

2 directories, 4 files
[root@jenkins jenkins-jenkinsfile]# pwd
/data/jenkins-jenkinsfile
[root@jenkins jenkins-jenkinsfile]#
  • 最後就是Jenkinsfile
@Library('lotbrick') _
run(this)
  • 新建一個名爲www.lotbrick.com的多分支流水線的Job

利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile
利用Jenkins Shared Libraries隱藏你的Jenkinsfile

創建好Job以後就能看到Jenkins已經發現了咱們倉庫的兩個分支了,接下來分別運行兩個分支的Job

  • 運行 master 分支

利用Jenkins Shared Libraries隱藏你的Jenkinsfile

  • 運行 dev 分支

利用Jenkins Shared Libraries隱藏你的Jenkinsfile


總結

最後咱們來分析一下執行的過程:

  1. Jenkins掃描發現倉庫的兩個分支
  2. 運行master分支
  3. jenkins調用master分支的Jenkinsfile
  4. Jenkinfile中有libraries庫,克隆庫到workspace/JOB_NAME@libs
  5. 運行 run.groovy 中的 call函數
  6. 經過load加載其餘的groovy文件
  7. 執行groovy文件中的pipeline
  • 爲何咱們要獨立出一個倉庫來放實際的pipeline呢,而不是像上面同樣直接寫在vars目錄下

仔細看上面的執行過程,第4步,克隆庫文件到workspace/JOB_NAME@libs,若是咱們有十個JOB,那麼Jenkins就會克隆十次.
當咱們把實際的pipeline寫在vars目錄下的時候,日積月累這個目錄文件不少的時候,那麼就會致使重複克隆,影響執行效率
最要命的是,vars目錄下的文件會直接變成全局的變量,能夠直接調用的,變量太多.JAVA的內存消耗能夠很恐怖的(感受會這樣)

  • 多節點的Jenkins

我沒有考慮多個node的狀況,咱這只是個小公司,沒這麼多項目來發布可是我知道的一點就是,當pipeline的 agent選項爲any的時候,Jenkins會默認把代碼拉到當前node的workspace下,因此不須要再去拉一遍代碼

相關文章
相關標籤/搜索