Jenkin入門指南

起步

本指南會向您介紹Jenkins的基本使用以及主要功能——Jenkins管線。本指南使用“獨立”Jenkins分佈模式,在您的機器本地執行。

準備工作

對本教程來說,您需要:

  • 一臺包含如下配件的機器:
    • 256MB內存,不過推薦512MB及以上內存
    • 10GB存儲空間(給Jenkins和Docker鏡像使用)
  • 需要安裝如下軟件:
    • Java 8(JRE或Java開發工具包(JDK)都可以)
    • Docker(訪問Docker官網下載對應您的平臺的版本)

下載和運行Jenkins

  1. 下載Jenkins
  2. 在下載目錄打開命令行終端。
  3. 執行 java -jar jenkins.war --httpPort=8080
  4. 瀏覽器訪問 http://localhost:8080
  5. 根據提示完成安裝。
    在安裝完成後,您的Jenkins就可以開始工作了!

創建您的第一個管線(Pipline)

什麼是Jenkins管線?

Jenkins管線(或簡稱爲“管線”)是一組支持通過Jenkins實現和集成持續交付管線的插件。
持續交付管線是一種自動化表達式,用來處理在用戶或客戶的版控系統中獲取軟件代碼。
Jenkins管線提供了一系列可擴展的工具用於“以代碼的形式”對簡單到複雜的發送管線的建模。Jenkins管線的定義通常會寫入文本文件中(稱爲Jenkinsfile),並輪流簽入一個項目的源碼控制倉庫。1
對於管線和Jenkinsfile到底是什麼的更多信息,請分別參考用戶手冊中的管線Jenkinsfile
使用管線的快速起步:

  1. 複製下述實例到您的倉庫,並將其命名爲Jenkinsfile
  2. 點擊Jenkins中的New Item(新建項)Jenkins菜單項
  3. 給您的新項起名(如“My Pipeline”),並選擇Multibranch Pipeline(多分支管線)
  4. 點擊**Add Source(添加源)**按鈕,選擇您要的倉庫的類型並添加詳細信息。
  5. 點擊**Save(保存)**按鈕並欣賞您第一個管線的執行!

您可能需要修改示例Jenkinsfile文件中的某寫地方以使其可以在您的項目上運行。嘗試修改sh命令以在您本地機器上執行相應的命令。
在您設置了您的管線之後,Jenkins將自動檢測您倉庫中任何新加入的分支或拉取請求併爲它們執行該管線。

快速起步實例

下面是簡單的可複製粘貼的對多種語言的管線實例。

Java

聲明式管線:

pipeline {
    agent { docker { image 'maven:3.3.3' } }
    stages {
        stage('build') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}

腳本式管線(進階):

/* 依賴Docker Pipeline插件 */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('maven:3.3.3').inside {
            sh 'mvn --version'
        }
    }
}

Node.js / JavaScript

聲明式管線:

pipeline {
    agent { docker { image 'node:6.3' } }
    stages {
        stage('build') {
            steps {
                sh 'npm --version'
            }
        }
    }
}

腳本式管線(進階):

/* 依賴Docker Pipeline插件 */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('node:6.3').inside {
            sh 'npm --version'
        }
    }
}

Ruby

聲明式管線:

pipeline {
    agent { docker { image 'ruby' } }
    stages {
        stage('build') {
            steps {
                sh 'ruby --version'
            }
        }
    }
}

腳本式管線(進階):

/* 依賴Docker Pipeline插件 */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('ruby').inside {
            sh 'ruby --version'
        }
    }
}

Python

聲明式管線:

pipeline {
    agent { docker { image 'python:3.5.1' } }
    stages {
        stage('build') {
            steps {
                sh 'python --version'
            }
        }
    }
}

腳本式管線(進階):

/* 依賴Docker Pipeline插件 */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('python:3.5.1').inside {
            sh 'python --version'
        }
    }
}

PHP

聲明式管線:

pipeline {
    agent { docker { image 'php' } }
    stages {
        stage('build') {
            steps {
                sh 'php --version'
            }
        }
    }
}

腳本式管線(進階):

/* 依賴Docker Pipeline插件 */
node('docker') {
    checkout scm
    stage('Build') {
        docker.image('php').inside {
            sh 'php --version'
        }
    }
}

執行多個步驟

管線由多個步驟組成,從而使您可以構建、測試和部署應用。Jenkins管線允許您以一種簡單的方式集成多個步驟,這可以幫助您對任意自動化處理組合進行建模。
可以將一個“步驟”想象成處理某個動作的一行命令。每個步驟執行成功後就會移向下一個步驟。如果某一步沒有正確執行,整個管線的執行就會失敗。
當一個管線的所有步驟都成功完成了,這個管線也就算是成功執行了。

Linux、BSD及Mac OS

