[Docker] Jenkins 파이프라인 사용법

[Docker] Jenkins 파이프라인 사용법

안녕하세요? 정리하는 개발자 워니즈 입니다. 지난 시간까지 젠킨스 셋팅까지 배웠습니다. 이제 원하는 업무를 수행하도록 JOB을 생성해주면됩니다. Jenkins에서는 요즘 여러가지 JOB을 원하는 입맛대로 구성하여 사용할 수 있도록 젠킨스 파이프라인을 제공해주고 있습니다. 👍

Jenkins 도커로 실행하기(1/3)
Jenkins 기본 셋업(Docker 내부에서 필요한 툴 설치)(2/3)
Jenkins 파이프라인 사용법(3/3)

1. 젠킨스 파이프라인이란?

A Jenkins pipeline is a suite of plugins which supports implementing and integrating continuous delivery pipelines into Jenkins.

위에서 보는 것처럼, 젠킨스 파이프라인은 젠킨스의 파이프라인을 구성해줄 수 있는 파이프라인 플러그인을 말하는 것이고, 이 파이프라인으로부터 제킨스의 워크플로우를 구성할 수 있다.

젠킨스 파이프라인 구성
젠킨스 파이프라인 공식문서
위의 예제에서 보듯이 워크플로우가 시작되면, 아래의 과정들로 진행됩니다.

  1. 소스 pull
  2. 빌드
  3. 테스트
  4. 배포

물론, 젠킨스의 다양한 플러그인을 이용해서 한개의 JOB에서 다른 JOB을 실행하게 하여 연결을 지을 수는 있지만, 한눈에 보기가 어렵습니다.

젠킨스의 파이프라인이란 연속적인 이벤트 혹은 Job의 그룹을 얘기합니다.
즉, 본인이 만든 젠킨스 Job들을 순차적 혹은 병렬적으로 실행시키거나 특별하게 작성한 스크립트로 이벤트들을 연속적으로 실행시키는 등의 일을 지원하는 기능입니다.

2. 젠킨스 파이프라인 타입

젠킨슴 파이프라인의 타입은 2가지를 지원합니다.

  1. 선언적 파이프라인
  2. 스크립트 파이프라인

그런데 파이프라인 문법 문서를 보면 두가지 스타일에 대한 얘기가 있습니다. 처음에는 Scripted 방식이였고 후에 Declarative 방식이 추가된 것으로 보입니다.

필자는 젠킨스 공식 문서를 보고 스크립트를 작성했는데, 개인적으로는 선언적 방식이 훨씬 간단해보여서 이방식으로 예제를 작성했습니다.

  • 선언적 방식 예제
pipeline {
    agent none
    stages{
        stage('Parallel Test') {
            parallel { 
                stage('Build-test-1') {
                    steps{ build 'Build-test-1' }
                }
                stage('Build-test-2') {
                    steps{ build 'Build-test-2' }
                }
                stage('Build-test-3') {
                    steps{ build 'Build-test-3' }
                }
            }
        }
        stage('Build-test-4') {
            steps{
                build 'Build-test-4'
            }
        }
    }
}
  • 스크립트 방식 예제
node {
  stage('Parallel-test') {
      parallel 'Build-test-1' : {
          build job : 'Build-test-1'
      } , 'Build-test-2' : {
          build job : 'Build-test-2'
      } , 'Build-test-3' : {
          build job : 'Build-test-3'
      }
  }
  stage('Build-test-4') {
     build job : 'Build-test-4'
  }
}

Scripted 문법의 경우 Groovy로 빌드되기 때문에 일반적으로 파이프라인을 생성하는데 Declarative 보다 훨씬 유연한 방법입니다.
반면, Declarative의 경우 Scripted보다 훨씬 더 간단하게 작성할 수 있는 방법입니다.
대신 고정된 방식으로만 사용해야합니다.

