세미나

Jenkins Pipeline - Basic

nineDeveloper 2019. 10. 22. 21:18
728x90

Hello World

Job 생성

  • Jenkins > New Item > Pipeline
node { 
    stage('Stage 1') { 
        echo 'Hello World' 
    } 
    stage('Stage 2') { 
        echo 'Stage 2' 
    } 
}

Node, Stage, Step

  • pipeline 스크립트는 node, stage, step 으로 구성된다.
  • node (Required)
    • 실행 머신
  • stage (Optional)
    • 관련 동작들 grouping 용도
  • Step
    • UI Job에서 설정했던 동작들
    • git checkout
    • execute shell script
    • junit report
    • archive, ...

실행 결과 (console output)

Started by user 신현일 Running in Durability level: MAX_SURVIVABILITY 

[Pipeline] Start of Pipeline 
[Pipeline] node Running on Jenkins in /data/jenkins/workspace/pipeline_training/hello world 
[Pipeline] { 
[Pipeline] stage 
[Pipeline] { (Stage 1) 
[Pipeline] echo Hello World 
[Pipeline] } 
[Pipeline] // stage 
[Pipeline] stage 
[Pipeline] { (Stage 2) 
[Pipeline] echo Stage 2 
[Pipeline] } 
[Pipeline] // stage 
[Pipeline] } 
[Pipeline] // node 
[Pipeline] End of Pipeline 

Finished: SUCCESS

Stage View

Step 별 실행 결과

Pipeline 기본 Flow

자유롭게 구성할 수 있다.

node { 
    stage("build") { 
        step step ... 
    } 
    stage("report") { 
        step step ... 
    } 
}

node를 여러 개 사용할 수 있다.

stage("1") { 
    node ("nodeA") { 
        step step ... 
    } 
} 
stage("2") { 
    node ("nodeB") { 
        step step ... 
    } 
}

Step 문법 외울 필요 없다 (코드 자동 생성 기능)

  • GUI 이용하여 코드 자동 생성

Example: git checkout

Generate Pipeline Script

기본 Steps

https://jenkins.io/doc/pipeline/steps/

sh (리눅스)

  • shell 명령어 실행
node { 
    stage('Stage 1') { 
        sh 'pwd' 
        sh 'java -version' 
    } 
}

sh: return value

  • returnStdout
    • output을 반환
node { 
    stage('s') { 
        def output = sh(encoding: 'UTF-8', returnStdout: true, script: 'java -version')
        echo output 
    }
}
  • returnStatus
    • status code 반환
node { 
    stage('s') { 
        def output = sh(encoding: 'UTF-8', returnStatus: true, script: 'java -version') 
        echo output.toString() 
    }
}

sh: label

human-readable 제목 붙이기

node { 
    stage('s') { 
        def output = sh(encoding: 'UTF-8', 
        label: 'print java version', //set label
        returnStatus: true, script: 'java -version') 
        echo output.toString() 
    } 
}

dir

node { 
    stage('s') { 
        sh 'mkdir -p hello' 
        sh 'pwd' 
        dir('hello') { 
            sh 'pwd' 
        }
    }
}

실행 결과

[Pipeline] sh 
+ mkdir -p hello 
[Pipeline] sh 
+ pwd /home1/irteam/apps/jenkins/jenkins_home/jobs/pipeline_training/jobs/sample/workspace 
[Pipeline] dir Running in /home1/irteam/apps/jenkins/jenkins_home/jobs/pipeline_training/jobs/sample/workspace/hello 
[Pipeline] { 
[Pipeline] sh 
+ pwd /home1/irteam/apps/jenkins/jenkins_home/jobs/pipeline_training/jobs/sample/workspace/hello 
[Pipeline] } 
[Pipeline] // dir

실습 01. Pipeline job 만들기

node() { 
    stage('git') { 

    } 
    stage('execute a script') { 

    } 
}

stash, unstash

  • 빌드 내에서 파일 저장/불러오기 (임시 저장)
    • 빌드 결과에 저장되지는 않음

archiveArtifacts

  • 파일 보관
node { 
    deleteDir() 
    stage('stash') { 
        dir('hello') { 
            sh 'echo "hey" > file1.txt' 
            stash includes: 'file1.txt', name: 'file1' 
        } 
    } // other steps 
    stage('unstash') { 
        unstash 'file1' 
        sh 'find ./' 
        sh 'cat file1.txt' 
    } 
}

build a job

node { 
    build 'build\_job\_target' 
}
  • options
    • Wait for completion
      • Job 빌드 종료를 기다릴 것인가?
    • Propagate errors
      • if true, Job 빌드 실패 시 build step도 실패이다.

