Jenkins 로 다수 환경의 파이프 라인 설정하기
Jenkins 로 다수 환경의 파이프 라인 설정하기

Jenkins 로 다수 환경의 파이프 라인 설정하기

Tags
Jenkins
Infra
CI/CD
Published
August 12, 2025
Author
lkdcode

🔥 Jenkins 설치

도커 혹은 Download and deployDownload and deployDownload and deploy 공식 홈페이지에서 다운로드한다.
가볍게 테스트하거나 연습은 도커를 운영 환경에서 안정적으로 사용하는 것은 공식 홈페이지를 추천한다.
Jenkins 초기 비밀번호만 입력하면 이후 특별한 추가 설정없이 사용할 수 있다.
# Linux/Mac sudo cat /var/lib/jenkins/secrets/initialAdminPassword # Window C:\\Program Files (x86)\\Jenkins\\secrets\\initialAdminPassword
 
위의 파일에 초기 비밀번호가 있으므로 관리자 비밀번호에 입력해주면 된다. 플러그인 설치 및 환경 설정 등은 기본값을 추천한다.
 

🔥 Generic Webhook Trigger - Plugin 설치

notion image
젠킨스 파이프라인을 구성하기 전에 플러그인을 설치해야 한다. Github 과 간단하게 연동할 수 있는 플러그인으로 Github 에서 Webhook 을 받아 파이프라인을 실행할 수 있도록 도와준다.
 

🔥 New Item

notion image
Jenkins 는 기본적으로 8080 포트에 떠있다. (아마 설치하면서 봤을 것이다.) localhost:8080 으로 접속한다면 바로 사용이 가능하다. Dashboard > All > New Item 을 통해 파이프라인, 폴더 등을 생성할 수 있다. 사진과 같이 Pipeline 을 생성해준다.
 

🎯 Pipeline - Git SCM

notion image
 
New Item 으로 생성한 파이프 라인 설정 창에서 하나씩 설정한다. 먼저 Jenkins 가 원격 레파지토리에 등록된 스크립트를 읽고 파이프라인을 실행할 수 있도록 설정한다.
notion image
  • Definition : Pipeline script from SCM
  • SCM : Git
  • Repositories : 파이프라인 스크립트가 저장된 원격 저장소
  • Credentials : 해당 레파지토리에 접근할 수 있는 권한
  • Brach Specifier : 해당 레파지토리 브랜치
  • Script Path : .jenkinsfile 경로
 
SCM Git 을 사용하기 위해서 Github 계정을 등록해 JenkinsGithub 에 접근할 수 있도록 설정한다.
Dashboard > Jenkins 관리 > Credentials > System > Global Credentials(혹은 특정 시스템) > …
 
위의 경로에서 Jenkins 가 사용할 Credentials 를 등록해줄 수 있는데 Github 계정으로 사용할 경우 Username with password 로 설정해주면 된다. 설정 후 Pipeline 에서 불러오면 된다.
 
Lightweight checkout 설정은 Jenkins 가 전체 저장소를 클론하지 않고 필요한 파일만 가져오는 설정이다. 체크를 안 하면 전체 클론 체크를 하면 해당 파일만 가져온다. Jenkins 가 스크립트를 수정하고 레파지토리에 푸시하는 경우라면 체크를 해제한다. (전체 클론)
 

🔥 Generic Webhook Trigger

Pipeline 설정이 끝났다면 Triggers 탭으로 이동해 Generic Webhook Trigger 를 활성화하여 설정해준다.
notion image
 

🎯 Token

notion image
아무나 트리거하지 못 하도록 외부 요청을 받을 때 인증을 확인하기 위한 파라미터 설정이다. Github → Webhook 설정에서 쿼리 파라미터로 넘겨줘야 한다.
 

🎯 Github Webhook

Jenkins 에서 설정한 토큰을 쿼리 파라미터로 넘겨주고 어떤 이벤트를 보낼지 등을 설정해 줄 수 있다.
notion image
위에처럼 PayloadURL 에서 해당 토큰을 넘겨주어야 한다.
 

