Jenkins 마스터하기: 파이프라인 초보에서 DevOps 영웅까지 🎭

발행: (2025년 12월 3일 오전 10:43 GMT+9)
8 min read
원문: Dev.to

Source: Dev.to

소개

적절한 CI/CD 파이프라인이 없던 시절을 떠올려 보세요 – 개발자들이 마치 디지털 카우보이처럼 코드를 수동으로 배포하고, 변경 사항이 사용자에게 폭발하지 않기를 바라던 어두운 시절이었습니다.

그때 등장한 Jenkins – 천재와 혼돈을 동시에 지닌 새로운 디지털 집사입니다. 원래는 Hudson이라는 이름이었지만(Oracle 변호사들이 파티를 망쳤죠), Jenkins는 2011년부터 개발자들의 삶을 자동화해 왔습니다. 재미있는 사실: Jenkins의 마스코트는 바로 “Jenkins”라는 이름의 집사입니다.

오늘은 여러분을 파이프라인 초보에서 CI/CD 기사로 변신시켜 드릴 겁니다. 자동화된 테스트 갑옷과 배포 초능력을 갖춘 기사 말이죠.

1. Jenkins 101: 새로운 디지털 집사와의 만남 🤖

좋은 점, 나쁜 점, 그리고 파란 공

Jenkins는 색깔이 있는 공에 대한 독특한 집착이 있습니다:

  • 초록은 성공을 의미합니다.
  • 파랑도 성공을 의미합니다(복잡합니다).
  • 빨강은 “이력서를 업데이트할 때다”를 의미합니다.

Jenkins 커뮤니티에서는 파란색과 초록색 표시를 두고 한때 뜨거운 논쟁이 있었습니다.

Jenkins 생존 키트

pipeline {
    agent any

    stages {
        stage('Hello World') {
            steps {
                echo 'Welcome to Jenkins, brave soul!'
                script {
                    def courage = "maximum"
                    echo "Setting courage level to: ${courage}"
                }
            }
        }
    }
}

덜 알려진 사실: Jenkins에는 1,800개가 넘는 플러그인이 있습니다 – 스위스 군용 나이프 공장보다도 다양하죠! Slack과 연동하고 싶나요? 플러그인이 있습니다. 화성에 배포하고 싶나요? 아마도 플러그인이 있을 겁니다(아직 화성은 아니지만…).

설치: 소환 의식

# Docker 방식 (현명한 사람들을 위해)
docker run -p 8080:8080 -p 50000:50000 jenkins/jenkins:lts

# 전통 방식 (용감한 사람들을 위해)
sudo apt update
sudo apt install openjdk-11-jdk
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb https://pkg.jenkins.io/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt update && sudo apt install jenkins

프로 팁: Jenkins는 관리자 비밀번호를 보물보다 깊은 파일에 숨겨 두고 인사합니다. 🗝️

2. 파이프라인 아키텍처: 코드를 프로덕션으로 연결하는 고속도로 🛣️

단계: 조립 라인 작업자들

파이프라인 단계는 각각 고유한 성격을 가진 조립 라인 작업자라고 생각하면 됩니다:

  • Build Bob – 코드를 컴파일하고 누락된 의존성을 불평합니다.
  • Test Terry – 카페인에 취한 QA 엔지니어처럼 열정적으로 테스트를 실행합니다.
  • Deploy Diana – 코드를 프로덕션에 배포합니다(성공률은 다양합니다).

예시 파이프라인

pipeline {
    agent any

    environment {
        DOCKER_IMAGE = "myapp:${env.BUILD_NUMBER}"
        DEPLOY_ENV = "staging"
    }

    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', url: 'https://github.com/yourcompany/awesome-app.git'
            }
        }

        stage('Build & Test') {
            parallel {
                stage('Build') {
                    steps {
                        sh 'mvn clean compile'
                        echo "Build completed! Bob is happy! 😊"
                    }
                }
                stage('Unit Tests') {
                    steps {
                        sh 'mvn test'
                        publishTestResults testResultsPattern: 'target/test-reports/*.xml'
                    }
                    post {
                        always {
                            echo "Terry finished testing (with varying degrees of success)"
                        }
                    }
                }
                stage('Security Scan') {
                    steps {
                        sh 'mvn dependency-check:check'
                        echo "Security scan complete - no known vulnerabilities found! 🔒"
                    }
                }
            }
        }
    }
}

놀라운 사실: 병렬 실행을 활용하면 빌드 시간을 60 % 이상 단축할 수 있습니다 – 마치 여러 조립 라인이 동시에 가동되는 것과 같습니다.

