Jenkins - Declarative Pipeline

namkun·2023년 1월 30일
0

Jenkins

목록 보기
1/1
post-thumbnail

Pipeline? 

Jenkins pipeline은 연속적인 이벤트 혹은 Job 의 그룹을 말합니다.
즉, 본인이 만든 Jenkins Job들을 순차 혹은 병렬로 실행시키거나, 특별하게 작성된 스크립트로 이벤트들을 연속적으로 실행시키는 등의 일을 지원하는 기능이라고 할 수 있습니다.
Pipeline은 pipeline DSL(domain-specific language) 을 통해서 파이프라인을 "코드"로 생성할 수 있습니다.

pipeline script에는 2가지 문법 종류가 있습니다. 

  • Scripted pipeline
  • Declarative pipeline

이 2 가지의 문법은 서로 호환이 되지 않기에, 스크립트를 작성할 경우 한 가지 문법으로만 작성해야합니다.

두 가지 문법의 차이라면, Scripted는 일반적으로 파이프라인을 생성하는데 더욱 유연하나 Declarative에 비해 복잡한 편이며, 유지보수하기 어렵습니다.

Declarative는 scripted 보다 훨씬 간단하게 작성할 수 있으나, 고정된 방식으로만 사용해야하기에 제한된다는 단점이 있습니다.

여기서는 Declarative로 설명하도록 하겠습니다. 훨씬 간단하고, 가독성이 좋아서 코드를 이해하기에도 쉽습니다. (그리고 어차피 필요한 scripted pipeline구문은 script 블록을 통해서 declarative pipeline에서도 사용할 수 있습니다. 이게 가능한 이유는 declarative pipeline이 scripted pipeline 기반으로 만들어졌거든요.)

(Scripted 에 관심이 있다면 구글링해보시면 정리 잘 해주신 분들이 나옵니다.)


Declarative Pipeline 문법

Declarative Pipeline 의 구조는 다음과 같이 정의됩니다.

하나의 Block 안에 Directive와 Section을 포함하는 코드

무슨 소리인가 싶습니다. 하나씩 알아봅시다

Block

블록은 시작과 끝이 { } 로 이루어진 코드의 묶음입니다. 

pipeline{
 // Declarative Pipeline code...
}

Section

섹션은 전체 파이프라인의 흐름 내에서 특정한 시점에 실행이 필요한 item 들을 묶는 방법 입니다.

섹션은 또 다음과 같은 3가지로 나뉩니다.

  • stages
    • 파이프라인의 중심 로직을 정의하는 각각의 stage를 묶는 섹션입니다.
  • steps
    • stage의 step을 묶는 섹션입니다.
    • 환경 변수와 같은 stage의 다른 item으로부터 step을 분리하는 역할을 합니다. 
  • posts
    • stage의 끝이나, pipeline의 실행의 마지막에 실행되거나 확인되어야 할 step과 조건을 묶는 세션입니다.

Directive

Directive는 파이프라인에서 값을 정의하고 행동을 설정하고 수행할 작업을 명시하는 지시문, 코드블록을 의미합니다.

그럼 위에서 말한 것들이 어떻게 사용되는지 자세히 알아봅시다.

Declarative Pipeline의 코드 구조는 다음과 같습니다. (block 처리는 필수요소 입니다.)

  • pipeline
    • agent
      • docker
      • dockerfile
    • environment
    • Input
    • options
    • parallel
    • parameters
    • Script
    • tools
    • triggers
    • when
    • libraries
    • stages
      • steps
    • post

필수 요소

pipeline

  • pipeline 블록은 Declarative Pipeline 의 필수 요소입니다. 
    간단하게 pipeline{ } 으로 표시되고 내부에 코드들이 들어가게 됩니다.

