10分鐘搞定讓你困惑的 Jenkins 環境變數

日拱一兵發表於2021-01-12

前言

Jenkins, DevOps 技術棧的核心之一,CI/CD 離不開編寫 Pipeline 指令碼,上手 Jenkins ,簡單查一下文件,你就應該不會被 agent,stages,step 這類關鍵詞弄懵,也能很快構建出 pipeline 的骨架

但是當向骨架中填充內容的時候,尤其如何利用環境變數(系統內建 | 自定義),多數人都會變得比較混亂,浪費很多時間,本文就幫助大家快速通關環境變數

準備

如果你想一邊閱讀本文,一邊實踐,但是沒有 Jenkins 服務可用,又想快速嘗試,可以應用 Docker 一個命令快速搭建 Jenkins 服務

docker container run --rm -p 8080:8080 -p 50000:50000 --name=jenkins -v $(pwd):/var/jenkins_home jenkins/jenkins

2021 年了,本地沒有 Docker 說不過去了,過來瞧瞧 Docker 系列是否入得了你的法眼?

開啟瀏覽器輸入:localhost:8080

  1. 找到終端的臨時密碼登陸
  2. 安裝推薦的依賴
  3. 建立新的 Pipeline 型別的 Item
  4. 點選左側 Config,然後在頁面底部 Pipeline 部分輸入我們接下來寫的指令碼進行測試就好了

就是這麼簡單.....

認識 Jenkins 環境變數

Jenkins 環境變數就是通過 env 關鍵字暴露出來的全域性變數,可以在 Jenkins 檔案的任何位置使用

其實和你使用的程式語言中的全域性變數沒有實質差別

檢視 Jenkins 系統內建環境變數

Jenkins 在系統內建了很多環境變數方便我們快速使用,檢視起來有兩種方式:

方式一:

直接在瀏覽器中訪問 ${YOUR_JENKINS_HOST}/env-vars.html 頁面就可以,比如 http://localhost:8080/env-vars.html ,每個變數的用途寫的都很清楚

方式二

通過執行 printenv shell 命令來獲取:

pipeline {
    agent any

    stages {
        stage("Env Variables") {
            steps {
                sh "printenv"
            }
        }
    }
}

直接 Save - Build, 在終端 log 中你會看到相應的環境變數,並且可以快速看到他們當前的值

通常這兩種方式可以結合使用

讀取環境變數

上面我們說了 env 是環境變數的關鍵字,但是讀取 Jenkins 內建的這些環境變數,env 關鍵字是可有可無, 但不能沒了底褲,都要使用 ${xxx} 包圍起來。以 BUILD_NUMBER 這個內建環境變數舉例來說明就是這樣滴:

如果你在 Jenkins 檔案中使用 shell 命令,使用這些內建環境變數甚至可以不用 {}, 來看一下:

pipeline {
    agent any

    stages {
        stage("Read Env Variables") {
            steps {
                echo "帶 env 的讀取方式:${env.BUILD_NUMBER}"
                echo "不帶 env 的讀取方式:${BUILD_NUMBER}"
                sh 'echo "shell 中讀取方式 $BUILD_NUMBER"'
            }
        }
    }
}

可以看到結果是一樣一樣滴,不管有幾種,記住第一種最穩妥

內建的環境變數雖好,但也不能完全滿足我們自定義的 pipeline 的執行邏輯,所以我們也得知道如何定義以及使用自定義環境變數

自定義 Jenkins 環境變數

Jenkins pipeline 分宣告式(Declarative)和 指令碼式(imperative)寫法,相應的環境變數定義方式也略有不同,歸納起來有三種方式:

還是看個實際例子吧:

pipeline {
    agent any

    environment {
        FOO = "bar"
    }

    stages {
        stage("Custom Env Variables") {
            environment {
                NAME = "RGYB"
            }

            steps {
                echo "FOO = ${env.FOO}"
                echo "NAME = ${env.NAME}"

                script {
                    env.SCRIPT_VARIABLE = "Thumb Up"
                }

                echo "SCRIPT_VARIABLE = ${env.SCRIPT_VARIABLE}"

                withEnv(["WITH_ENV_VAR=Come On"]) {
                    echo "WITH_ENV_VAR = ${env.WITH_ENV_VAR}"
                }
            }
        }
    }
}

來看執行結果:

注意:withEnv(["WITH_ENV_VAR=Come On"]) {} 這裡的 = 號兩側不能有空格,必須是 key=value 的形式

一個完整的 pipeline 通常會有很多個 stage,環境變數在不同的 stage 有不同的值是很常見的,知道如何設定以及讀取環境變數後,我們還得知道如何重寫環境變數

重寫 Jenkins 環境變數