🎯 Post content parameters

notion image
body 로 부터 받은 JSON 데이터를 파싱해 변수에 저장할 수 있다. 아래의 예시와 같은 페이로드를 넘겨받으면 $.action 으로 { "action" : "opened" } 의 값인 opended 를 얻을 수 있다. 해당 데이터를 MY_ACTION 이라는 변수에 설정하는 것이다.
 
  • exam JSON
notion image
 

🎯 Optional filter

notion image
트리거 실행 여부를 설정할 수 있다. (Predicate) 위에서 설정한 MY_ACTION 이라는 값이 opended 일 때만 트리거를 수행하게 하고 싶다면 Expression^opened$ 값을 적어주고 Text 에 $MY_ACTION 을 넣어주면 된다. Regular expression 링크로 이동해 해당 정규식이 올바른지 확인할 수 있다.
 

🎯 my-script.jenkinsfile

Jenkins 가 수행할 파이프라인을 간단하게 작성해보자.
pipeline { agent any // stages 에서 사용할 전역 환경 변수 environment { MY_VARIABLE = "Hello" // 매 스테이지마다 이 값으로 초기화 됨 } stages { stage('Stage 1') { steps { echo "${MY_VARIABLE} world" // Hello world env.MY_VARIABLE = "이렇게 덮어쓰기를 하더라도" echo "${MY_VARIABLE} world" // 변경은 각 스테이지에서만 유효 } } stage('Stage 2') { steps { echo "${MY_VARIABLE} world" // Hello world 출력됨. } } } // 슬랙으로 메시지 보내기 (Slack Notification Plugin) post { success { slackSend ( channel: '#my-cicd', color: '#00FF00', message: """ 배포 성공 """ ) } failure { slackSend ( channel: '#my-cicd', color: '#FF0000', message: """ 배포 실패 """ ) } } }
위의 스크립트를 Jenkins 가 바라보는 레파지토리에 위치해 놓고 Github 에서 트리거를 발생 시켜 파이프라인이 동작하도록 볼 수 있다.
 

🔥 Github Webhook → Trigger

설정이 모두 완료되면 Github 이벤트로부터 시작된 트리거가 잘 동작할 것이다. 각 상황에 맞게 배포 파이프라인 스크립트를 작성해 주면 된다.
 

🔥 Pipeline

시나리오대로 보면 각각의 파이프 라인은 다음과 같다.
  1. feat/.. → dev 병합 → Webhook → Jenkins → 개발 파이프라인 실행 → 개발 서버에 배포
  1. dev → prod 병합 → Webhook → Jenkins → 프로덕션 파이프라인 실행 → 프로덕션 서버에 배포
  1. prod → releases → Webhook → Jenkins → 온-프라미스 파이프라인 실행 → 빌드, 압축, 슬랙 전송
 

🎯 Example) dev pipeline script

pipeline { agent any environment { TARGET_BRANCH = 'dev' GITHUB_REPO = 'https://github.com/my-server-api.git' BUILD_DIR = 'build/libs' APP_PORT : '18080' IMAGE_NAME : 'lkdcode-api' CONTAINER_NAME : 'lkdcode-api-container' SPRING_PROFILES : "dev" } stages { stage('🐈‍⬛ Git Checkout') { steps { sh 'git remote prune origin || true' git branch: "${TARGET_BRANCH}", url: "${GITHUB_REPO}" } } stage('🚀 Project Build') { steps { script { sh './gradlew clean bootJar' def files = sh(script: "ls ${BUILD_DIR}/*.jar", returnStdout: true).trim().split("\n") if (files.size() == 0) { error("❌ No JAR file found in ${BUILD_DIR}") } env.JAR_NAME = files[0].tokenize('/').last() echo "✅ Found JAR: ${env.JAR_NAME}" } } } stage('🐳 Docker Build') { steps { sh """ PID=\$(lsof -ti tcp:${APP_PORT}) && kill -9 \$PID || true docker stop ${CONTAINER_NAME} || true docker rm ${CONTAINER_NAME} || true docker rmi ${IMAGE_NAME} || true docker build --build-arg JAR_FILE=${BUILD_DIR}/${JAR_NAME} -f ${DOCKER_FILE} -t ${IMAGE_NAME} . docker run -d \ --name ${CONTAINER_NAME} \ -p ${APP_PORT}:${APP_PORT} \ -e SPRING_PROFILES_ACTIVE=${SPRING_PROFILES} \ ${IMAGE_NAME} """ } } } post { success { slackSend ( channel: '#my-cicd', color: '#00FF00', message: """ 메시지 작성.. """ ) } failure { slackSend ( channel: '#my-cicd', color: '#FF0000', message: """ 메시지 작성.. """ ) } } }
 
  1. feat/.. → dev 병합 → Webhook → Jenkins → 개발 파이프라인 실행 → 개발 서버에 배포
 

