Jenkins 파이프라인
Jenkins를 사용한 CI/CD 파이프라인 구축과 관리
Jenkins를 사용한 CI/CD 파이프라인 구축과 관리
목표: 프로덕션급 Jenkins 파이프라인을 구축하고 유지보수할 수 있는 수준
🎯 Jenkins 개요
아키텍처
graph TB
subgraph "Jenkins"
MASTER["🎛️ Jenkins Master"] -->|"스케줄링"| AGENT1["💻 Agent 1"]
MASTER -->|"스케줄링"| AGENT2["💻 Agent 2"]
subgraph "Master"
UI["🌐 Web UI"]
CONFIG["⚙️ 설정"]
QUEUE["📋 Job Queue"]
end
end
GIT["📁 Git"] -->|"Webhook"| MASTER
AGENT1 -->|"빌드"| ARTIFACT["📦 Artifacts"]
🛠️ 설치
Docker로 설치
# Jenkins 실행
docker run -d \
--name jenkins \
-p 8080:8080 \
-p 50000:50000 \
-v jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/jenkins:lts
# 초기 비밀번호
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
Kubernetes로 설치
# Helm 설치
helm repo add jenkins https://charts.jenkins.io
helm install jenkins jenkins/jenkins \
--set controller.serviceType=LoadBalancer
# 비밀번호
kubectl exec --namespace default -it svc/jenkins -c jenkins \
-- /bin/cat /run/secrets/additional/chart-admin-password
📝 Pipeline 작성
Declarative Pipeline
// Jenkinsfile
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'myregistry.com'
IMAGE_NAME = 'myapp'
VERSION = sh(script: 'git describe --tags --always', returnStdout: true).trim()
}
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
disableConcurrentBuilds()
timeout(time: 30, unit: 'MINUTES')
}
triggers {
pollSCM('H/5 * * * *')
// 또는 webhook
}
stages {
stage('Checkout') {
steps {
checkout scm
sh 'git log -1'
}
}
stage('Build') {
steps {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm run test:unit'
}
post {
always {
publishTestResults testResultsPattern: 'coverage/*.xml'
}
}
}
stage('Integration Tests') {
steps {
sh 'npm run test:integration'
}
}
}
}
stage('Code Quality') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'sonar-scanner'
}
}
}
stage('Security Scan') {
steps {
sh 'trivy fs --exit-code 0 --no-progress .'
}
}
stage('Build Docker Image') {
steps {
script {
docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${VERSION}")
}
}
}
stage('Push Docker Image') {
when {
branch 'main'
}
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-registry-credentials') {
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${VERSION}").push()
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${VERSION}").push('latest')
}
}
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
sh '''
kubectl config use-context staging
kubectl set image deployment/myapp myapp=${DOCKER_REGISTRY}/${IMAGE_NAME}:${VERSION}
kubectl rollout status deployment/myapp
'''
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input message: 'Deploy to Production?', ok: 'Deploy'
sh '''
kubectl config use-context production
kubectl set image deployment/myapp myapp=${DOCKER_REGISTRY}/${IMAGE_NAME}:${VERSION}
kubectl rollout status deployment/myapp
'''
}
}
}
post {
always {
cleanWs()
}
success {
slackSend(color: 'good', message: "Build succeeded: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
failure {
slackSend(color: 'danger', message: "Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
}
}
Scripted Pipeline
// 더 유연한 Scripted Pipeline
node('docker-agent') {
try {
stage('Checkout') {
checkout scm
}
stage('Build') {
docker.image('node:18').inside {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Test') {
docker.image('node:18').inside {
sh 'npm test'
}
}
if (env.BRANCH_NAME == 'main') {
stage('Deploy') {
sh 'kubectl apply -f k8s/'
}
}
} catch (e) {
currentBuild.result = 'FAILURE'
throw e
} finally {
cleanWs()
}
}
🔧 고급 기능
Shared Library
// vars/buildDocker.groovy
def call(Map config) {
def image = docker.build("${config.registry}/${config.image}:${config.tag}")
if (config.push) {
docker.withRegistry("https://${config.registry}", config.credentials) {
image.push()
}
}
return image
}
// Jenkinsfile
@Library('my-shared-library') _
pipeline {
agent any
stages {
stage('Build') {
steps {
buildDocker(
registry: 'myregistry.com',
image: 'myapp',
tag: 'v1.0.0',
push: true,
credentials: 'docker-registry-credentials'
)
}
}
}
}
멀티브랜치 파이프라인
// Jenkinsfile (각 브랜치에)
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make build'
}
}
}
when {
anyOf {
branch 'main'
branch 'develop'
branch pattern: 'release/.*', comparator: 'REGEXP'
}
}
}
📋 체크리스트
- Jenkins 설치
- Pipeline 작성
- Declarative vs Scripted
- 환경변수 설정
- 병렬 실행
- 조건적 실행
- Docker 빌드/푸시
- Kubernetes 배포
- Shared Library
- 알림 설정
- 보안 설정
🔗 관련 노트
- 01-01-CI-CD-개념 — CI/CD 개념
- 02-02-GitHub-Actions — GitHub Actions
- 01-03-도커-기본 — Docker
마지막 업데이트: 2026-06-01