notes
Jenkins - Pipeline 자동배포(CICD)
Jenkins Pipeline으로 CI/CD 자동배포를 구성하는 단계·스크립트 메모.
- free style 이랑 달리 pipeline 은 스크립트에 트리거를 직접 써야 webhook 을 받는다
- webhook 빌드인지 수동 빌드인지 보고 브랜치를 정한다
- 스테이지는 체크아웃, 의존성 설치, 빌드 세 개다
플러그인 준비
free style 작업은 설정 화면에서 webhook 트리거를 체크하면 끝인데 pipeline 은 플러그인이 따로 있어야 한다. GitHub plugin 은 기본으로 깔려 있고, GitHub Integration Plugin 은 저장소 연결이랑 트리거 쪽을 맡는다.
webhook 트리거
플러그인을 깔고 나서 스크립트에 triggers 블록을 넣어야 Jenkins 가 push 이벤트를 알아듣는다. 저장소 쪽 webhook Payload URL 은 Jenkins 주소 뒤에 /github-webhook/ 을 붙인 거다.
// 이걸 넣어야 webhook 을 인식한다
triggers {
githubPush() // push 이벤트
}
브랜치 고르기
스크립트는 SCM 에서 읽지 않고 작업 설정의 Pipeline script 칸에 바로 넣었다. 제일 신경 쓴 부분은 빌드가 왜 시작됐는지 보는 거다. 빌드 원인에 GitHubPushCause 가 있으면 webhook 이고, 아니면 내가 직접 누른 거다.
webhook 이면 현재 커밋이 어느 브랜치에 있는지 git ls-remote 로 찾고, prod/smartq 나 prod/cmng 가 아니면 바로 error 로 끊는다. 수동 빌드는 SELECT_PROJECT 값으로 브랜치를 정한다.
스테이지 구성
브랜치를 체크아웃하고 나면 node_modules 가 없을 때만 pnpm install 을 돌리고, 브랜치에 맞는 빌드 명령을 실행한다. 끝나면 성공이든 실패든 cleanWs 로 작업 공간을 비운다.
// Jenkins 파이프라인 정의
// PR merge 이벤트와 수동 빌드를 모두 지원하는 빌드 파이프라인
pipeline {
// 모든 Jenkins 에이전트에서 실행 가능
agent any
// 수동 빌드 시 사용할 파라미터 정의
parameters {
choice(
name: 'SELECT_PROJECT', // 파라미터 이름
choices: ['smartq', 'cmng'], // 선택 가능한 프로젝트 목록
description: '빌드할 프로젝트 선택' // 파라미터 설명
)
}
// GitHub webhook 트리거 설정
// PR merge 시 자동으로 빌드를 시작하기 위한 설정
triggers {
githubPush()
}
// 파이프라인 스테이지 정의
stages {
// Git 저장소 체크아웃 스테이지
stage("Repository") {
steps {
script {
// 현재 빌드가 시작된 원인 확인
def cause = currentBuild.getBuildCauses()
echo "Build Causes: ${cause}" // 빌드 원인 로깅
// GitHub webhook에 의한 빌드인지 확인
def isWebhook = cause.toString().contains('GitHubPushCause')
echo "Is Webhook Build: ${isWebhook}" // webhook 여부 로깅
if (isWebhook) {
// webhook(PR merge)에 의한 자동 빌드 처리
echo "Webhook 빌드 시작"
// git 명령어를 사용하여 현재 커밋의 브랜치 정보 가져오기
def branch = sh(
script: 'git ls-remote --heads origin | grep $(git rev-parse HEAD) | cut -d "/" -f 3-',
returnStdout: true
).trim()
echo "감지된 브랜치: ${branch}" // 감지된 브랜치 정보 로깅
// prod/smartq 또는 prod/cmng 브랜치인지 확인
if (branch.contains('prod/smartq') || branch.contains('prod/cmng')) {
// 해당 브랜치 체크아웃
git branch: branch,
credentialsId: 'cpt4567', // GitHub 인증 정보
url: 'https://github.com/<org>/<repo>.git'
} else {
// 지원하지 않는 브랜치인 경우 에러 발생
error "지원하지 않는 브랜치: ${branch}"
}
} else {
// 수동 빌드 처리
// 선택된 프로젝트에 따라 브랜치 결정
def branch = params.SELECT_PROJECT == 'smartq' ? 'prod/smartq' : 'prod/cmng'
echo "수동 빌드 - 선택된 브랜치: ${branch}"
// 선택된 브랜치 체크아웃
git branch: branch,
credentialsId: 'cpt4567',
url: 'https://github.com/<org>/<repo>.git'
}
// 체크아웃된 파일 목록 확인
sh 'ls -la'
}
}
}
// 프로젝트 의존성 설치 스테이지
stage('Install Dependencies') {
steps {
script {
// node_modules 디렉토리 존재 여부 확인
def nodeModulesExists = fileExists 'node_modules'
if (!nodeModulesExists) {
echo "node_modules 없음, 의존성 설치 시작"
// pnpm을 사용하여 의존성 설치
sh 'pnpm install'
} else {
echo "node_modules 이미 있음"
}
}
}
}
// 프로젝트 빌드 스테이지
stage('Build Project') {
steps {
script {
// 현재 체크아웃된 브랜치 확인
def currentBranch = sh(
script: 'git rev-parse --abbrev-ref HEAD',
returnStdout: true
).trim()
echo "빌드 대상 브랜치: ${currentBranch}"
// 브랜치에 따라 적절한 빌드 명령 실행
if (currentBranch.contains('prod/smartq')) {
echo "Smart 프로젝트 빌드 시작"
sh 'pnpm build:smart'
} else if (currentBranch.contains('prod/cmng')) {
echo "Cmng 프로젝트 빌드 시작"
sh 'pnpm build:cmng'
} else {
error "지원하지 않는 브랜치: ${currentBranch}"
}
}
}
}
}
// 빌드 완료 후 실행되는 작업들
post {
// 빌드 성공 시 실행
success {
echo '빌드 성공!'
}
// 빌드 실패 시 실행
failure {
echo '빌드 실패!'
}
// 빌드 결과와 관계없이 항상 실행
always {
// 작업 공간 정리 (다음 빌드를 위해)
cleanWs()
}
}
}커밋 브랜치를 ls-remote 로 찾는 방식은 같은 커밋이 두 브랜치에 동시에 있으면 둘 다 걸린다. 프로젝트별로 브랜치를 따로 운영해서 문제는 없었는데, 구조가 바뀌면 작업을 프로젝트마다 나누는 게 더 깔끔하다.