중요한 점은 이 2가지 문법은 서로 호환되지 않습니다.

    • A password parameter, for example: parameters { password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'A secret password') }

    Jenkinsfile (Declarative Pipeline)
    pipeline {
      agent any
      parameters {
          string(name: 'PERSON', defaultValue: 'Mr Jenkins', description: 'Who should I say hello to?')
    
          text(name: 'BIOGRAPHY', defaultValue: '', description: 'Enter some information about the person')
    
          booleanParam(name: 'TOGGLE', defaultValue: true, description: 'Toggle this value')
    
          choice(name: 'CHOICE', choices: ['One', 'Two', 'Three'], description: 'Pick something')
    
          password(name: 'PASSWORD', defaultValue: 'SECRET', description: 'Enter a password')
      }
      stages {
          stage('Example') {
              steps {
                  echo "Hello ${params.PERSON}"
    
                  echo "Biography: ${params.BIOGRAPHY}"
    
                  echo "Toggle: ${params.TOGGLE}"
    
                  echo "Choice: ${params.CHOICE}"
    
                  echo "Password: ${params.PASSWORD}"
              }
          }
      }
    }
    
  • triggers : cront, pollSCM, upstream 등 여러방식으로 트리거를 구성할 수 있다.
    ex. 새벽 3시마다 빌드하기

    Jenkinsfile (Declarative Pipeline)
    pipeline {
      agent any
      triggers {
          cron('H */4 * * 1-5')
      }
      stages {
          stage('Example') {
              steps {
                  echo 'Hello World'
              }
          }
      }
    }
    

4. 젠킨스 파이프라인 예시

필자는 처음에 젠킨슴 파이프라인을 도입하게 된 이유는 여러가지 JOB들을 연계해서 한번에 실행하고 싶었고, 그것을 한눈에 보기 쉽게 대시보드로 보고싶었다.

물론, RUNDECK이라는 CD툴을 이용하면 손쉽게 workflow 구성이 가능하다.
하지만, 젠킨스의 다양한 플러그인들을 포기할 수 없었고, 파이프라인구성으로 RUNDECK처럼 꾸며보고 싶었다.

필자는 다음의 업무를 수행하고 싶었다. (필자의 업무기에 모든 내용을 전달할 순 없지만, 그냥 여러가지 일을 파이프라인으로 연결시키는것에 주목해주길 바랍니다.)

  • 라이브 서버에서 SOURCE(컨텐츠)를 추출합니다.
  • 추출한 SOURCE(컨텐츠)를 검증용 서버에 설치합니다.
  • 검증용 서버에서 SOURCE(컨텐츠)를 조작(컨텐츠 url 변경) 합니다.
  • 검증용 서버에서 변경된 SOURCE(컨텐츠)를 추출합니다.
  • 라이브 서버에 변경된 SOURCE(컨텐츠)를 설치합니다.

즉, 현재 라이브 서버에 있는 컨텐츠를 가져와서 검증용 서버에서 변경하고, 다시 라이브서로 옮기는 작업이다.

상위의 작업을 진행하려면, 총 5번의 JOB을 수행해야하고, 매번 INPUT을 입력해줘야하기 때문에 실수가 발생할 수 있다.

def ZIP_FILE

pipeline {
    agent any
    parameters {

        choice(name: 'SOURCE',choices: "라이브 서버1\n라이브 서버2\n라이브 서버3",description: 'SOURCE-SERVER' )

        choice(name: 'TARGET',choices: "검증용 서버1\n검증용 서버2\n검증용 서버3",description: 'TARGET-SERVER' )

        text defaultValue: '''/content/path''', description: '소스 추출', name: 'SOURCECONTENT'

        text defaultValue: '''/content/path''', description: '컨텐츠 카피', name: 'COPYCONTENT'

        text defaultValue: '''/content/path''', description: '타겟 추출', name: 'TARGETCONTENT'

    }
    stages {
        stage('Source - Build') {
            steps {
                script{
                      def myjob=build job: 'LIVE_extract_Server', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.SOURCE}"], string(name: 'TITLE', value: 'B2C_AP_Author_Build_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILTER', value: "${params.SOURCECONTENT}")]
                      def jobDisplayName=myjob.getDisplayName()
                      ZIP_FILE = jobDisplayName.split('#')[1] + ".zip"
                }

            }
        }
        stage('Target - Install(ITG)') {
            steps {
                script{
                    def myjob=build job: 'STG_Deploy_Server', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.TARGET}"], string(name: 'TITLE', value: 'B2C_AP_Author_Deploy_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILE', value:ZIP_FILE)]
                }
            }
        }
        stage('Target - Copy') {
            steps {
                script{
                    println ZIP_FILE
                    def myjob=build job: 'STG_PageTool_Server_APPS', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.TARGET}"], string(name: 'TITLE', value: 'B2C_AP_Author_Copy_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), string(name: 'ACTION', value:'copy'), text(name: 'PAGE', value:"${params.COPYCONTENT}")]
                }
            }
        }
        stage('Target - Build') {
            steps {
               script{
                      def myjob=build job: 'STG_Build_Server', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.TARGET}"], string(name: 'TITLE', value: 'B2C_AP_Author_Build_APPS_flag'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILTER', value: "${params.TARGETCONTENT}")]
                      def jobDisplayName=myjob.getDisplayName()
                      ZIP_FILE = jobDisplayName.split('#')[1] + ".zip"
                }
            }
        }
        stage('Source - Upload(LIVE)') {
            steps {
                script{
                    def myjob=build job: 'LIVE_Deploy_Server', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.SOURCE}"], string(name: 'TITLE', value: 'B2C_AP_Author_Deploy_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILE', value:ZIP_FILE)]
                }
            }
        }
    }
}