在Linux、BSD和Mac OS等(類Unix)系統上,sh步驟用於在管線內執行shell命令。
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh 'echo "Hello World"'
                sh '''
                    echo "Multiline shell steps works too"
                    ls -lah
                '''
            }
        }
    }
}

腳本式管線(進階):

node {
    stage('Build') {
        sh 'echo "Hello World"'
        sh '''
            echo "Multiline shell steps works too"
            ls -lah
        '''
    }
}

Windows

基於Windows的操作系統應該使用bat步驟來執行批處理命令。
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                bat 'set'
            }
        }
    }
}

腳本式管線(進階):

node {
    stage('Build') {
        bat 'set'
    }
}

超時和重試…

有一些功能強大的步驟可以“封裝”其他步驟,從而可以輕鬆解決像重試(retry)步驟直至成功,或在某個步驟耗時太久(timeout)時將其退出等問題。
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                retry(3) {
                    sh './flakey-deploy.sh'
                }

                timeout(time: 3, unit: 'MINUTES') {
                    sh './health-check.sh'
                }
            }
        }
    }
}

腳本式管線(進階):

node {
    stage('Deploy') {
        retry(3) {
            sh './flakey-deploy.sh'
        }

        timeout(time: 3, unit: 'MINUTES') {
            sh './health-check.sh'
        }
    }
}

“Deploy”階段會嘗試3次flakey-deploy.sh腳本,並等待3分鐘health-check.sh腳本的執行。如果健康檢查腳本在3分鐘之後還沒有執行完畢,管線就會標記“Deploy”階段爲已失敗。
“封裝器”步驟,如timeoutretry可能會包含其他步驟,包括timeoutretry自身。
我們可以將這些步驟組合起來。例如,假設我們的部署可以嘗試5次,但總耗時不許超過3分鐘,否則該階段失敗:
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Deploy') {
            steps {
                timeout(time: 3, unit: 'MINUTES') {
                    retry(5) {
                        sh './flakey-deploy.sh'
                    }
                }
            }
        }
    }
}

腳本式管線(進階):

node {
    stage('Deploy') {
        timeout(time: 3, unit: 'MINUTES') {
            retry(5) {
                sh './flakey-deploy.sh'
            }
        }
    }
}

臨結束

在管線執行完畢後,您可能需要執行清理步驟或執行一些基於管線執行結果的動作。這些動作可以在post部分發揮作用。
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh 'echo "失敗!"; exit 1'
            }
        }
    }
    post {
        always {
            echo '我總是會執行'
        }
        success {
            echo '我只會在成功時運行'
        }
        failure {
            echo '我只會在失敗時運行'
        }
        unstable {
            echo '我只在執行被標記爲不穩定時運行'
        }
        changed {
            echo '我只會在管線的狀態改變時運行'
            echo '例如,管線在之前執行失敗但現在成功了'
        }
    }
}    

腳本式管線(進階):

node {
    try {
        stage('Test') {
            sh 'echo "失敗!"; exit 1'
        }
        echo '我只會在成功時執行'
    } catch (e) {
        echo '我只會在失敗時執行'

        // 由於我們爲了輸出報告而捕獲了異常,
        // 爲了確保構建被標記爲失敗,我們需要重拋這個異常
        throw e
    } finally {
        def currentResult = currentBuild.result ?: 'SUCCESS'
        if (currentResult == 'UNSTABLE') {
            echo '我只在執行被標記爲不穩定時運行'
        }

        def previousResult = currentBuild.previousBuild?.result
        if (previousResult != null && previousResult != currentResult) {
            echo '我只會在管線的狀態改變時運行'
            echo '例如,管線在之前執行失敗但現在成功了'
        }

        echo '我總是會執行'
    }
}

在學習過定義多步驟之後,讓我們繼續學習“定義執行環境”吧。


定義執行環境

在上一節,您可能注意到了每個示例中的agent指令。agent指令告訴Jenkins在何處以及如何執行管線或其自集。如你所想,agent在所有管線中都是必填項。
在表象之下,agent會導致一些事情的發生:

  • 代碼塊中包含的所有步驟會形成隊列用於被Jenkins執行。在一個executor(執行器)可用的時候,這些步驟就會開始執行。
  • 會申請一個workspace(工作空間)用於存放自代碼控制系統簽出的文件及其他管線工作需要的附加工作文件。

在管線中有許多定義agent的方式,在本教程中,我們將只關注使用短暫Docker容器。
管線被設計爲可以簡單地使用Docker容器和鏡像並在其中運行。這使得管線可以不用在agent裏配置一系列系統工具和依賴的前提下定義所需的環境和工具。這種方法使得您可以使用幾乎所有可以被打包進Docker容器的工具。
更多關於agent的技術選項,請查看語法參考
聲明式管線:

