젠킨스 파이프라인 사용하여 자동 배포환경 만들어보기!
지난 글(젠킨스 사용하여 자동 배포환경 만들어보기) 에서 Jenkins Build Pipeline 으로 자동 배포 시스템을 구축하는 것에 대해 공유를 했었습니다.
댓글을 통해 Jenkins Pipeline
이라는게 있다는 것을 처음 알고 되고..!!
그 때서야, 젠킨스 2.0 의 가장 큰 변화가 Jenkins Pipeline
이라는 것을 알게 되었습니다. 이 때부터 마음의 숙제로 가지고 있었지만, 최근 블로그 통계로 젠킨스 블로깅 글의 방문이 높다는 것을 알고 급하게(groovy를 제대로 숙지하지 못한 채..ㅜㅜ) 글을 작성하게 되었습니다.
(최근 이 짤을 굉장히 많이 쓰게 됩니다..)
Jenkins Pipeline
이란 스크립트를 통해 파이프라인의 흐름을 정의하는 기능입니다. 이 스크립트는 groovy
로 작성되며, Pipeline DSL을 통해 전달 파이프라인을 작성할 수 있습니다.
Jenkins Pipeline에 대한 자세한 설명은 생략!
그리하여, 저 또한 지난 Build Pipeline
으로 작성했던 시스템을, Pipeline Script
기반으로 모두 전환하였습니다.
구축한 내용과 결과를 공유합니다.
구축하고자 하는 환경
구축하고자 하는 환경은 지난 Build Pipeline 으로 구축했을 당시의 조건을 그대로 구축하는 것 입니다.
Git 연동이나, Gradle, Maven 빌드 등 기본적인 조건을 제외하고 가장 원했던 조건은 아래와 같습니다.
- 공통 내용을 모듈화하여 공용으로 사용할 수 있는 구조.
- 각 단계에 대한 수동 실행이 가능한 구조.
구축
Jenkins File
젠킨스는 Pipeline Script
를 Admin Web에서 직접 작성하여 저장하는 방법과, Git을 통해 JenkinsFile 을 읽는 방법을 제공합니다.
만들고자 하는 스크립트는, 배포 Flow가 지나치게 다르지 않은 프로젝트에 한하여, 모두 동일한 스크립트를 사용할 수 있도록 구성하여 파라미터 기반으로 실행될 수 있는 스크립트 입니다.
그렇기 때문에 저장소에 대한 수정만으로 모든 프로젝트에 적용할 수 있는 Git을 통해 JenkinsFile 을 읽는 방법을 사용합니다.
groovy 사용이 미숙하여, 코드가 더러운 점은 양해 부탁드립니다. groovy 사용성을 숙지하여 추후 리펙토링할 예정입니다.
Stage
젠킨스 Pipeline Script
는 실행에 대한 단위인 node
와 파이프라인을 구성하는 stage
가 있습니다. node
와 stage
간에는 먼저 선언되야 하는 것은 없이 작성할 수 있습니다.
node('', {
stage('example1', {
echo ""
})
})
stage('example2', {
node('', {
echo ""
})
})
Pipeline Script
는 병렬 실행을 지원하기 때문에 실행 단위를 분리해야할 경우 node
를 사용하여 실행 단위를 분리할 수 있고, Pipeline
의 단계를 stage
로 구성하게 됩니다.
자동 배포를 위해 만드려고 하는 Stage는 아래와 같습니다.
Flow Check
-Parameter Check
-Git CheckOut
-Test
-Build
-Deploy
-Switch
파라미터 정의
Flow
각 잡 실행 Pipeline에서 진행할 Stage
를 정의하는 파라미터들입니다.
USE_TEST
: Test 단계 사용 유무
USE_BUILD
: Build 단계 사용 유무
USE_DEPLOY
: Deploy 단계 사용 유무
USE_SWITCH
: Switch 단계 사용 유무
Module
각 프로젝트 Pipeline Job에서 넘겨주어야 할 모듈 정보입니다.
GRADLE_VERSION
: Gradle Version
JAVA_VERSION
: Java Version
NODE_VERSION
: Node Version (Optional)
SLACK_TOKEN
: Slack Token (Optional)
Git
소스 Check Out 을 위한 정보입니다.
GIT_URL
: Git Repository Url
BRANCH_SELECTOR
: 대상 Branch
Deploy
그리고 각 배포 환경에 따라 정의되어야 하는 파라미터들이 있습니다. 아래 파라미터는 제가 배포해야할 환경에 필요한 파라미터로 각 배포 환경에 맞게 구성하시면 됩니다.
CONFIG_NAME
, REMOTE_PATH
, TARGET_USER
, TARGET_SERVER
변수 정의
Flow
def useTest = true
def useBuild = true
def useDeploy = true
def useSwitch = true
각 단계에 대한 수동 실행이 가능한 구조
를 위해 사용되는 변수들입니다. 이 변수들은 Stage
내부에서 확인될 것 입니다.(Stage를 분기처리하면 전체 Flow가 망기지기 때문에) 기본 값은 모두 실행되는 것이며, 이후 아래에서 설명할 Pipeline Job을 만드는 과정에서 이 값들이 어떻게 쓰일지 다시 한번 설명하도록 하겠습니다.
Module
def useNode = true
def useSlack = true
해당 모듈을 사용할지 결정하는 변수입니다. 모든 프로젝트가 Gradle Build라는 가정하에 적성하여, useGradle
, useMaven
는 작성하지 않았습니다. 필요하다면 작성하셔서 사용하면 좋을 것 같습니다.
1. Flow Check
이 과정은 해당 빌드가 어떤 스테이지를 실행하는지 확인할 수 있도록 작성한 Stage
입니다.
stage("Flow Check", {
try {
println " TEST FLOW = $USE_TEST"
useTest = "$USE_TEST" == "true"
}
catch (MissingPropertyException e) {
println " TEST FLOW = true"
}
try {
println " BUILD FLOW = $USE_BUILD"
useBuild = "$USE_BUILD" == "true"
}
catch (MissingPropertyException e) {
println " BUILD FLOW = true"
}
try {
println " DEPLOY FLOW = $USE_DEPLOY"
useBuild = "$USE_DEPLOY" == "true"
}
catch (MissingPropertyException e) {
println " BUILD DEPLOY = true"
}
try {
println " SWITCH FLOW = $USE_SWITCH"
useBuild = "$USE_SWITCH" == "true"
}
catch (MissingPropertyException e) {
println " SWITCH FLOW = true"
}
})
$
로 시작하는 이름들은 파라미터로 넘겨받을 환경 변수들입니다. 해당 환경변수가 설정되지 않았을 때 MissingPropertyException
이 발생하지만, 이 값들은 없으면 Default 값으로 동작하도록 예외처리를 하였습니다.
2. ParameterCheck
stage("Parameter Check", {
println " BUILD_USER = " + BUILD_USER
println " CONFIG_NAME = $CONFIG_NAME"
println " REMOTE_PATH = $REMOTE_PATH"
println " TARGET_USER = $TARGET_USER"
println " TARGET_SERVER = $TARGET_SERVER"
println " GIT_URL = $GIT_URL"
println " BRANCH_SELECTOR = $BRANCH_SELECTOR"
println " GRADLE_VERSION = $GRADLE_VERSION"
println " JAVA_VERSION = $JAVA_VERSION"
env.JAVA_HOME="${tool name : JAVA_VERSION}"
env.PATH="${env.JAVA_HOME}/bin:${env.PATH}"
try {
println " SLACK_TOKEN = $SLACK_TOKEN"
}
catch (MissingPropertyException e) {
useSlack = false
}
try {
println " NODE_VERSION = $NODE_VERSION"
}
catch (MissingPropertyException e) {
useNode = false
}
})
파라미터를 검증하는 Stage
입니다. 환경변수가 없을 때는 예외를 발생시키므로, 필수 사용 파라미터에 대해서는 예외처리를 하지 않습니다.
3. Git CheckOut
stage("Git CheckOut", {
if (useTest || useBuild) {
println "Git CheckOut Started"
checkout(
[
$class : 'GitSCM',
branches : [[name: '${BRANCH_SELECTOR}']],
doGenerateSubmoduleConfigurations: false,
extensions : [],
submoduleCfg : [],
userRemoteConfigs : [[url: '${GIT_URL']]
]
)
println "Git CheckOut End"
} else {
println "Git CheckOut Skip"
}
})
넘겨받은 GitUrl
과 Branch
를 사용하여 Git Check Out 을 실행하는 Stage
입니다. Test
나 Build
중 하나라도 Flow에 포함되어 있다면 이 단계가 실행됩니다.
4. Test
stage('Test') {
if (useTest) {
println "Test Started"
try {
sh "${tool name: GRADLE_VERSION, type: 'hudson.plugins.gradle.GradleInstallation'}/bin/gradle test -Dorg.gradle.daemon=true"
} finally {
junit allowEmptyResults: true, keepLongStdio: true, testResults: 'build/test-results/*.xml'
}
println "Test End"
} else {
println "Test Skip"
}
}
넘겨받은 Gradle Version을 사용하여, test Task 를 실행시킵니다. 테스트가 완료된 후에는 테스트 결과를 수집합니다. testResults는 각 프로젝트 환경에 맞는 Path
를 작성합니다.
5. Deploy, Switch(Manual Flow)
두 Stage
는 독립된 Stage
이지만 자세한 내용을 공개할 수 없으므로, 간단히 작성합니다.
stage("Deploy", {
if(useDeploy) {
println "Deploy Started"
sh "****"
println "Deploy End"
} else {
println "Deploy Skip"
}
})
stage("Switch", {
if(useSwitch) {
try {
input("스위칭 하시겠습니까?")
println "Switch Started"
sh "****"
println "Switch End"
} catch (Exception e) {
println "Switch Skip"
}
} else {
println "Switch Skip"
}
})
Manual Flow
를 위해 input을 사용합니다. flow는 input을 만나게되면, 사용자가 proceed
혹은 abort
를 선택하기 전까지는 대기 상태로 들어갑니다.
abort
를 선택하면 예외가 발생하지만, 그렇다고 해당 Stage
가 실패한 것은 아니라고 생각하여 예외처리를 작성하였습니다.
기타. Node 사용하기
gradle 빌드 과정에 node 를 사용해야 할 task가 구성되어 있을 때 Nvm Wrapper Plugin을 사용합니다.
if (useNode) {
nvm('version' : "${NODE_VERSION}") {
stage('Test') {
if (useTest) {
println "Test Started"
~~
println "Test End"
} else {
println "Test Skip"
}
}
stage("Build", {
if(useBuild) {
println "Build Started"
~~
println "Build End"
} else {
println "Build Skip"
}
})
}
} else {
stage('Test') {
if (useTest) {
println "Test Started"
~~
println "Test End"
} else {
println "Test Skip"
}
}
stage("Build", {
if(useBuild) {
println "Build Started"
~~
println "Build End"
} else {
println "Build Skip"
}
})
}
nvm이 {}
scope 안에서만 사용 가능하여 위와 같이 else문에 똑같은 중복 코드가 발생하는 로직을 일단 사용하였습니다…
groovy 학습 후 리펙토링을..ㅜㅜ
Pipeline Template Job 만들기
위의 스크립트는 많은 파라미터를 필요로 합니다. 프로젝트를 추가해야할 때마다 매번 파라미터를 추가하는 노가다를 피하기 위하여, Template로 사용할 수 있는 Pipeline Template Job
을 만드려고 합니다.
생성
new Job에서 Pipeline을 선택합니다.
Jenkins File Git SCM 연동
Pipeline script from SCM 를 사용하여, 필요한 정보를 채워줍니다.
Script Path
에 작성한 Jenkins File 경로를!
Parameter 설정
Flow
캡쳐본과 위에서 설명한 스크립트가 조금 다릅니다. SCP, DRONE, QUEEN이 DEPLOY, SWITCH 입니다. 해당 파라미터를 Boolean Parameter
로 설정합니다.
Module
이 단계에서는 Extensible Choice Parameter plugin를 잠시 소개하겠습니다.
Extensible Choice Parameter plugin
은 Global 로 등록해놓은 Choice Parameter 를 사용할 수 있도록 되어 있습니다. 즉 Choice Parameter 이지만, 중앙 관리가 가능하도록 제공하는 Plugin 입니다.
이 Plugin을 통해 Jenkins Global Tool Installation을 연동한 듯한(?) 효과를 주기 위함입니다. System Config
에서 Jenkins Global Tool Installation
에 정의한 이름으로 매칭하여 Extensible Choice Parameter
를 위와 같이 구성했습니다.
그리고 Extensible Choice
로 설정한 Choice Parameter 를 정의하였습니다.
Jenkins Global Tool Installation
관련pipeline script
에서 사용 가능한 Choice Parameter Plugin이 있다면 공유 부탁드립니다..ㅜㅜ(JDK Parameter Plugin을 시도했으나 기대와 다르게 동작하여 적용하지 못했습니다.)
Git
String Parameter
로 아래 정보를 구성합니다.
${BRANCH_TO_BUILD}
환경 변수는 Hook으로 인해 발생한 Branch를 읽는 환경 변수입니다. Template에 유일하게 이 부분만 디폴트 Value를 지정해줍니다. (캡처 본에서 Default Value가 채워진 곳이 몇 군데 있지만, 실제 계속 재사용되는 것은, Branch 뿐입니다.)
Template 사용하기
Git 계정 연동 및 Hook 설정은 생략합니다
Job 생성
new Job에서 제일 아래 Copy from을 통해 Template Job을 복사합니다.
기본 값 채워넣기
대부분의 설정은 기본 설정을 유지하고, 파라미터의 Default Value
만 각 프로젝트에 맞게 설정하여줍니다.
수동 실행
수동으로 Job을 빌드하려면 Build with Parameter
를 사용합니다.
그러면 아래와 같이 기본 값들이 채워진 상태로 나오며 실행을 하면 끝!
다시 실행
이미 진행된 Job에 대해서 다시 실행하려면 Rebuild Plugin 을 사용합니다.
이 플러그인은 이미 종료된 실행 잡을 다시 빌드할 수 있도록 도와주는 플러그인입니다. Parameterized
를 지원하여, 해당 실행 잡이 가지고 있던 파라미터를 그대로 사용할 수 있으며, 수정도 가능합니다.
사용 방법은 매우 간단합니다. 플러그인을 설치하면, 아래와 같이 파이프 라인 뷰의 실행 잡에 Rebuild 탭이 보이게 됩니다.
클릭하게 되면 해당 잡 실행기록이 가지고 있던 파라이터가 그대로 기본 설정이 되어 동일한 조건으로 잡의 실행이 가능합니다.
Script 기능 확인!
Pipeline Job
이 구성된 후 Job이 실행되면 Stage View
에 아래와 같은 화면을 보여줍니다! 기존 Build Pipeline
에서는 View
를 따로 만들어야해서 불편했었습니다.
Flow
수동 실행 시 단계를 Test 단계를 스킵하면, 설정한 스크립트대로 아래와 같이!
수동 파이프라인
위와 같이 수동 단계에서 대기하며, 버튼이 제공됩니다!
TIP
IntelliJ에서 GDSL 사용하기
https://gist.github.com/arehmandev/736daba40a3e1ef1fbe939c6674d7da8
gdsl 다운
Snippet Generator
Pipeline gdsl 로 코드를 작성하기 어렵다면, 제공해주는 Snippet Generator를 사용합니다.
Build Pipeline
사용성으로 groovy script 를 생성해주며, 다양한 snippet 이 있습니다! 저 같은 경우는 Docs
와 Stack Overflow
을 찾아보면서 했을 때 생기던 오류들이, 정말 쉽게 해결..!
마무리
Jenkins Pipeline
은 강력했습니다. Build Pipeline
으로 만들었던 모든 기능을 그대로 옮길 수 있을 뿐더라, 제약으로 막혀있던 기능을 스크립트 기반이기에 해결할 수 있었습니다. 고로 Jenkins Pipeline
은 뭐든지 가능하다!
그러나 단점도 존재하는 것 같습니다.
일단 groovy를 사용해야 한다는 점! snippet 생성기가 어느 정도 이 부분을 해소해주지만, groovy에 대한 거부감, 혹은 언어를 배우고 싶지 않다면 Build Pipeline
으로 구성하는 것이 쉽습니다. 스크립트 작성없이 클릭만(?)으로 만들 수 있기 때문입니다.
또 한가지 단점은 아직도 지원하지 않는 Plugin
이 많다는 점 입니다. 인기있는 플러그인 중에서도 Jenkins Pipeline
을 지원하지 않거나, 안정적이지 않은 플러그인들을 꽤 보았습니다. 이 부분은 분명히 시간이 해결해주리라! 믿습니다.
저는 개인적으로 Jenkins Pipeline
이 굉장히 마음에 듭니다. maven
보다 gradle
을 선호하듯, 개발자 친화적
+ CoC
가 저는 좋습니다.