간단하게 하나씩 설명을 하면,

  • 파라미터 셋팅
choice(name: 'SOURCE',choices: "라이브 서버1\n라이브 서버2\n라이브 서버3",description: 'SOURCE-SERVER' )

        choice(name: 'TARGET',choices: "검증용 서버1\n검증용 서버2\n검증용 서버3",description: 'TARGET-SERVER' )

        text defaultValue: '''/content/path''', description: '소스 추출', name: 'SOURCECONTENT'

        text defaultValue: '''/content/path''', description: '컨텐츠 카피', name: 'COPYCONTENT'

        text defaultValue: '''/content/path''', description: '타겟 추출', name: 'TARGETCONTENT'

2개의 choice 파라미터와, 3개의 멀티라인 파라미터를 셋팅했습니다.

  • job 호출
stage('Source - Build') {
            steps {
                script{
                      def myjob=build job: 'LIVE_extract_Server', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.SOURCE}"], string(name: 'TITLE', value: 'B2C_AP_Author_Build_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILTER', value: "${params.SOURCECONTENT}")]
                      def jobDisplayName=myjob.getDisplayName()
                      ZIP_FILE = jobDisplayName.split('#')[1] + ".zip"
                }

            }
        }

제일 처음에는 stages라는 block으로 전체 stage를 구성해줍니다.
그리고 각각, stgae를 맞춰서 작성합니다.

상위의 예는 1개를 뒀지만, 사실 5개의 job호출 형태는 모두 동일하기 때문에 1개에 대해서만 설명드리겠습니다.

  1. 스테이지 명시
stage('Source - Build') {
}
  1. 스텝 지정
steps {
}
  1. 스크립트 작성
script{
                      def myjob=build job: 'LIVE_extract_Server', parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.SOURCE}"], string(name: 'TITLE', value: 'B2C_AP_Author_Build_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILTER', value: "${params.SOURCECONTENT}")]
                      def jobDisplayName=myjob.getDisplayName()
                      ZIP_FILE = jobDisplayName.split('#')[1] + ".zip"
                }

스크립트의 설명은 LIVE_extract_Server라는 job을 호출합니다.

parameters: [[$class: 'com.cwctravel.hudson.plugins.extended_choice_parameter.ExtendedChoiceParameterValue', name: 'SERVER', value: "${params.SOURCE}"], string(name: 'TITLE', value: 'B2C_AP_Author_Build_APPS'), password(description: '', name: 'PASSWORD', value: 'password'), text(name: 'FILTER', value: "${params.SOURCECONTENT}")]

파라미터 셋팅 같은 경우,

extendedChoiceParameter

String

password

text

타입으로 파라미터를 넘깁니다.

파이프라인 대시보드

5. 마치며

젠킨스 파이프라인 구성을 알아봤습니다. 정리를 하면, 우선 2가지 타입(선언, 스크립트)이 존재하며, 필자가 설명드린 선언방식의 문법 에 대해서 한가지씩 알아봤습니다.

파이프라인을 사용하는 가장큰 이유는 job들의 workflow 구성일 것입니다. 위의 방식으로 손쉽게 구성을 할 수 있을것이라고 생각이 듭니다.
워니즈 블로그
워니즈 깃헙

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다