pipeline {
    agent {
        docker { image 'node:7-alpine' }
    }
    stages {
        stage('Test') {
            steps {
                sh 'node --version'
            }
        }
    }
}

腳本式管線(進階):

node {
    /* 需要已安裝Docker Pipeline插件 */
    docker.image('node:7-alpine').inside {
        stage('Test') {
            sh 'node --version'
        }
    }
}

在管線執行時,Jenkins將自動啓動指定的容器並在其中執行所定義的步驟:

[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] sh
[guided-tour] Running shell script
+ node --version
v7.4.0
[Pipeline] }
[Pipeline] // stage
[Pipeline] }

混合或匹配不同的容器或agent,爲管線的執行提供了極大的靈活性。要學習更多配置選項,請繼續閱讀“使用環境變量”。

使用環境變量

全局變量可以如下文示例所示全局設定,也可以分階段設定。如你所想,按階段定義環境變量意味着它們將只會在它們所在的階段有效。
聲明式管線:

pipeline {
    agent any
    environment {
        DISABLE_AUTH = 'true'
        DB_ENGINE = 'sqlite'
    }

    stages {
        stage('Build') {
            steps {
                sh 'printenv'
            }
        }
    }
}

腳本式管線(進階):

node {
    withEnv(['DISABLE_AUTH=true',
             'DB_ENGINE=sqlite']) {
        stage('Build') {
            sh 'printenv'
        }
    }
}

Jenkinsfile中定義環境變量的方法會在執行腳本時非常有用,例如對於Makefile,可以在Jenkins中使用不同的參數配置構建和測試。
另一個環境變量的常用場景是在構建或測試腳本里設置或重寫“空包”證書。由於(很明顯)直接給Jenkinsfile傳入證書是個壞主意,Jenkins管線允許用戶又快又安全地訪問Jenkinsfile裏預定義的證書而不需要了解其值。

環境中的證書

參閱用戶手冊裏的處理證書獲取更多信息。

記錄測試和打包

儘管測試是一個好的持續交付管線的重要組成,多數用戶並不想在數以千計的控制檯輸出中篩選以查找有關失敗測試用例的信息。爲了簡化這一點,只要您的測試執行器可以輸出測試結果文件,Jenkins就可以記錄和彙集測試結果。Jenkins通常同捆綁定junit步驟,但如果您的測試執行器不能輸出JUnit風格的XML報告,我們也有額外的插件用於處理幾乎所有廣泛使用的測試報告格式。
爲了手機我們的打包和測試結果,我們需要使用post片段。
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                sh './gradlew check'
            }
        }
    }
    post {
        always {
            junit 'build/reports/**/*.xml'
        }
    }
}

腳本式管線(進階):

node {
    try {
        stage('Test') {
            sh './gradlew check'
        }
    } finally {
        junit 'build/reports/**/*.xml'
    }
}

Jenkins將總是會追蹤它們,收集測試結果、計算趨勢並報告。管線的測試如果沒能通過則會被標記爲“UNSTABLE(不穩定的)”,在web UI上以黃色標註。這和“FAILED(失敗)”狀態不同,失敗會標記爲紅色。
當測試失敗時,抓取當前Jenkins的打包進行本地分析和調查是常常是很有用的。Jenkins內建的對“打包結果”以及管線執行期間產生文件的保存功能就派上用場了。
使用archiveArtifacts步驟和一個文件通配符表達式就能很簡單地實現這一功能,如下文示例所示:
聲明式管線:

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                sh './gradlew build'
            }
        }
        stage('Test') {
            steps {
                sh './gradlew check'
            }
        }
    }

    post {
        always {
            archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
            junit 'build/reports/**/*.xml'
        }
    }
}

腳本式管線(進階):

node {
    try {
        stage('Test') {
            sh './gradlew check'
        }
    } finally {
        archiveArtifacts artifacts: 'build/libs/**/*.jar', fingerprint: true
        junit 'build/reports/**/*.xml'
    }
}

如果在archiveArtifacts步驟裏設置了多於1個的參數,那麼每個參數的名字必須在該步驟裏明確指定,例如,artifactss代表打包的路徑和文件名,fingerprint用於使用該選項。如果您只需要指定打包路徑和文件名,那麼就可以省略參數名artifacts了,就像這樣:
archiveArtifacts 'build/libs/**/*.jar'
在Jenkins裏記錄測試和打包信息對簡單快速地向團隊成員提供信息是很有用的。在下一節我們將討論關於如何告訴這些團隊成員在我們的管線裏發生過什麼。

清理和通知

由於管線的post部分會保證其在管線之行之後運行,我們可以添加一些通知或其他步驟以實現最終確定、通知或其他後管線任務。
聲明式管線:

pipeline {
    agent any
    stages {
        stage('No-op') {
            steps {
                sh 'ls'
            }
        }
    }
    post {
        always {
            echo '無論如何,我的執行完成了'
            deleteDir() /* 清理工作空間 */
        }
        success {
            echo '我成功執行了!'
        }
        unstable {
            echo '我是不穩定的 :/'
        }
        failure {
            echo '我失敗了 :('
        }
        changed {
            echo '事情和以前不一樣了哦…'
        }
    }
}

腳本式管線(進階):

node {
    try {
        stage('No-op') {
            sh 'ls'
        }
    }
    catch (exc) {
        echo '我失敗了'
    }
    finally {
        if (currentBuild.result == 'UNSTABLE') {
            echo '我是不穩定的 :/'
        } else {
            echo '不論如何,我的執行完成了'
        }
    }
}

發送通知的手段有很多種,下面是在管線中發送通知的一些示例片段,包括電子郵件、Hipchat房間和Slack頻道。

Email

post {
    failure {
        mail to: '[email protected]',
             subject: "管線執行失敗:${currentBuild.fullDisplayName}",
             body: "有些地方發生了錯誤:${env.BUILD_URL}"
    }
}

Hipchat

post {
    failure {
        hipchatSend message: “注意 @here ${env.JOB_NAME} #${env.BUILD_NUMBER} 執行失敗了。",
        color: 'RED'
    }
}

Slack

post {
    success {
        slackSend channel: '#ops-room',
                  color: 'good',
                  message: "管線${currentBuild.fullDisplayName} 的執行完全成功。"
    }
}

現在團隊就可以在失敗、不穩定或甚至是成功的時候收到通知了。我們將完成我們持續交付管線的最令人激動的部分:部署!

部署

最基本的持續交付管線應該,至少,擁有三個階段,定義在Jenkinsfile中:Build(構建)、Test(測試)和Deploy(部署)。本節我們將主要關注部署階段,但一定要記住穩定的構建和測試階段對任何部署行爲都是重要的前提。
聲明式管線:

pipelien {
    agent any
    stages {
        stage('Build') {
            steps {
                echo '正在構建'
            }
        }
        stage('Test') {
            steps {
                echo '正在測試'
            }
        }
        stage('Deploy') {
            steps {
                echo '正在部署'
            }
        }
    }
}

腳本式管線(進階):

node {
    stage('Build') {
        echo '正在構建'
    }
    stage('Test') {
        echo '正在測試'
    }
    stage('Deploy') {
        echo '正在部署'
    }
}

用階段定義部署環境

一種常用的模式是擴展階段的數量以附加部署環境,如“演習環境”或“生產環境”,如下文所示。

stage('Deploy - Staging') {
    steps {
        sh './deploy staging'
        sh './run-smoke-tests'
    }
}
stage('Deploy - Production') {
    steps {
        sh './deploy production'
    }
}

在本示例中,我們假定所有的“冒煙測試”都通過我們的./run-smoke-tests腳本來執行,從而保證足夠的效率並驗證其可否作爲生產環境的一個發行版。此類自動部署代碼到生產環境的管線可以看做是對“持續部署”的一種實現。儘管這是一個不錯的主意,但針對爲何持續部署也許並不適合實踐也有許多人提出了不錯的理由,儘管如此他們仍然可以得到持續發送帶來的好處。2

詢問用戶輸入並處理

在通過階段與階段之間時,特別是環境階段,您通常可能會希望用戶輸入然後再繼續。例如,判斷應用是否足夠完善從而可以被“晉升”至生產環境。我們可以通過input步驟來完成這一點。下文所示的例子中,“Sanity check”步驟會阻塞並等待輸入,在用戶確認之前處理不會繼續。
聲明式管線:

pipeline {
    agent any
    stages {
        /* 省略了“build”和“Test”階段 */
        
        stage('Deploy - Staging') {
            steps {
                sh './deploy staging'
                sh './run-smoke-tests'
            }
        }

        stage('Sanity check') {
            steps {
                input "Does the staging environment look ok?"
            }
        }

        stage('Deploy - Production') {
            steps {
                sh './deploy production'
            }
        }
    }
}

腳本式管線(進階):

node {
     /* 省略了“build”和“Test”階段 */

    stage('Deploy - Staging') {
        sh './deploy staging'
        sh './run-smoke-tests'
    }

    stage('Sanity check') {
        input "Does the staging environment look ok?"
    }

    stage('Deploy - Production') {
        sh './deploy production'
    }
}

結尾

本教程向您介紹了使用Jenkins和Jenkins管線的基礎知識。由於Jenkins是極度可擴展的,它可以通過修改和配置以支持幾乎任意的自動化相關部分。要學習更多關於Jenkins可以做什麼,您可以參考用戶手冊,或Jenkins博客來了解最新的事件、教程和更新。


  1. 源碼控制管理 ↩︎

  2. en.wikipedia.org/wiki/Continuous_delivery ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章