agent

  • Jenkins는 분산 빌드를 agent 노드에 위임해서 수행하는 기능을 제공합니다. 이렇게 하면 작업량이 agent에 분산되는 동안 Jenkins 서버의 인스턴스 하나만 사용해서 여러개의 프로젝트를 실행시킬 수 있습니다.
  • agent 는 파이프라인 또는 스테이지를 실행하기 위해서 필요한 노드를 지정합니다.
  • 여기에 명시된 매개변수는 젠킨스 시스템에 존재하는 노드의 label를 찾아가서 연결합니다. (젠킨스의 설정내에 있습니다.) 
  • label은 플랫폼 (linux, windows...), 버전, 위치등을 이용해서 식별하기 쉽도록 명명해야합니다.
  • 옵션
    • agent any
      • 해당 파이프라인이나 스테이지에 상관없이 어떠한 agent에서 실행될 수 있음을 말합니다.
      • 이 옵션을 선택하면 Jenkins가 알아서 사용가능한 노드중 하나에서 작업을 실행합니다.
    • agent none
      • 최상위 레벨에서 사용하면 전역 범주에서 agent 지정을 무효화 시킵니다.
    • agent { label 'label name'}
      • 파이프라인이나 스테이지가 label name 인 레이블을 가진 agent에서 실행될 수 있음을 의미합니다. 
    • agent { docker { <element> } }
      • docker의 agent를 자세하게 정의합니다. 
      • element에는 다음과 같은 요소를 사용할 수 있습니다.
        • image '<image>': 젠킨스가 이미지를 내려받아서 파이프라인 코드 실행시에 사용합니다.
        • label '<label>' : 컨테이너를 초기화해서 '<label>' 과 매칭되는 노드에 제공합니다.
        • args '<string>' : 해당되는 매개변수를 도커 컨테이너에 전달합니다.
    • agent {dockerfile <option> }
      • Dockerfile로 부터 이미지를 생성할 때, 세부적인 옵션에 대해 정의합니다.
        • true : Dockerfile이 소스코드 저장소 최상단에 있을때 사용합니다. 해당 Dockerfile을 이용해서 이미지를 빌드하고 컨테이너를 초기화한 뒤에 파이프라인을 해당 컨테이너에서 실행시킵니다.
        • { filename '<path of dockerfile>'} : Dockerfile의 경로가 다를 경우 사용합니다. dockerfile을 직접 지정할 수 있습니다.
        • { label '<label>' } : 컨테이너를 초기화해서 이를 label에 매칭되는 노드에 제공합니다.
        • { args '<string>' } : <string>에 입력된 매개변수를 컨테이너에 전달합니다.

stage

  • Stages 에는 하나 이상의 stage Section을 정의해야합니다.
    하나의 Stage section에는 pipeline이 실행할 작업을 포함합니다.
  • Jenkins는 각 Stage를 인터페이스에 표시하므로, Stage는 해당 section이 어떤 작업을 하느냐에 따라 명명해야합니다.
  • stage는 다음과 같이 작성합니다.
    pipeline {
        agent any
        stages {
            stage ('build') {
                ...
            }
            stage ('test: functional') {
                ...
            }
            stage ('deploy') {
                ...
            }
        }
    }

steps

  • steps 은 Stage section안에 정의됩니다. 
    여기서는 보통 우리가 원하는 작업에 관련된 코드를 입력합니다. 

  • 예를 들면 sh 명령어를 통해서 linux 상에서 명령어를 실행 시킨다거나 하는 것을 한다고 보시면 됩니다.

    pipeline {
    
        agent any
    
        stages{
            stage('hello world'){
                steps{
                    sh 'echo "Hello World!"'
                }
            }
        }
    }

그 외 옵션들

Environment

  • 파이프라인 내의 환경변수를 지정하는 역할을 합니다.

  • environment를 최상단에 지정하면 파이프라인의 모든 Step에서 접근이 가능합니다.

  • 반대로 Stage 내에서 지정되면 그 Stage 에서만 접근이 가능하게 됩니다. 

    • pipeline 수준에서 사용

      pipeline{
          environment{
              NAME = "namkun"
              FULL_NAME = "KIM_${NAME}"
          }
      }
    • stage 수준에서의 사용

      pipeline{
          agent any
      
          stages{
              environment{
                  NAME = "namkun"
              }
      
              stage('name'){
                  steps{
                      sh 'echo ${NAME}'
                  }
              }
          }
      }
    • 사용이 필요한 곳에 맞게 위의 예시들 처럼 pipeline 수준이나 stage 수준에서 사용할 수 있습니다.

Input

  • input은 Stage 안에서 정의되며, 해당 파이프라인을 실행시켰을 때, 입력을 요구하도록 합니다. 

  • 사용자가 입력하기 전까지 스테이지는 일시 중지됩니다.

  • input에서는 다음과 같은 옵션을 제공합니다.

    • message : user에서 입력을 요구할 때 띄울 메세지를 지정합니다.
    • id : 입력 식별자 입니다. 해당 옵션을 따로 지정하지 않으면 stage의 이름을 사용합니다.
    • ok : ok 버튼의 텍스트를 지정합니다. 지정하지 않으면 ok 를 그대로 사용합니다.
    • submitter : 입력할 수 있는 user 또는 group을 지정합니다. 따로 지정하지 않으면 기본적으로는 모든 사용자가 허용됩니다.
    • submitterParameter : submitter 이름으로 설정하는 환경변수의 이름을 지정합니다. (존재하는 경우만 지정합니다.)
    • parameters : submitter에 의해 제공되는 파라미터 옵션입니다. input에서는 parameters 블록이 안에 있지만, 추후에 나오는 parameters 블록은 stages 블록 외에 존재합니다.
    pipeline{
        agent any
    
        stages{
            stage('build'){
                input{
                    message "계속하려면 OK 버튼을 눌러주세요"
                    submitter "user1, user2"
                    parameters{
                        string(name : 'username', defaultValue: 'user', description: 'ok 버튼을 누른 사람입니다')
                    }
                }
    
                steps{
                    echo "User : ${username} 가 Ok를 눌렀습니다."
                }
            }
        }
    }

