Jenkins란 무엇인가?
Jenkins는 Java 기반의 오픈소스 지속적 통합(CI) 도구입니다. 반복적인 작업을 자동화하여 소프트웨어 개발 프로젝트의 CI를 지원하는 데 사용됩니다. CI는 개발자가 코드를 제출하면 즉시 빌드, 컴파일, 단위 테스트 등을 수행하여 효율성을 높이고 오류를 줄이는 개발 방식입니다. 지속적 배포(CD)는 CI의 확장으로, 통합된 코드를 실제 운영 환경에 배포하는 과정을 자동화합니다.
Jenkins Docker 설치
Docker를 사용하여 Jenkins를 설치할 수 있습니다. 먼저 Docker를 설치하고, Jenkins 데이터를 저장할 디렉토리에 대한 권한을 설정해야 합니다.
# 사용자 및 그룹 생성 (UID/GID 1000으로 Jenkins 이미지와 일치)
groupadd jenkins -g 1000
useradd jenkins -u 1000 -g jenkins
chown -R 1000:1000 /jenkins
# Docker 네트워크 생성
docker network create jenkins
# Jenkins 컨테이너 실행
docker run \
--name jenkins-docker \
--detach \
--restart always \
--add-host="gitlab.example.com:192.168.10.10" \
--privileged \
--network jenkins \
--network-alias docker \
--env DOCKER_TLS_CERTDIR=/certs \
--volume /jenkins/certs:/certs/client \
--volume /jenkins/data:/var/jenkins_home \
--publish 8080:8080 \
--publish 50000:50000 \
jenkins/jenkins:latest
# 초기 관리자 비밀번호 확인
docker exec -it jenkins-docker cat /var/jenkins_home/secrets/initialAdminPassword
이제 서버IP:8080으로 Jenkins에 접속할 수 있습니다.
Jenkins 파이프라인 (Pipeline)
Jenkins를 효과적으로 사용하려면 파이프라인 사용법을 익히는 것이 중요합니다. 파이프라인은 코드 통합, 빌드, 품질 검사, 배포 등 일련의 작업을 스크립트로 정의하는 기능입니다. 기본 파일명은 Jenkinsfile이며, Groovy 스크립트 언어를 사용합니다.
파이프라인 문법: 스크립트형 vs 선언형
- 스크립트형 (Scripted Pipeline): Groovy 기반의 DSL을 사용하며, 전통적인 스크립트처럼 흐름 제어가 가능합니다.
node블록으로 작업 실행 환경을 지정하고,stage내에서try-catch블록을 사용하여 오류를 처리할 수 있습니다.
node {
stage('Example') {
try {
sh 'exit 1'
} catch (exc) {
echo 'Something failed, I should sound the klaxons!'
throw
}
}
}
- 선언형 (Declarative Pipeline): 더 높은 유연성과 가독성을 제공하며, Jenkinsfile 작성을 권장합니다. 파이프라인 전체 구조를 명확하게 정의할 수 있으며, 스크립트형 구문을 포함할 수도 있습니다.
pipeline {
agent any
stages {
stage('Example') {
steps {
echo 'Hello World'
script {
def browsers = ['chrome', 'firefox']
for (int i = 0; i < browsers.size(); ++i) {
echo "Testing the ${browsers[i]} browser"
}
}
}
}
}
}
선언형 파이프라인 주요 구성 요소
- pipeline: 파이프라인 전체를 감싸는 최상위 블록입니다.
- agent: 파이프라인 또는 특정 stage를 실행할 환경을 지정합니다.
any: 사용 가능한 모든 에이전트에서 실행합니다.none: 특정 에이전트를 지정하지 않으며, 각 stage에서 개별적으로 지정해야 합니다.label: 지정된 레이블을 가진 에이전트에서 실행합니다.node: 레이블, 사용자 정의 작업 공간 등 상세 설정을 포함하여 실행할 노드를 지정합니다.
pipeline { agent { node { label "container" customWorkspace '/path/to/workspace' } } } - stages: 하나 이상의
stage를 포함하는 시퀀스입니다. 빌드, 테스트, 배포 등 논리적 단계를 구분합니다. - stage: 파이프라인의 개별 단계를 나타내며, 고유한 이름이 필요합니다.
- steps: stage 내에서 실행될 실제 작업들을 정의합니다. 쉘 명령 등을 포함할 수 있습니다.
pipeline { agent any stages { stage('Build') { steps { echo 'Building the project...' sh 'make build' } } } } - post: 파이프라인 또는 stage 완료 후 실행될 작업을 정의합니다.
always: 항상 실행됩니다.success: 성공 시 실행됩니다.failure: 실패 시 실행됩니다.changed: 이전 빌드와 상태가 다를 경우 실행됩니다.unstable: 불안정 상태일 경우 실행됩니다.aborted: 중단 시 실행됩니다.cleanup: 위의 상태 외의 경우 실행됩니다.
pipeline { agent any stages { stage('Test') { steps { echo 'Running tests...' sh 'make test' } } } post { success { echo 'Tests passed!' } failure { echo 'Tests failed!' } } } - environment: 파이프라인 전역 또는 특정 stage에 환경 변수를 설정합니다.
credentials()함수를 사용하여 Jenkins에 저장된 비밀 정보를 안전하게 로드할 수 있습니다. (Secret Text, Secret File, Username/Password, SSH Private Key 등)pipeline { agent any environment { API_KEY = credentials('my-api-key-secret') } stages { stage('Deploy') { steps { echo "Deploying with API Key: ${env.API_KEY}" } } } } - options: 파이프라인 실행에 대한 특정 옵션을 설정합니다.
buildDiscarder: 보관할 최대 빌드 수를 지정합니다.disableConcurrentBuilds: 동시 빌드를 비활성화합니다.retry: 실패 시 지정된 횟수만큼 재시도합니다.timeout: 파이프라인 또는 stage의 최대 실행 시간을 설정합니다.timestamps: 모든 콘솔 출력에 타임스탬프를 추가합니다.
pipeline { agent any options { timestamps() timeout(time: 1, unit: 'HOURS') buildDiscarder(logRotator(numToKeepStr: '10')) } stages { stage('Build') { steps { echo 'Building...' } } } } - parameters: 파이프라인 실행 전에 사용자로부터 입력을 받을 수 있는 파라미터를 정의합니다. (String, Text, Boolean, Choice, Password 등)
pipeline { agent any parameters { string(name: 'TARGET_ENV', defaultValue: 'staging', description: 'Deployment environment') booleanParam(name: 'FORCE_DEPLOY', defaultValue: false, description: 'Force deployment even if not needed') } stages { stage('Deploy') { steps { echo "Deploying to ${params.TARGET_ENV}" script { if (params.FORCE_DEPLOY) { echo "Forcing deployment." } } } } } } - triggers: 파이프라인 자동 실행 규칙을 정의합니다.
cron: 지정된 cron 스케줄에 따라 실행합니다.pollSCM: 소스 코드 변경 감지 시 스케줄에 따라 실행합니다.upstream: 다른 Jenkins 작업 완료 시 실행합니다.
H(Hash)를 사용하여 부하를 분산시킬 수 있습니다.pipeline { agent any triggers { cron('H 0 * * 1-5') // 월-금 매일 0시경 실행 } stages { stage('Scheduled Task') { steps { echo 'Running scheduled task...' } } } } - tools: Maven, JDK, Gradle과 같은 도구를 Jenkins 전역 설정에 정의된 이름으로 사용합니다.
pipeline { agent any tools { maven 'Maven 3.6.3' // Jenkins Global Tool Configuration에 정의된 Maven 이름 } stages { stage('Build') { steps { sh 'mvn --version' } } } } - input: 파이프라인 실행 중 사용자 입력을 대기합니다. 승인 시 다음 단계로 진행됩니다.
pipeline { agent any stages { stage('Approve Deployment') { input { message "Deploy to production?" ok "Yes, deploy." submitter "user1,user2" // 승인 가능 사용자 } steps { echo "Deployment approved." } } } } - when: 특정 조건이 충족될 때 stage를 실행할지 결정합니다. (branch, environment, expression, allOf, anyOf, not 등)
pipeline { agent any stages { stage('Deploy to Production') { when { branch 'main' } steps { echo "Deploying to production from main branch." } } } } - Parallel: 여러 stage를 병렬로 실행합니다. stage 내에
parallel블록을 사용하여 정의합니다.failFast옵션을 사용하면 병렬 stage 중 하나라도 실패하면 나머지 stage를 중단시킵니다.pipeline { agent any stages { stage('Build') { steps { echo 'Building...' } } stage('Parallel Tests') { parallel { stage('Test on Linux') { agent { label 'linux-agent' } steps { echo 'Running tests on Linux...' } } stage('Test on Windows') { agent { label 'windows-agent' } steps { echo 'Running tests on Windows...' } } } } stage('Deploy') { steps { echo 'Deploying...' } } } } - script: 선언형 파이프라인 내에서 스크립트형 코드를 실행할 수 있게 합니다. 복잡한 로직 처리에 유용합니다.
pipeline { agent any stages { stage('Complex Logic') { steps { script { def data = [1, 2, 3, 4, 5] def processedData = data.findAll { it % 2 == 0 } echo "Even numbers: ${processedData}" } } } } }스크립트형 파이프라인
스크립트형 파이프라인은 Groovy의 모든 기능을 활용할 수 있는 완전한 DSL입니다.
node블록을 사용하여 실행 환경을 정의하고, Groovy의 제어문(if/else, try/catch)을 사용하여 파이프라인 흐름을 제어합니다.node { def buildResult = 'SUCCESS' stage('Build') { try { echo 'Building...' sh './build.sh' } catch (err) { echo "Build failed: ${err}" buildResult = 'FAILURE' } } stage('Test') { when { expression { return buildResult == 'SUCCESS' } } steps { echo 'Running tests...' sh './test.sh' } } }