Jenkins 讓人相對困惑最多的地方就是重寫環境變數,但是隻要記住下面這三條規則,就可以搞定一切了

  1. withEnv(["WITH_ENV_VAR=Come On"]) {} 內建函式的這種寫法,可以重寫任意環境變數
  2. 定義在 environment {} 的環境變數不能被指令碼式定義的環境變數(env.key="value")重寫
  3. 指令碼式環境變數只能重寫指令碼式環境變數

這三點是硬規則,沒涵蓋在這 3 點規則之內的也就是被允許的了

三條規則就有點讓人頭大了,農夫選豆種,舉例為證吧

pipeline {
    agent any

    environment {
        FOO = "你當像鳥飛往你的山"
        NAME = "Tan"
    }

    stages {
        stage("Env Variables") {
            environment {
                  // 會重寫第 6 行 變數
                NAME = "RGYB" 
                  // 會重寫系統內建的環境變數 BUILD_NUMBER
                BUILD_NUMBER = "10" 
            }

            steps {
                  // 應該列印出 "FOO = 你當像鳥飛往你的山"
                echo "FOO = ${env.FOO}" 
                  // 應該列印出 "NAME = RGYB"
                echo "NAME = ${env.NAME}" 
                  // 應該列印出 "BUILD_NUMBER = 10"
                echo "BUILD_NUMBER =  ${env.BUILD_NUMBER}" 

                script {
                      // 指令碼式建立一個環境變數
                    env.SCRIPT_VARIABLE = "1" 
                }
            }
        }

        stage("Override Variables") {
            steps {
                script {
                      // 這裡的 FOO 不會被重寫,違背 Rule No.2
                    env.FOO = "Tara"
                      // SCRIPT_VARIABLE 變數會被重寫,符合 Rule No.3
                    env.SCRIPT_VARIABLE = "2" 
                }

                  // FOO 在第 37 行重寫失敗,還會列印出 "FOO = 你當像鳥飛往你的山"
                echo "FOO = ${env.FOO}" 
                  // 會列印出 "SCRIPT_VARIABLE = 2"
                echo "SCRIPT_VARIABLE = ${env.SCRIPT_VARIABLE}" 

                  // FOO 會被重寫,符合 Rule No.1
                withEnv(["FOO=Educated"]) { 
                      // 應該列印 "FOO = Educated"
                    echo "FOO = ${env.FOO}" 
                }

                  // 道理同上
                withEnv(["BUILD_NUMBER=15"]) {
                      // 應該列印出 "BUILD_NUMBER = 15"
                    echo "BUILD_NUMBER = ${env.BUILD_NUMBER}"
                }
            }
        }
    }
}

來驗證一下結果吧

看到這,基本的設定應該就沒有什麼問題了,相信你也發現了,Jenkins 設定環境變數和程式語言的那種設定環境變數還是略有不同的,後者可以將變數賦值為物件,但 Jenkins 就不行,因為在 Jenkins 檔案中,所有設定的值都會被當成 String, 難道沒辦法應用 Boolean 值嗎?

Jenkins 中使用 Boolean 值

如果設定一個變數為 false ,Jenkins 就會將其轉換為 "false", 如果想使用 Boolean 來做條件判斷,必須要呼叫 toBoolean() 方法做轉換

pipeline {
    agent any

    environment {
        IS_BOOLEAN = false
    }

    stages {
        stage("Env Variables") {
            steps {
                script {
                      // Hello 會被列印出來,因為非空字串都會被認為是 Boolean.True
                    if (env.IS_BOOLEAN) {
                        echo "Hello"
                    }

                      // 真正的 Boolean 比較
                    if (env.IS_BOOLEAN.toBoolean() == false) {
                        echo "日拱一兵"
                    }
                  
                      // 真正的 Boolean 
                    if (!env.IS_BOOLEAN.toBoolean()) {
                        echo "RGYB"
                    }
                }
            }
        }
    }
}

來看執行結果:

如果你寫過 Pipeline,你一定會知道,寫 Pipeline 是離不開寫 shell 的,有些時候,需要將 shell 的執行結果賦值給環境變數,Jenkins 也有方法支援

Shell 結果賦值給環境變數

實現這種方式很簡單,只需要記住一個格式:sh(script: 'cmd', returnStdout:true)

pipeline {
    agent any

    environment {
          // 使用 trim() 去掉結果中的空格
        LS_RESULT = "${sh(script:'ls -lah', returnStdout: true).trim()}"
    }

    stages {
        stage("Env Variables") {
            steps {
                echo "LS_RESULT = ${env.LS_RESULT}"
            }
        }
    }
}

總結

關於 Jenkins 環境變數,瞭解這些基本上就滿足絕大多數應用場景了,當再遇到環境變數問題時,可以回過來翻看一下了,有解決的困惑嗎?

相關文章