options

  • options 는 전체 파이프라인에 대해서 적용할 수 있는 기능들입니다.

  • 전체 파이프라인에 대한 옵션이기에, 파이프라인 단계에서 사용되어야 합니다.

  • options에 정의된 기능들 중 몇 가지 기능만 설명하도록 하겠습니다. 나머지는 공식 문서를 확인하시면 됩니다.

    • retry() : pipeline이 실패하면 몇 번 재시도를 할 지 정합니다.
    • buildDiscarder() : 최근에 실행된 빌드의 console output과 artifacts를 몇 개를 유지할 지 지정합니다.
    • disableConcurrentBuilds() : 동시에 여러개의 빌드하는 것을 방지합니다.
    • disableResume() : Jenkins가 다시 시작했을 경우 자동으로 시작하는 것을 방지합니다. 
    • timeout() : pipeline 빌드시에 timeout을 건다. 만약 해당 시간을 초과한 경우 FlowInterruptedException을 발생시킵니다.
    • parallelsIsAlwaysFailFast() : Parallel 스테이지중 하나가 실패하는 경우에는 다른 parallel 스테이지도 멈추고 해당 Build의 상태를 실패로 바꿉니다.
    pipeline{
        agent any
    
        options{
            buildDiscarder(
                logRotator(
                    artifactDaysToKeepStr: "30", // artifacts를 보관할 일자
                    artifactNumToKeepStr: "100", // artifacts를 보관할 개수
                    daysToKeepStr: "60", // console output을 보관할 일자
                    numToKeepStr: "200" // console output을 보관할 개수
                )
            )
            retry(2) // 실패시에 재시도할 횟수 입력
            disableConcurrentBuilds()
            disableResume() 
            timeout(time: 300, unit: "SECONDS") // timeout 시간 정의
            parallelsIsAlwaysFailFast()
        }
        ...
    }

Parallel

  • Stage 내에서 병렬로 실행될 다른 Stage를 선언하는 기능입니다. 

  • 간단하게 stage안에 parallel Block을 선언하고 사용하면 됩니다. 

    pipeline{
        agent any
    
        stages{
            stage('parallel'){
                parallel{
                    stage('hello'){
                        steps{
                            echo "hello"
                        }
                    }
                    stage('world'){
                        steps{
                            echo "world"
                        }
                    }
                }
            }
        }
    }
  • Parallel은 다음과 같은 제약사항이 있으니 주의해야합니다.

    • stage directive안에 parallel directive와 step directive를 둘 다 동시에 사용할 수 없습니다.
    • parallel directive 안에 있는 stage directive에서는 parallel directive를 중첩해서 사용할 수 없습니다. 오직 step directive만 사용할 수 있습니다.
    • parallel directive 안에 있는 stage directive에서는 "agent"나 "tools"를 사용할 수 없습니다. 

Parameters

  • pIpeline script에서 사용할 Parameter들을 정의하는 역할을 합니다. 

  • parameters는 pipeline 단계에서 정의해야 하고, 사용할 수 있는 유효한 타입은 String 과 boolean입니다.

  • 추가로 plugin을 설치해서 gitParmeter등의 다른 타입의 파라미터를 정할 수 있습니다. (관련해서는 해당 plugin의 공식문서를 보는게 좋습니다.)

    pipeline{
        agent any
    
        parameters{
            string(name : 'USER' , defaultValue : 'namkun', description : 'user who triggers the pipeline')
    
            gitParameter(
                branch: '',
                branchFilter: "origin/(.*)",
                defaultValue: "dev",
                description: "배포할 브랜치 선택",
                listSize: '20',
                name: 'BRANCH',
                quickFilterEnabled: false,
                selectedValue: 'NONE',
                sortMode: 'ASCENDING_SMART',
                tagFilter: "*",
                type: 'PT_BRANCH',
                useRepository: 'repository url'
            )
    
            choice(
                name : 'NUM',
                choices: ['1', '2', '3', '4', '5'],
                description: ""
            )
      }
    
        stages{
            stage('print'){
                steps{
                    echo "pipeline triggered by ${params.USER}"
                }
            }
        }
    }