More Steps

Input Step

echo "build" 
input message: 'Confirm?', ok: '확인' 
echo "confirmed"

빌드 성공, 실패

Tip. 복잡한 Pipeline 구성 시 빌드 실패 테스트가 필요하다. 강제로 특정 Step을 실패하도록 만들어 원하는 결과가 나타나는지 확인하는 것이 좋다.

빌드 실패

  • step이 실패하면 빌드가 중지된다.
    • 이 후 step들은 실행되지 않는다.
      • 빌드 결과 (currentBuild.result): FAILURE
node { 
    sh 'echo "hello"' 
    echo "RESULT: ${currentBuild.result}" 
    // do something that fails 
    sh "exit 1" 
    echo "RESULT: ${currentBuild.result}" 
}

실행 결과

  • sh "exit 1" 에서 실패가 발생하고 pipeline 실행이 중지된다.
+ echo hello 
hello 
[Pipeline] echo RESULT: null 
[Pipeline] sh 
+ exit 1 
[Pipeline] } 
[Pipeline] // node 
[Pipeline] End of Pipeline 

ERROR: script returned exit code 1 Finished: FAILURE

try - catch

  • try-catch로 빌드 실패를 막을 수 있다.
node { 
    try { 
        // do something that fails 
        sh "exit 1" 
    } catch (Exception err) { 

    } 
}

실행 결과

Running on Jenkins in /data/jenkins/workspace/pipeline_training/try-catch 

[Pipeline] { 
[Pipeline] sh 
+ exit 1 
[Pipeline] } 
[Pipeline] // node 
[Pipeline] End of Pipeline 

Finished: SUCCESS

강제 실패

currentBuild.result = 'FAILURE'
node { 
    try { 
        // do something that fails 
        sh "exit 1" 
    } catch (Exception err) { 
        // post actions 
        currentBuild.result = 'FAILURE' 
    } 
    echo "after try-catch" 
}

실행 결과: currentBuild.result=FAILURE

[Pipeline] sh 
+ exit 1 
[Pipeline] echo after try-catch 
[Pipeline] } 
[Pipeline] // node 
[Pipeline] End of Pipeline 

Finished: FAILURE

강제 실패

error step 사용

node() { 
    stage("s1") { 
        sh 'ls -al' 
    } 
    def aa stage("s2") { 
        aa = sh script: 'exit 1', returnStatus: true 
        if(aa != 0) { 
            error "hello" // <=== 여기서 실행이 중지된다. 
        } 
    } 
    stage("s3") { 
        sh 'ls -al' 
    } 
}

특정 Stage 실패

  • warnError, unstable step으로 stage 실패를 구분할 수 있다.
    • Blue Ocean에서만 지원된다. Stage View에서는 아직 지원되지 않는다.
node() { 
    stage("s1") { 
        sh 'ls -al' 
    } 
    stage("s2") { 
        sh 'ls -al' 
        // https://jenkins.io/blog/2019/07/05/jenkins-pipeline-stage-result-visualization-improvements/
        // required: Blue Ocean 
        warnError('script error') { 
            sh script: 'exit 1' 
        } 
    } 
    stage("s3") { 
        sh 'ls -al' 
    }
}

실습 02. 빌드 실패

  • A. stage 여러개 만들고, step 실패가 있으면 빌드를 중지시킨다.
  • B. stage 여러개 만들고, step 실패가 있더라도 계속 진행한다.
    • 빌드 최종 결과 = unstable
  • C. stage 여러개 만들고, step 실패가 있더라도 계속 진행한다.
    • 빌드 최종 결과 = failure
  • currentBuild = "UNSTABLE", "FAILURE"
  • steps: error, unstable, warnError

실패하는 step 예 sh 'exit 10'

종합

maven + junit

  • flow
    • git checkout -> mvn clean test -> junit, jacoco report
properties([parameters([string(defaultValue: 'test', description: 'test or verify', name: 'testType', trim: false)])
]) 

node { 
    stage("git") { 
        git 'https://github.com/hyunil-shin/java-maven-junit-helloworld.git' 
    } 
    stage('build') { 
        //withEnv(["PATH+MAVEN=${tool 'mvn-3.3.9'}/bin"]) { 
        //withEnv(["PATH+MAVEN=${tool 'mvn-3.6.2'}/bin"]) { 
        withEnv(["PATH+MAVEN=${tool 'mvn-3.6.0'}/bin"]) { 
            sh 'mvn --version' 
            sh "mvn clean ${params.testType}" 
        } 
    } 
    stage('report') { 
        junit 'target/surefire-reports/*.xml' 
        jacoco execPattern: 'target/**.exec' 
    } 
}

