/엔지니어/Git / CI·CD/Jenkins 파이프라인 기초 — Declarati
Git / CI·CD중급linuxjenkinscicdpipeline

Jenkins 파이프라인 기초 — Declarative Pipeline으로 CI/CD 구축

Jenkinsfile를 사용한 Declarative Pipeline 문법을 처음부터 설명합니다. agent·stages·steps 구조, 환경 변수, 병렬 빌드, post 블록, 자격 증명 관리, 공유 라이브러리까지 실전 CI/CD 파이프라인을 코드로 정의하는 방법을 다룹니다.

Jenkins Pipeline 이란?

Jenkins Pipeline은 빌드·테스트·배포 과정을 코드(Jenkinsfile) 로 정의하는 기능입니다. 웹 UI에서 클릭으로 잡(Job)을 구성하던 자유형(Freestyle) 방식과 달리, 파이프라인을 코드로 작성해 Git 저장소에 함께 보관하므로 버전 관리·코드 리뷰·재현이 가능합니다. 이를 "Pipeline as Code"라고 부릅니다.

파이프라인 작성 방식은 두 가지입니다.

방식특징
Declarativepipeline { } 블록으로 시작하는 구조화된 문법. 가독성이 높고 검증이 쉬워 권장됨
ScriptedGroovy 코드 기반. 자유도가 높지만 복잡하고 진입 장벽이 큼

이 가이드는 현업에서 표준으로 쓰이는 Declarative Pipeline을 다룹니다.


사전 준비

Jenkins가 설치되어 있고, 다음 플러그인이 필요합니다.

  • Pipeline (기본 포함)
  • Git / GitHub Branch Source
  • Credentials Binding (자격 증명 주입)

새 잡 생성 시 "Pipeline" 또는 "Multibranch Pipeline" 타입을 선택하고, 소스로 Pipeline script from SCM을 지정하면 저장소 루트의 Jenkinsfile을 읽습니다.


가장 기본적인 Jenkinsfile

GROOVY
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)이 어디서 실행될지 결정합니다.

GROOVY
// 1) 사용 가능한 아무 노드
agent any

// 2) 특정 라벨이 붙은 노드만
agent { label 'linux && docker' }

// 3) Docker 컨테이너 안에서 실행
agent {
    docker {
        image 'node:20-alpine'
        args  '-v /tmp:/tmp'
    }
}

// 4) 파이프라인 레벨은 none, stage마다 따로 지정
agent none

agent { docker { ... } }를 쓰면 빌드마다 깨끗한 컨테이너에서 실행되므로 "내 머신에선 됐는데" 문제를 줄일 수 있습니다. 단, 에이전트 노드에 Docker가 설치되어 있어야 합니다.


environment — 환경 변수

environment 블록으로 변수를 선언합니다. 파이프라인 전체 또는 특정 stage 범위로 지정할 수 있습니다.

GROOVY
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로 참조합니다.

GROOVY
environment {
    // Secret Text 타입 자격 증명을 환경 변수로 주입
    DOCKER_TOKEN = credentials('docker-registry-token')
}

Username/Password 타입은 withCredentials로 명시적으로 묶는 방식이 안전합니다.

GROOVY
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로 동시에 돌려 시간을 단축합니다.

GROOVY
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 브랜치에서만 돌립니다.

GROOVY
stage('Deploy') {
    when {
        branch 'main'
    }
    steps {
        sh 'make deploy'
    }
}

whenbranch, environment name: 'X', value: 'Y', expression { ... }, changeset 등 다양한 조건을 지원합니다.


post — 빌드 후 처리

빌드 성공/실패와 무관하게 정리 작업이나 알림을 보냅니다.

GROOVY
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

GROOVY
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 배포

GROOVY
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 저장소에 공유 라이브러리를 만들어 재사용합니다.

GROOVY
@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 Generatorjenkins-cli declarative-linter로 사전 검증하면 빌드 한 번을 낭비하지 않습니다.


정리

항목핵심
정의 방식Declarative Pipeline (pipeline { }) 권장
필수 구조agentstagesstagesteps
환경 변수environment { }, 내장 BUILD_NUMBER/GIT_COMMIT
비밀 관리credentials(), withCredentials로 안전 주입
속도 개선parallel로 독립 작업 동시 실행
조건 분기when { branch 'main' }
후처리post { always / success / failure }
재사용Shared Library로 빌드 로직 공유

Declarative Pipeline은 코드로 CI/CD를 정의해 재현성과 협업성을 확보하는 출발점입니다. 위 완성형 예제를 기반으로 자신의 프로젝트에 맞게 stage를 조정해 나가면 됩니다.

#jenkins#cicd#pipeline#jenkinsfile#automation
편집 안내 · Editorial Note

이 가이드는 AI 도구를 활용해 초안을 구성하고 사람이 명령어·문맥을 검토해 발행했습니다. 운영체제와 도구 버전에 따라 결과가 달라질 수 있으므로 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요.

관련 공식 문서Git 공식 문서

질문 & 답변 (Q&A)

이 가이드에 대해 궁금한 점을 질문해보세요. 확인 후 답변드립니다.