Post

  • post는 pipeline 단계 또는 stage 단계 다음에 조건에 맞게 실행할 것들을 넣는 section입니다. 

  • post의 옵션을 통해서 조건을 걸 수 있습니다. 

    • always : post section은 해당 pipeline or stage의 상태와 상관없이 실행됩니다.
    • changed : 이번 빌드의 상태가 이전 빌드의 상태와 달라졌다면 실행됩니다.
    • fixed : 만약 이전 빌드의 상태가 failure이고, 현재 빌드의 상태가 Success이면 실행합니다.
    • regression : 만약 이전 빌드의 상태가 success이고, 현재 빌드가 fail, abort, unstable 상태면 실행합니다.
    • aborted : 해당 pipeline or stage의 상태가 aborted면 해당 블록을 실행합니다.
    • failure : 해당 pipeline or stage의 상태가 fail이면 해당 블록을 실행합니다.
    • success : 해당 pipeline or stage의 상태가 success면 해당 블록을 실행합니다.
    • unstable : 해당 pipeline or stage의 상태가 unstable이면 해당 블록을 실행합니다.
    pipeline{
        agent any
    
        stages{
            stage('stage1'){
                steps{
                    ...
                }	
            }
        }
    
        post{
            always{
                echo "pipeline finished!"
            }
        }
    }

Script

  • Declarative pipeline 구문에서 표현할 수 없는 것이 생기면 script 블록을 사용해서 scripted pipeline의 구문을 사용할 수 있습니다..
  • for-loop, try-catch등의 구문을 사용할 수 있게 됩니다.
    pipeline {
        agent any
        stages{
            stage('batch'){
                steps{
                    script{
                        try{
                            ...
                        } catch(e){
                            currentBuild.result = 'FAILURE'
                            throw e
                        }
                    }
                }
            }
        }
    }

Tools

  • 파이프라인 내에서 빌드할 때 필요한 도구(jdk, gradle, maven...)들을 여기서 지정해줄 수 있습니다.

  • agent none 으로하면 tools를 활성화 시킬 노드나 에이전트가 없기에 지정해도 해당 tool을 원하는대로 사용할 수 없습니다.

  • 이렇게 지정하면 따로 직접 도구를 설치해줄 필요가 없습니다. 실행하면 알아서 설치됩니다.

  • 그러나 docker, dockerfile agent에서는 동작하지 않으니, 그럴때는 설치되어있는 이미지를 사용해서 처리하도록 합시다.

  • Tool은 Jenkins 옵션에서 따로 이름을 지정해서 사용합니다. 고로 필요하면 Jenkins 에서 따로 지정하시면 됩니다.

    pipeline {
      agent any
    
      tools {
        nodejs "nodeJS-14.17.3"
        git "Default"
      }
    }

Triggers

  • triggers는 트리거를 사용해서 파이프라인의 빌드를 시작시킬 수 있도록 지정하는 방법입니다.

  • triggers에는 다음과 같은 종류가 있습니다.

    • cron : cron 구문을 사용하여 파이프라인이 다시 시작하는 시기를 정의할 수 있습니다. 

      pipeline {
          agent any
          triggers {
              //Execute weekdays every four hours starting at minute 0
              cron('0 */4 * * 1-5')
          }
          stages {
              ...
          }
      }
    • pollSCM : cron 구문을 사용해서 Jenkins가 Git이 업데이트를 확인하는 시점을 정의할 수 있습니다. 변경시에는 다시 파이프라인을 시작합니다. 

      pipeline {
          agent any
          triggers {
              //Query repository weekdays every four hours starting at minute 0
              pollSCM('0 */4 * * 1-5')
          }
          stages {
              ...
          }
      }
    • upstream :  Jenkins Job과 threshold를 입력하여서 지정한 Jenkins job의 threshold 조건을 충족시키면 해당 파이프라인을 시작합니다.

      pipeline {
          agent any
              triggers {
                  //Execute when either job1 or job2 are successful
                  upstream(upstreamProjects: 'job1, job2', threshold: hudson.model.Result.SUCCESS)
              }
          stages {
                  ...
          }
      }

When

  • when 블록 안에 있는 조건이 일치하면, 그 다음의 단계를 실행합니다. 

  • stage 내부에서만 선언할 수 있습니다. 

  • 다음과 같이 사용할 수 있습니다. 

    pipeline{
        stages{
            stage('test'){
                when {
                    expression {
                        STATUS == false 
                    }
                }
    
                steps{
                    ....
                }
            }
        }
    }

    expression 블록을 사용해서 상태확인을 하면, 그 아래의 steps 블록을 실행하는 구조입니다.


이 글은 사실 사용자가 맨날 까먹어서 기억을 잃지 않기 위해 작성하였습니다.

profile
개발하는 중국학과 사람

0개의 댓글