Jenkins Pipeline: Một vài ghi chép hữu ích

Tổng quan

Pipeline là kiểu job mới trên Jenkins 2.x, cho phép chúng ta sử dụng code để thực thi một complex CI build (Pipeline as Code). Nó được xây dựng dựa trên ngôn ngữ Groovy với một DSL (Domain-specific language) syntax riêng. Đọc thêm về Pipeline documentation để làm quen với nó. Bài viết này sẽ ghi chép lại một số chú ý quan trọng khi làm việc với Pipeline.

Một template chung cho việc bắt đầu viết một pipeline script có thể như sau:

node('pipeline') { // Run on slave with label 'pipeline'
    timestamps { // show timestamps in console output
        ansiColor('xterm') { // support for ANSI color in console output
            try {
                // Do main works in stages
                stage('Stage 1') {
                    // Execute Pipeline steps here
                    sh 'printenv | sort'
                    if (something is wrong) {
                        // Signal an error and pipeline execution will be stopped here
                        error 'Something is wrong. Stopping pipeline execution...'
                    }
                }

                stage('Stage 2') {

                }
            } catch (ex) {
                // Handle errors in this stage
                stage('Error Handling') {
                    echo "ERROR: ${ex.message}"
                    currentBuild.result = 'FAILURE'
                }
            } finally {
                // Execute post-build actions in this stage
                stage('Post-build actions') {
                    cleanWs notFailBuild: true
                }
            }
        }
    }
}

Chú ý là chúng ta đang sử dụng Scripted Pipeline thay cho Declarative Pipeline vì nó linh hoạt và phù hợp hơn với complex workflow.

Trong template script trên, có vài điểm lưu ý sau:

  • Các stage trong try block sẽ là các main stage trong CI build. Nó sẽ thực hiện những công việc chính là chạy build và test.
  • Nếu có lỗi xảy ra trong try block (từ một trong số các stage), thì nó sẽ được xử lí ở catch block, trong stage Error Handling. Các lỗi có thể phát sinh do các build script, build step hoặc từ error step.
  • Cuối cùng, trong stage Post-build actions (luôn luôn được chạy), chúng ta sẽ chạy các bước như cleanup, send mail, archive actifacts, v.v.

Các biến global

Trong suốt quá trình build, Jenkins cung cấp sẵn cho pipeline script một vài biến global. Những biến này có thể được xem tại JENKINS_URL/pipeline-syntax/globals (ví dụ localhost:8080/jenkins/pipeline-syntax/globals). Các biến sau đây là thông dụng:

  • currentBuild: Một object có kiểu RunWrapper, cho phép chúng ta truy vấn một số thông tin của build đang chạy, cũng như thay đổi một số thuộc tính của nó, chẳng hạn kết quả build.
  • env: Cho phép truy vấn và lưu trữ các biến môi trường trong suốt quá trình build.
  • params: Chứa các tham số của build đang chạy.

Pipeline steps

Một step là một việc cụ thể nào đó mà chúng ta muốn làm, ví dụ như đọc một file, gửi mail, lưu artifact, v.v. Có rất nhiều step được cung cấp trong Jenkins Pipeline, một số có sẵn, một số được cung cấp qua các plugin. Tuy nhiên một số step sau đây là thông dụng:

Basic steps

  • echo: In một message ra console output.
    echo "Some build information."
    
  • dir: Thay đổi thư mục hiện hành trong quá trình build. Mặc định trong quá trình build, mọi thứ sẽ được diễn ra trong thư mục WORKSPACE. Ví dụ:
    dir("${WORKSPACE}/somedir") {
        // Any step inside this block will use ${WORKSPACE}/somedir as current directory
    }
    

    Nếu chúng ta không cung cấp full path, ví dụ dir('somdir') {...}, thì mặc định nó sẽ hiểu đây là relative path với thư mục hiện hành.

  • pwd: Cho biết thư mục hiện hành.

    dir("${WORKSPACE}/somedir") {
        echo "${pwd()}"
    }
    
  • deleteDir: Xóa thư mục hiện hành, mà mặc định là WORKSPACE. Sử dụng dir step nếu muốn xóa một thư mục khác.
    dir("${WORKSPACE}/somedir") {
        deleteDir()
    }
    
  • readFile: Đọc một file và trả về nội dung của file như là một Groovy String.
    def content = readFile(file: "${WORKSPACE}/file.txt", encoding: 'UTF-8')
    echo "${content}"
    
  • writeFile: Ghi một Groovy String vào một file.
    writeFile(file: "${WORKSPACE}/file.txt", text: 'some content', encoding: 'UTF-8')
    