stage('🐈‍⬛ Git Checkout')

배포할 프로젝트가 있는 원격 레파지토리로 접속해 해당 프로젝트를 클론한다.
 

stage('🚀 Project Build')

클론된 프로젝트를 빌드한다. SpringBoot 를 기준으로 작성됐으며 상황에 따라 알맞은 명령을 통해 빌드를 수행하게 하면 된다.
 

stage('🐳 Docker Build')

빌드된 프로젝트로 Docker 이미지를 생성하고 컨테이너에 띄운다. Jenkins 가 설치된 로컬이 개발 서버라 가정한 스크립트이다. 파이프 라인을 구축하다보면 사용하지 않는 이미지들이 많이 쌓이게 되는데 이미지와 컨테이너들을 정리하는 스크립트를 추가하여 메모리 관리를 하자.
 

post {…}

해당 스텝은 파이프 라인 성공/실패 여부를 슬랙 메시지로 받는 설정이다. 슬랙에 앱 추가에서 Jenkins CI 를 선택해 설정해주면 된다. 스크립트에서는 채널을 설정해주고 슬랙에 메시지를 보낼 수 있으며 슬랙에 커스텀으로 추가한 이모지도 사용할 수 있다. (공식 문서를 참고 Slack Notification PluginSlack Notification PluginSlack Notification Plugin)
 

🎯 Example) prod pipeline script

pipeline { agent any environment { TARGET_BRANCH = 'prod' GITHUB_REPO = 'https://github.com/lkdcode-jenkins.git' BUILD_DIR = 'build/libs' APP_PORT : '18080' IMAGE_NAME : 'lkdcode-api' CONTAINER_NAME : 'lkdcode-api-container' SPRING_PROFILES : "dev" } stages { stage('🐈‍⬛ Git Checkout') { steps { sh 'git remote prune origin || true' git branch: "${TARGET_BRANCH}", url: "${GITHUB_REPO}" } } stage('🚀 Project Build') { steps { script { sh './gradlew clean bootJar' def files = sh(script: "ls ${BUILD_DIR}/*.jar", returnStdout: true).trim().split("\n") if (files.size() == 0) { error("❌ No JAR file found in ${BUILD_DIR}") } env.JAR_NAME = files[0].tokenize('/').last() echo "✅ Found JAR: ${env.JAR_NAME}" } } } stage('🐳 Docker Build & Push to ECR') { steps { sh """ aws ecr get-login-password <login..> docker buildx create --use --name ecrbuilder >/dev/null 2>&1 || true docker buildx build \ --platform linux/amd64,linux/arm64 \ --label app=my-server-app \ -f Dockerfile \ --build-arg JAR_FILE=${BUILD_DIR}/${JAR_NAME} \ -t <aws-ecr>${IMAGE_TAG} \ . --push docker buildx prune -af || true docker image prune -a -f || true """ } } stage('🚀 Deploy to EC2') { steps { sshagent(credentials: ['lkdcode-aws-pem']) { sh """ ssh -o StrictHostKeyChecking=no <lkdcode-aws> 'bash -se' << 'REMOTE' set -euo pipefail sudo docker stop my-server-app || true sudo docker rm my-server-app || true sudo docker rmi my-server-app || true sudo docker image prune -a -f --filter "label=app=my-server-app" aws ecr get-login-password <login> sudo docker pull <aws-ecr>${IMAGE_TAG} sudo docker run -d --name my-server-app -p ${APP_PORT}:${APP_PORT} --restart unless-stopped -e SPRING_PROFILES_ACTIVE=prod <aws-ecr>${IMAGE_TAG} REMOTE """ } } } } post { success { slackSend ( channel: '#my-cicd', color: '#00FF00', message: """ 메시지 작성.. """ ) } failure { slackSend ( channel: '#my-cicd', color: '#FF0000', message: """ 메시지 작성.. """ ) } } }
 
  1. dev → prod 병합 → Webhook → Jenkins → 프로덕션 파이프라인 실행 → 프로덕션 서버에 배포
 
