Jenkins Pipeline 이란?
Jenkins Pipeline은 빌드·테스트·배포 과정을 코드(Jenkinsfile) 로 정의하는 기능입니다. 웹 UI에서 클릭으로 잡(Job)을 구성하던 자유형(Freestyle) 방식과 달리, 파이프라인을 코드로 작성해 Git 저장소에 함께 보관하므로 버전 관리·코드 리뷰·재현이 가능합니다. 이를 "Pipeline as Code"라고 부릅니다.
파이프라인 작성 방식은 두 가지입니다.
| 방식 | 특징 |
|---|---|
| Declarative | pipeline { } 블록으로 시작하는 구조화된 문법. 가독성이 높고 검증이 쉬워 권장됨 |
| Scripted | Groovy 코드 기반. 자유도가 높지만 복잡하고 진입 장벽이 큼 |
이 가이드는 현업에서 표준으로 쓰이는 Declarative Pipeline을 다룹니다.
사전 준비
Jenkins가 설치되어 있고, 다음 플러그인이 필요합니다.
- Pipeline (기본 포함)
- Git / GitHub Branch Source
- Credentials Binding (자격 증명 주입)
새 잡 생성 시 "Pipeline" 또는 "Multibranch Pipeline" 타입을 선택하고, 소스로 Pipeline script from SCM을 지정하면 저장소 루트의 Jenkinsfile을 읽습니다.
가장 기본적인 Jenkinsfile
pipeline {
agent any
stages {
stage('Build') {
steps {
echo '빌드 시작'
sh 'make build'
}
}
stage('Test') {
steps {
sh 'make test'
}
}
stage('Deploy') {
steps {
sh 'make deploy'
}
}
}
}핵심 구조는 다음과 같습니다.
| 블록 | 역할 |
|---|---|
pipeline | 전체 파이프라인을 감싸는 최상위 블록 (필수) |
agent | 어느 노드/환경에서 실행할지 지정 |
stages | 하나 이상의 stage를 담는 컨테이너 |
stage | 논리적 단계 (Build, Test, Deploy 등). UI에 시각화됨 |
steps | 단계 안에서 실제로 실행할 명령 |
agent — 실행 환경 지정
agent는 파이프라인(또는 개별 stage)이 어디서 실행될지 결정합니다.
// 1) 사용 가능한 아무 노드
agent any
// 2) 특정 라벨이 붙은 노드만
agent { label 'linux && docker' }
// 3) Docker 컨테이너 안에서 실행
agent {
docker {
image 'node:20-alpine'
args '-v /tmp:/tmp'
}
}
// 4) 파이프라인 레벨은 none, stage마다 따로 지정
agent noneagent { docker { ... } }를 쓰면 빌드마다 깨끗한 컨테이너에서 실행되므로 "내 머신에선 됐는데" 문제를 줄일 수 있습니다. 단, 에이전트 노드에 Docker가 설치되어 있어야 합니다.
environment — 환경 변수
environment 블록으로 변수를 선언합니다. 파이프라인 전체 또는 특정 stage 범위로 지정할 수 있습니다.
pipeline {
agent any
environment {
APP_NAME = 'my-service'
BUILD_TAG = "v1.${BUILD_NUMBER}" // 내장 변수 사용
REGISTRY = 'registry.example.com'
}
stages {
stage('Info') {
steps {
sh 'echo "빌드: $APP_NAME:$BUILD_TAG"'
}
}
}
}자주 쓰는 내장 환경 변수:
| 변수 | 의미 |
|---|---|
BUILD_NUMBER | 빌드 일련번호 |
BUILD_ID | 빌드 ID |
JOB_NAME | 잡 이름 |
WORKSPACE | 작업 디렉터리 절대 경로 |
GIT_COMMIT | 체크아웃된 커밋 해시 |
BRANCH_NAME | (Multibranch) 브랜치 이름 |
자격 증명(Credentials) 안전하게 사용
비밀번호·토큰을 Jenkinsfile에 평문으로 적으면 안 됩니다. Manage Jenkins → Credentials에 등록한 뒤 ID로 참조합니다.
environment {
// Secret Text 타입 자격 증명을 환경 변수로 주입
DOCKER_TOKEN = credentials('docker-registry-token')
}Username/Password 타입은 withCredentials로 명시적으로 묶는 방식이 안전합니다.
steps {
withCredentials([usernamePassword(
credentialsId: 'docker-login',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS')]) {
sh 'echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin $REGISTRY'
}
}자격 증명은 로그에 출력되더라도 Jenkins가 자동으로 ****로 마스킹합니다. 그래도 echo "$DOCKER_PASS"처럼 직접 출력하는 코드는 절대 작성하지 마세요.
병렬 실행 — parallel
서로 의존성이 없는 작업(예: 여러 OS·버전 매트릭스 테스트)은 parallel로 동시에 돌려 시간을 단축합니다.
stage('Test Matrix') {
parallel {
stage('Unit') {
steps { sh 'make test-unit' }
}
stage('Integration') {
steps { sh 'make test-integration' }
}
stage('Lint') {
steps { sh 'make lint' }
}
}
}조건 분기 — when
특정 조건에서만 stage를 실행합니다. 배포는 보통 main 브랜치에서만 돌립니다.
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'make deploy'
}
}when은 branch, environment name: 'X', value: 'Y', expression { ... }, changeset 등 다양한 조건을 지원합니다.
post — 빌드 후 처리
빌드 성공/실패와 무관하게 정리 작업이나 알림을 보냅니다.
post {
always {
junit 'reports/**/*.xml' // 테스트 리포트 수집
cleanWs() // 작업 공간 정리
}
success {
echo '✅ 빌드 성공'
}
failure {
mail to: '[email protected]',
subject: "실패: ${JOB_NAME} #${BUILD_NUMBER}",
body: "로그 확인: ${BUILD_URL}"
}
}| 조건 | 실행 시점 |
|---|---|
always | 결과와 무관하게 항상 |
success | 성공했을 때만 |
failure | 실패했을 때만 |
unstable | 테스트 실패 등으로 불안정할 때 |
changed | 직전 빌드와 결과가 달라졌을 때 |
options & triggers
options {
timeout(time: 30, unit: 'MINUTES') // 30분 초과 시 중단
retry(2) // 실패 시 2회 재시도
disableConcurrentBuilds() // 동시 빌드 금지
timestamps() // 로그에 타임스탬프
}
triggers {
cron('H 2 * * *') // 매일 새벽 2시경
pollSCM('H/15 * * * *') // 15분마다 변경 폴링
}cron('H 2 * * *')의 H는 해시(hash)로, 잡마다 분산된 시각을 자동 배정해 동시 부하를 막습니다. 일반 cron의 고정 분(0) 대신 권장됩니다.
완성형 예제 — Node 앱 빌드 & Docker 배포
pipeline {
agent { label 'docker' }
environment {
REGISTRY = 'registry.example.com'
IMAGE = "${REGISTRY}/web-app"
TAG = "${GIT_COMMIT.take(8)}"
}
options {
timeout(time: 20, unit: 'MINUTES')
disableConcurrentBuilds()
}
stages {
stage('Checkout') {
steps { checkout scm }
}
stage('Install & Test') {
agent { docker { image 'node:20-alpine'; reuseNode true } }
steps {
sh 'npm ci'
sh 'npm test'
}
}
stage('Build Image') {
steps {
sh "docker build -t ${IMAGE}:${TAG} ."
}
}
stage('Push') {
when { branch 'main' }
steps {
withCredentials([usernamePassword(
credentialsId: 'docker-login',
usernameVariable: 'U', passwordVariable: 'P')]) {
sh 'echo "$P" | docker login -u "$U" --password-stdin $REGISTRY'
sh "docker push ${IMAGE}:${TAG}"
}
}
}
}
post {
always { cleanWs() }
failure { echo '빌드 실패 — 로그 확인 필요' }
}
}공유 라이브러리(Shared Library)
여러 프로젝트가 같은 빌드 로직을 쓴다면 별도 Git 저장소에 공유 라이브러리를 만들어 재사용합니다.
@Library('my-shared-lib') _
pipeline {
agent any
stages {
stage('Build') {
steps {
buildApp(lang: 'node') // vars/buildApp.groovy 의 호출
}
}
}
}라이브러리 저장소 구조는 vars/ (전역 함수), src/ (Groovy 클래스), resources/ (정적 파일)로 구성됩니다. Manage Jenkins → System → Global Pipeline Libraries에 등록합니다.
트러블슈팅
| 증상 | 원인 / 해결 |
|---|---|
sh 단계에서 권한 오류 | 에이전트 사용자에게 실행 권한 부여, Docker 그룹 추가 |
| 파이프라인이 멈춤 | input 승인 대기 중일 수 있음. timeout으로 감싸기 |
| 자격 증명이 빈 값 | credentialsId 오타 또는 폴더 스코프 불일치 확인 |
| Docker agent에서 워크스페이스 분실 | reuseNode true 옵션 추가 |
문법 오류는 Pipeline Syntax → Declarative Directive Generator나 jenkins-cli declarative-linter로 사전 검증하면 빌드 한 번을 낭비하지 않습니다.
정리
| 항목 | 핵심 |
|---|---|
| 정의 방식 | Declarative Pipeline (pipeline { }) 권장 |
| 필수 구조 | agent → stages → stage → steps |
| 환경 변수 | environment { }, 내장 BUILD_NUMBER/GIT_COMMIT |
| 비밀 관리 | credentials(), withCredentials로 안전 주입 |
| 속도 개선 | parallel로 독립 작업 동시 실행 |
| 조건 분기 | when { branch 'main' } |
| 후처리 | post { always / success / failure } |
| 재사용 | Shared Library로 빌드 로직 공유 |
Declarative Pipeline은 코드로 CI/CD를 정의해 재현성과 협업성을 확보하는 출발점입니다. 위 완성형 예제를 기반으로 자신의 프로젝트에 맞게 stage를 조정해 나가면 됩니다.
이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.
질문 & 답변 (Q&A)
이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.