Tương tự như dir step, nếu chúng ta không cung cấp full path cho file, nó sẽ hiểu là relative path với thư mục hiện hành. Điều này được áp dụng tương tự như các step readwrite dưới đây.

Utility steps

Để có thể sử dụng các step sau đây, chúng ta cần cài đặt plugin sau:
https://wiki.jenkins.io/display/JENKINS/Pipeline+Utility+Steps+Plugin

  • readProperties: Đọc một properties file hoặc một String có định dạng của một propeties file (key=value) và trả về một Groovy Map với các key là Groovy String.
    def props = readProperties(file: "${WORKSPACE}/file.prop")
    
    props.each { k, v ->
        echo "${k}=${v}"
    }
    
  • readJSON: Tương tự readProperties step nhưng với định dạng JSON.

  • readYaml: Tương tự readProperties step nhưng với định dạng YAML và kiểu trả về có thể là List, String, Long, Boolean, v.v.
  • readCSV: Tương tự readProperties step nhưng với định dạng CSV và trả về kiểu danh sách của các object có kiểu CSVRecord.
  • writeJSON: Ghi một Groovy Map object vào một JSON file.
  • writeYaml: Ghi một Groovy Object (có thể là String, List, Long, Boolean, v.v) vào một YAML file.
  • writeCSV: Ghi một danh sách các CSVRecord vào một CSV file.

Các step khác

  • sh: Chạy một shell script.

    sh '''#!/bin/bash
    printenv | sort
    '''
    

    sh step có hai option quan trọng đó là returnStatusreturnStdout.

    int status = sh(returnStatus: true, script: '''#!/bin/bash
    exit 1''')
    
    if (status != 0) {
        error "A meaningful error message"
    }
    
    String output = sh(returnStdout: true, script: '''#!/bin/bash
    somescript 2> /dev/null''').trim()
    
    // do some stuff with output
    
  • build: Build một job khác.
    def b = build(job: 'Demo', parameter: [
        string(name: 'PARAM', value: 'VALUE')
    ], propagate: false)
    
    if (b.currentResult == 'FAILURE') {
        error "'Demo' build failed."
    }
    

    Kết quả trả về của build step là một RunWrapper object (giống như currentBuild).

  • checkout: Kéo source code về từ một version control system, ví dụ như Git. Step này có khá nhiều option và thông thường chúng ta sẽ dùng Snippet Generator của Jenkins để viết step này.

    checkout(
        poll: false, 
        scm: [$class: 'GitSCM', branches: [[name: '*/master']], 
        doGenerateSubmoduleConfigurations: false, extensions: [], 
        submoduleCfg: [], 
        userRemoteConfigs: [[
                credentialsId: 'admin',         
                url: 'git@github.com:testdrivenio/django-on-docker.git'
        ]]
    ])
    
  • git: Một shortcut của checkout step.
    git(url: 'git@github.com:testdrivenio/django-on-docker.git', 
        branch: 'master',
        credentialsId: 'admin'
    )
    
  • archiveArtifacts: Lưu trữ những artifact sau khi build, ví dụ một file .jar, một file .tar.gz chứa các log file. Những artifact này sẽ được lưu tại thư mục JENKINS_HOME/jobs/JOB_NAME/builds/BUILD_NUMBER/archive/. Đây là một post-build action.
    archiveArtifacts artifacts: 'build_logs.tar.gz', fingerprint: true
    
  • emailext: Step này dùng để gửi mail. Đây là một post-build action.
    emailext(body: 'Something went wrong, check build log!', 
             subject: 'Build failed', 
             to: 'someone@example.com')
    

TL;DR

Trên đây là một vài ghi chú ngắn gọn về Jenkins Pipeline với các tính năng cơ bản và thông dụng của nó. Ngoài ra còn khá nhiều khía cạnh liên quan đến Pipeline đã được đề cập khá chi tiết ở documentation như:

  • Jenkins Shared Pipeline Libraries: Một cơ chế để tránh duplicate code trong nhiều pipeline script.
  • Handling Credentials: Cách để chúng ta handle crendentials trên Jenkins một cách an toàn và hiệu quả.
  • Parallel execution: Một step đặc biệt trong Scripted Pipeline cho phép chạy song song nhiều step, stage hay build cùng một lúc.
  • Script Security: Một vài security concern với Groovy script trên Jenkins. (xem thêm tại đâyđây).

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Design a site like this with WordPress.com
Get started