dev 파이프 라인 과 크게 다를 건 없다. Git → Build → Docker → .. 의 순서는 똑같지만 EC2에 ECR을 이용해 배포하는 스크립트이다. 순서를 다시 정의하면 Git → Build → Docker Image Push to ECR → Pull Image to ECR → Run.. 순서로, 빌드된 프로젝트를 도커 이미지로 만들고 AWS-ECR 에 push 한다. 이후 EC2 로 ssh 접속을 하여 ECR 에 push한 이미지를 pull 한다. 이후 EC2 안에서 컨테이너를 내리고 실행하는 건 똑같다.
 
여기서 주의할 점은 platform 구절인데, macOS(Jenkins가 설치된 곳)에서 Docker 이미지를 빌드하면 기본값은 해당 macOS CPU 아키텍처 하나만 빌드가 되므로 AWS-EC2의 아키텍처와 다르면 해당 이미지로 컨테이너를 실행시킬 수 없다. Linux 냐 macOS 냐보다는 CPU 아키텍처 (amd64 vs arm64) 가 중요하다. AWS-EC2 에도 해당 이미지를 사용할 수 있도록 빌드해 준다.
 

🎯 Example) on-premise pipeline script

/* 생략 */ stages { stage('🥚 배포 파일 압축하기') { steps { sh ''' set -eux zip -j "${NEW_JAR_VERSION}.zip" "my-dir/build/libs/${JAR_VERSION}.jar" ''' } } stage('📤 슬랙에 압축파일 보내기') { steps { slackUploadFile( channel: "<my-slack-channel-id>", filePath: "${JAR_VERSION}.zip", credentialId: "my-slack-token" ) } } } /* 생략 */
 
  1. prod → releases → Webhook → Jenkins → 온-프라미스 파이프라인 실행 → 빌드, 압축, 슬랙 전송
 
온-프라미스 파이프라인 도 위의 스테이지와 흡사하며 마지막에 빌드된 결과를 압축 파일로 만들어 슬랙에 전송하기만 하면 된다. 슬랙에 기본 메시지를 보내는 것과 다른 권한이 필요한데, 해당 권한을 얻기 위해 아래의 사진을 따라하면 된다. 우선 공식 문서를 참고하자. Slack Notification PluginSlack Notification PluginSlack Notification Plugin
 
  • Slack api 로 이동해 Your apps 로 이동한다.
notion image
 
  • Create New App 클릭
notion image
 
  • OAuth & Permissions 에서 토큰 생성
notion image
 
생성된 토큰을 Jenkins 가 사용할 수 있도록 Credentials 에 등록해주고 step 에서 불러온 것이다.
credentialId 에서 불러온 “my-slack-token” 이 위에서 발급한 OAuth Tokens 으로 등록된 자격이다.
stage('📤 슬랙에 압축파일 보내기') { steps { slackUploadFile( channel: "<my-slack-channel-id>", filePath: "${JAR_VERSION}.zip", credentialId: "my-slack-token" ) } }