properties

properties([parameters([string(defaultValue: 'test', description: 'test or verify', name: 'testType', trim: false)])])
  • 빌드 속성 정의
  • Pipeline Syntax 사용하여 자동 생성 가능
  • 매개변수 접근
    • params.[매개변수명]

withEnv

  • 환경 변수 추가
  • PATH+WHATEVER=/something
    • PATH=/something:$PATH 와 동일한 의미
  • 참고. 도구 설정
    • maven
      • Jenkins 관리 > Global Tool Configuration > Maven installations

도구 선택

//tool [tool name] => tool path 반환 
withEnv(["PATH+MAVEN=${tool 'apache-maven-3.6.0'}/bin"]) { 

}

mvn 빌드

withEnv(["PATH+MAVEN=${tool 'apache-maven-3.6.0'}/bin"]) { 
    sh 'mvn --version' 
    sh "mvn clean ${params.testType}" 
}

junit, jacoco

stage('report') { 
    junit 'target/surefire-reports/*.xml' 
    jacoco execPattern: 'target/**.exec' 
}

테스트 실패

  • mvn test가 실패하면 sh step이 실패하여 이후 코드들이 실행되지 않는다.
  • try-catch 또는 maven.test.failure.ignore=true 사용
... 

    stage('build') { 
        withEnv(["PATH+MAVEN=${tool 'apache-maven-3.6.0'}/bin"]) { 
            sh 'mvn --version' 
            sh "mvn clean ${params.testType} -Dmaven.test.failure.ignore=true" 
        }
    } 
    stage('report') { 
        junit 'target/surefire-reports/*.xml' 
        jacoco execPattern: 'target/**.exec' 
    } 
}

실습 03. mvn

  • maven + junit pipeline 스크립트 작성
  • git fork (개인 repository 생성)
  • 사용해야 할 steps
    • mvn test
    • junit
    • jacoco
    • 빌드 결과를 메일로 전달받기
      • emailext step
    • archiveArtifacts
      • junit xml
  • 확장
    • 빌드 매개변수
      • properties step
      • 브랜치 선택
      • 다른 JDK 버전 사용
        • withEnv step
        • 사용 가능한 버전: openjdk8, openjdk9, openjdk10
      • 다른 maven 버전 사용
        • 사용 가능한 버전: mvn-3.6.2, mvn-3.6.0, mvn-3.3.9

Pipeline 스크립트

Pipeline Step + Groovy + Jenkins API

Groovy Syntax

Groovy - String Interpolation

def username = 'Jenkins' 
echo 'Hello Mr. ${username}' 
echo "I said, Hello Mr. ${username}"

실행 결과

Hello Mr. ${username} I said, Hello Mr. Jenkins

Parsing Json data

def body = """ { "f1": "b?", "f2": "c" }""" 
def parser = new groovy.json.JsonSlurper() 
def data = parser.parseText(body) 
echo data.f1 
echo data.f2

Jenkins API

  • 강력한 기능
    • Jenkins 모든 데이터에 접근 가능
  • 보안 때문에 Jenkins admin 승인 필요

Example: 특정 Job의 git 정보 조회

@NonCPS 
def getGitInfo(String jobName) { 
    def map1 = jenkins.model.Jenkins.instance.getItemMap() 
    def project 
    if(jobName.contains('/') == true) { 
        // folder를 사용하는 경우 
        def tmp = jobName.split('/') 
        project = map1.get(tmp[0]).getJob(tmp[1]) 
    } else { 
        project = map1.get(jobName) 
    } 
    def giturl = project.scm.getUserRemoteConfigs()[0].getUrl().toString() 
    def credential = project.scm.getUserRemoteConfigs()[0].getCredentialsId().toString() 
    println giturl 
    println credential 
    return [giturl, credential] 
}

Global Variable Reference

  • http://[your_jenkins]/pipeline-syntax/globals
  • env
  • params
  • currentBuild
  • manager

env (환경 변수)

  • env.[환경변수명] (env. 없이도 사용 가능)
echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}" 
env.MYTOOL_VERSION = '1.33' 
node { 
    echo JOB_NAME 
    sh "echo $MYTOOL_VERSION" 
    sh "echo ${env.MYTOOL_VERSION}" 
    sh 'echo $MYTOOL_VERSION' 
}

currentBuild

import groovy.json.* 

// Get all Causes for the current build 
def causes = currentBuild.getBuildCauses() 
// Get a specific Cause type (in this case the user who kicked off the build), 
// if present. 
def specificCause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
echo specificCause[0].userId echo specificCause.toString()
728x90