CI/CD를 위한 첫 Jenkins 선언적 파이프라인 구축
Jenkins는 오픈 소스 자동화 서버의 사실상의 표준으로, 수천 개 조직의 지속적 통합 및 지속적 전달(CI/CD) 파이프라인의 중추 역할을 합니다. Jenkins는 스크립트 기반(Scripted)과 선언적(Declarative)의 두 가지 파이프라인 구문 유형을 제공하지만, 선언적 파이프라인(Declarative Pipeline) 구문은 더 단순하고 구조화되어 있으며 가독성이 높은 Groovy DSL 덕분에 대부분의 사용자에게 권장되는 접근 방식입니다.
본 가이드는 첫 번째로 작동하는 Jenkins 선언적 파이프라인을 구축하는 방법에 대한 포괄적인 단계별 튜토리얼을 제공합니다. 소스 코드 관리(SCM) 통합, 빌드 환경 정의, 테스트 실행, 샘플 배포 단계 구현 등을 다루어 소프트웨어 제공 수명 주기 자동화를 위한 강력한 기반을 제공할 것입니다.
선언적 파이프라인 구조 이해하기
선언적 파이프라인은 전체 워크플로를 애플리케이션 코드와 함께 SCM 리포지토리(예: Git)에 저장되는 Jenkinsfile이라는 파일에 정의합니다. 이 방식을 파이프라인 코드화(Pipeline as Code)라고 합니다.
선언적 파이프라인의 핵심 요소
A 선언적 파이프라인은 다음 최상위 블록을 포함해야 합니다.
pipeline: 파이프라인 콘텐츠를 정의하는 필수적인 가장 바깥쪽 블록입니다.agent: 파이프라인 또는 특정 단계가 실행될 위치를 지정합니다(예:any,none, 또는 특정 레이블).stages: 하나 이상의 순차적인stage블록을 포함합니다.stage: 작업의 개념적 블록(예: 빌드, 테스트, 배포)을 정의합니다.steps:stage내에서 실행되는 하나 이상의 명령이나 함수를 포함합니다.
pipeline {
agent any
environment { // 선택 사항: 환경 변수 정의
APP_VERSION = '1.0.0'
}
stages {
stage('Build') {
steps {
// 명령어가 여기에 들어갑니다
}
}
}
post { // 선택 사항: 파이프라인 완료 후 실행되는 작업
always {
echo '파이프라인 완료.'
}
}
}
1단계: Jenkins 작업 설정하기
코드를 통해 파이프라인을 실행하려면 리포지토리에서 Jenkinsfile을 읽도록 Jenkins 작업을 구성해야 합니다.
- Jenkins 대시보드로 이동하여 새 항목(New Item)을 선택합니다.
- 파이프라인의 이름을 입력합니다(예:
my-first-ci-pipeline). - 파이프라인(Pipeline) 항목 유형을 선택하고 확인(OK)을 클릭합니다.
- 구성 페이지에서 파이프라인(Pipeline) 섹션으로 스크롤합니다.
- 정의를 파이프라인 스크립트(Pipeline script)에서 SCM에서 파이프라인 스크립트(Pipeline script from SCM)로 변경합니다.
- SCM(예: Git)을 선택합니다.
- 필요한 경우 리포지토리 URL을 입력하고 자격 증명을 구성합니다.
- 스크립트 경로(Script Path)가
Jenkinsfile(기본값)로 설정되어 있는지 확인합니다.
2단계: CI/CD를 위한 Jenkinsfile 정의하기
성공적인 배포를 위한 표준 단계를 통합하여 간단한 CI/CD 워크플로를 시뮬레이션하는 포괄적인 Jenkinsfile을 생성하겠습니다.
루트에 Jenkinsfile이 있고 애플리케이션 소스 코드(예: 간단한 Python 또는 Java 프로젝트)를 포함하는 리포지토리 구조가 있다고 가정합니다.
전체 선언적 파이프라인 예제
이 파이프라인은 node 에이전트(구성된 경우)를 사용하고 기본적인 셸 단계(sh)를 활용하여 실제 작업을 시뮬레이션합니다. 배포 단계는 이전 단계가 성공적으로 완료된 경우에만 실행되도록 조건이 지정됩니다.
// Jenkinsfile
pipeline {
// 1. 에이전트 정의: 전체 파이프라인이 실행될 위치 지정
agent {
label 'my-build-agent' // 사용 가능한 특정 레이블을 사용하거나 'any' 사용
}
// 2. 환경 변수: 파이프라인 전체에서 사용할 수 있는 변수 정의
environment {
CONTAINER_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'myapp'
}
stages {
// 단계 1: 체크아웃 (소스 코드 관리)
stage('Checkout Source') {
steps {
// 'checkout scm' 단계는 Jenkins 작업 설정에 정의된 구성을 기반으로 코드를 자동으로 체크아웃합니다.
checkout scm
sh 'echo "소스 코드 체크아웃 성공."
}
}
// 단계 2: 아티팩트 빌드
stage('Build Artifact') {
steps {
sh 'echo "$IMAGE_NAME:$BUILD_ID 에 대한 빌드 시작 중"'
// 시뮬레이션: Docker 이미지 빌드 또는 jar/war 파일 컴파일
sh "docker build -t ${IMAGE_NAME}:${BUILD_ID} ."
}
}
// 단계 3: 테스트 및 품질 게이트
stage('Run Tests') {
steps {
sh './run_unit_tests.sh'
sh 'echo "통합 테스트 실행 중..."'
// 테스트 결과 수집 예시 (적절한 플러그인 필요)
// junit '**/target/surefire-reports/*.xml'
}
}
// 단계 4: 배포 (조건부)
stage('Deploy to Staging') {
// 'when' 지시문은 특정 조건에서만 단계가 실행되도록 보장합니다.
when {
branch 'main'
}
steps {
sh "docker push ${CONTAINER_REGISTRY}/${IMAGE_NAME}:${BUILD_ID}"
sh 'kubectl apply -f k8s/deployment-staging.yaml'
script {
// 스크립트 블록을 사용하면 선언적 단계 내에서 전통적인 Groovy 논리를 사용할 수 있습니다.
echo "Staging 환경에 배포 성공."
}
}
}
}
// 3. 후속 작업: 최종 파이프라인 상태를 기반으로 작업 정의
post {
success {
echo '파이프라인 성공적으로 완료됨. Slack으로 팀에 알리는 중...'
// slackSend channel: '#devops-alerts', message: 'CI/CD 성공!'
}
failure {
echo '파이프라인 실패. 오류는 로그를 확인하세요.'
}
cleanup {
sh 'docker rmi -f $(docker images -aq --filter label=build_id=$BUILD_ID)'
}
}
}
3단계: 파이프라인 실행 및 모니터링
Jenkinsfile이 커밋되어 Jenkins 작업에 구성된 브랜치로 푸시되면:
- Jenkins 작업 페이지에서 지금 빌드(Build Now)를 클릭합니다(또는 SCM 폴링/웹훅 트리거를 기다립니다).
- 빌드 내역(Build History) 패널에서 빌드를 모니터링합니다.
- 실행 중인 빌드 번호를 클릭하고 콘솔 출력(Console Output)을 선택하여 실행 세부 정보를 단계별로 확인합니다.
- Stage View 시각화(플러그인이 설치된 경우)를 사용하여
Checkout,Build,Test,Deploy단계의 진행 상황을 그래픽으로 볼 수도 있습니다.
모범 사례 및 고급 기능
라이브러리 및 공유 코드 활용
복잡하거나 반복적인 단계의 경우 Jenkinsfile에 장황한 Groovy 코드를 직접 작성하는 것을 피하십시오. 대신 공유 라이브러리(Shared Libraries)를 사용하십시오. 이러한 외부 라이브러리를 사용하면 여러 파이프라인에서 호출할 수 있는 일반적인 함수(표준 배포 루틴 또는 알림 함수 등)를 정의할 수 있어 Jenkinsfile을 더 깔끔하게 유지할 수 있습니다.
// 공유 라이브러리 단계 호출
stage('Custom Setup') {
steps {
customLibrary.initializeEnvironment(env: 'prod')
}
}
자격 증명 작업
민감한 정보(암호, 토큰, API 키)를 Jenkinsfile에 직접 하드코딩하지 마십시오. Jenkins의 내장된 자격 증명 관리 시스템을 사용하십시오.
withCredentials 단계는 저장된 비밀에 안전하게 액세스할 수 있도록 합니다.
stage('Authenticate Registry') {
steps {
withCredentials([usernamePassword(credentialsId: 'docker-registry-creds',
passwordVariable: 'PASS',
usernameVariable: 'USER')]) {
sh "docker login -u ${USER} -p ${PASS} ${CONTAINER_REGISTRY}"
}
}
}
경고: 에이전트 선택
팁: 보안 위험이 있고 컨트롤러 성능에 영향을 미칠 수 있으므로
agent any대신 항상label을 사용하여agent를 구체적으로 정의하십시오.agent any를 사용하면 파이프라인이 Jenkins 컨트롤러 노드에서 실행될 수 있습니다. Docker, Maven, Node.js 등 필요한 도구가 설치된 Jenkins 에이전트(노드)가 적절하게 구성되었는지 확인하십시오.
결론
Jenkins 선언적 파이프라인은 지속적 통합 및 지속적 전달을 구현하기 위한 강력하고 읽기 쉬우며 유지 관리가 용이한 프레임워크를 제공합니다. 에이전트, 환경 변수, 순차적 단계 및 빌드 후 작업을 포함한 워크플로 구조를 정의함으로써 소프트웨어 자동화 프로세스에 대한 완전한 가시성과 제어 권한을 얻게 됩니다. 이 기반이 되는 Jenkinsfile이 설정되었으므로 이제 자동화된 보안 스캐닝, 아티팩트 게시, 다중 환경 프로모션과 같은 더 복잡한 단계를 통합할 준비가 되었습니다.