Jenkinsfile: 파이프라인의 DNA

Jenkinsfile은 파이프라인의 유전 코드입니다. 이를 레포지토리에 보관하고 Pipeline‑as‑Code(코드로서의 파이프라인)로 다루세요. 이는 인프라를 코드로 관리하는 Infrastructure‑as‑Code보다 더 멋진 사촌이죠.

3. 제로에서 히어로까지: 실전 파이프라인 구현 🦸‍♂️

완전한 파이프라인: 현대적인 걸작

pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = "your-registry.com"
        APP_NAME = "awesome-app"
        STAGING_SERVER = "staging.awesome-app.com"
        PRODUCTION_SERVER = "awesome-app.com"
    }

    stages {
        stage('Preparation') {
            steps {
                cleanWs()
                git branch: "${BRANCH_NAME}", url: 'https://github.com/yourcompany/awesome-app.git'
                script {
                    env.BUILD_VERSION = "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
                }
            }
        }

        stage('Build & Quality Gates') {
            parallel {
                stage('Application Build') {
                    steps {
                        sh 'mvn clean package -DskipTests'
                        archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
                    }
                }
                stage('Code Quality') {
                    steps {
                        sh 'mvn sonar:sonar'
                        echo "SonarQube analysis complete - code quality checked! ✨"
                    }
                }
                stage('Dependency Audit') {
                    steps {
                        sh 'mvn dependency-check:check'
                        publishHTML([
                            allowMissing: false,
                            alwaysLinkToLastBuild: true,
                            keepAll: true,
                            reportDir: 'target',
                            reportFiles: 'dependency-check-report.html',
                            reportName: 'Dependency Check Report'
                        ])
                    }
                }
            }
        }

        stage('Testing Symphony') {
            parallel {
                stage('Unit Tests') {
                    steps {
                        sh 'mvn test'
                        publishTestResults testResultsPattern: 'target/test-reports/*.xml'
                    }
                }
                stage('Integration Tests') {
                    steps {
                        sh 'mvn integration-test'
                        echo "Integration tests passed - components are playing nice! 🤝"
                    }
                }
                stage('API Tests') {
                    steps {
                        sh 'npm install && npm run api-tests'
                        publishTestResults testResultsPattern: 'api-test-results.xml'
                    }
                }
            }
        }

        stage('Containerization') {
            steps {
                script {
                    def image = docker.build("${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.BUILD_VERSION}")
                    docker.withRegistry("https://${env.DOCKER_REGISTRY}", 'docker-registry-credentials') {
                        image.push()
                        image.push('latest')
                    }
                    echo "Docker image ${image.id} pushed to ${env.DOCKER_REGISTRY}"
                }
            }
        }

        stage('Deploy to Staging') {
            steps {
                sh """
                ssh user@${STAGING_SERVER} '
                    docker pull ${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.BUILD_VERSION}
                    docker run -d --rm -p 8080:8080 ${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.BUILD_VERSION}
                '
                """
                echo "Deployed ${APP_NAME} to staging."
            }
        }

        stage('Approval') {
            steps {
                input message: 'Promote to production?', ok: 'Deploy'
            }
        }

        stage('Deploy to Production') {
            steps {
                sh """
                ssh user@${PRODUCTION_SERVER} '
                    docker pull ${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.BUILD_VERSION}
                    docker run -d --rm -p 80:8080 ${env.DOCKER_REGISTRY}/${env.APP_NAME}:${env.BUILD_VERSION}
                '
                """
                echo "Deployed ${APP_NAME} to production."
            }
        }
    }

    post {
        always {
            cleanWs()
            echo 'Workspace cleaned.'
        }
        success {
            mail to: 'team@example.com',
                 subject: "✅ Successful build ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                 body: "Great job! The pipeline completed successfully."
        }
        failure {
            mail to: 'team@example.com',
                 subject: "❌ Failed build ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                 body: "Something went wrong. Check the Jenkins console for details."
        }
    }
}

이 파이프라인이 보여주는 내용:

  • 병렬 단계를 통한 빠른 피드백 제공.
  • SonarQube를 이용한 코드 품질 검사.
  • 의존성 감사와 HTML 보고서 생성.
  • Docker를 활용한 컨테이너화 및 사설 레지스트리로 이미지 푸시.
  • 스테이징 배포, 수동 승인, 그리고 프로덕션 배포.
  • 빌드 후 작업으로 정리 및 알림 전송.
Back to Blog

관련 글

더 보기 »

AWS와 Docker에서 Jenkins

Jenkins on AWS + Docker용 표지 이미지 https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-upload...