
이전 포스트(Git 연동 & Webhook, 브랜치/PR 트리거 설계)에서 Jenkins와 GitHub Webhook 연동으로
develop브랜치 PR merge 시 자동 트리거 설정을 완료했습니다. 이번 글에서는 Spring Boot 프로젝트를 Docker 이미지로 빌드 → tar 전송 → 원격 배포하는 파이프라인을 설명합니다.
파이프라인은 5단계로 구성되어 있습니다.
example:latest + example:v1.2.3)pipeline {
agent any
options {
timestamps()
timeout(time: 40, unit: 'MINUTES')
}
environment {
// [필수 설정] 원격 서버 접속 정보
REMOTE_HOST = "xx.xx.xxx"
// 원격 서버에서 Docker 컨테이너 실행 유저
REMOTE_USER = "ubuntu"
// Jenkins에 등록된 SSH Key ID
SSH_CRED_ID = "web-server-example"
DEPLOY_ROOT = "/home/ubuntu/app/jenkins/example"
// 이미지 및 파일 이름 정의
IMG_SPRING = "example:latest"
TAR_SPRING = "example-spring.tar"
}
tools {
jdk "JDK 21"
gradle "Gradle 8.13"
}
stages {
stage('Build JAR') {
steps {
script {
// gradlew로 clean -> bootJar(JAR생성) -> 테스트 실행 생략
sh "chmod +x gradlew"
sh "./gradlew clean bootJar -x test"
}
}
}
stage('Build & Save Image') {
steps {
script {
// 🚨 디스크 용량 확보 (Pre-cleaning)
echo "🧹 Docker space optimization..."
sh "docker image prune -f"
sh "docker builder prune -f --filter 'until=24h'"
// Git Short Hash 추출
def gitVer = sh(script: "git describe --tags --always", returnStdout: true).trim()
// 이미지 태그 설정
def baseImageName = IMAGE_NAME.replace(":latest", "")
def imgTagged = "${baseImageName}:${gitVer}"
echo "🐳 Building Docker Image (Tags: ${gitVer}, latest)..."
sh """
docker build --no-cache \
-t $IMG_SPRING \
-t ${imgTagged} .
"""
echo "📦 Archiving Image to Tar..."
sh "docker save -o $TAR_SPRING $IMG_SPRING ${imgTagged}"
}
}
}
// 2️⃣ 원격 서버로 tar 파일 전송
stage('Transfer to Remote') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: env.SSH_CRED_ID, keyFileVariable: 'SSH_KEY')]) {
script {
echo "🚀 Transferring artifacts to $REMOTE_HOST..."
// 원격 배포 폴더 생성 (이미 있으면 무시)
sh 'ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_HOST" "mkdir -p $DEPLOY_ROOT"'
// Docker 이미지 tar 파일 전송
sh 'scp -i "$SSH_KEY" -o StrictHostKeyChecking=no "$TAR_SPRING" "$REMOTE_USER@$REMOTE_HOST:$DEPLOY_ROOT/"'
sh 'rm "$TAR_SPRING"'
}
}
}
}
// 3️⃣ 원격 배포 및 서비스 재시작
stage('Remote Deploy') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: env.SSH_CRED_ID, keyFileVariable: 'SSH_KEY')]) {
script {
echo "🚀 Executing remote deployment on $REMOTE_HOST..."
// 원격 서버 접속 후 이미지 로드 및 컨테이너 재기동
sh '''
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_HOST" "
cd $DEPLOY_ROOT
docker compose down
mkdir -p logs
chmod -R 755 logs 2>/dev/null || true
docker load -i $TAR_SPRING
docker compose up -d --no-build
rm $TAR_SPRING
docker image prune -f
"
'''
}
}
}
}
}
// ✅ 파이프라인 종료 후 후처리
post {
success { echo "✅ Spring Boot Deployment Successful!" }
failure { echo "❌ Deployment Failed" }
cleanup {
sh "rm -f *.tar"
sh "docker image prune -f"
}
}
}
sh "docker image prune -f" // Dangling 이미지 삭제
sh "docker builder prune -f --filter 'until=24h'" // 24시간 경과 캐시 삭제
로컬/원격 모두 적용으로 No space left on device 오류 방지합니다.
def gitVer = sh(script: "git describe --tags --always", returnStdout: true).trim()
currentBuild.displayName = "#${BUILD_NUMBER}-${gitVer}"
env.GIT_VER = gitVer // 전역 변수 저장
결과: #13-v1.0.0 또는 #13-v1.0.0-3-gabc123
sh "./gradlew clean bootJar -x test"
clean : 이전 빌드 삭제bootJar : 실행가능 fat JAR 생성-x test : 배포용 테스트 생략sh 'ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_HOST" "mkdir -p $DEPLOY_ROOT"'
sh 'scp -i "$SSH_KEY" -o StrictHostKeyChecking=no "$TAR_SPRING" "$REMOTE_USER@$REMOTE_HOST:$DEPLOY_ROOT/"'
특징: tar 파일(~500MB)만 전송, 소스코드 제외
sh '''
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$REMOTE_USER@$REMOTE_HOST" "
cd $DEPLOY_ROOT
docker compose down
docker load -i $TAR_SPRING
docker compose up -d --no-build
rm $TAR_SPRING
docker image prune -f
"
'''
핵심: --no-build로 tar 로드 이미지만 사용
원격 서버의 docker-compose.yml 예시:
version: '3.8'
services:
example:
image: example:latest
ports:
- "8080:8080"
volumes:
- ./logs:/app/logs
restart: unless-stopped
위치:
/home/ubuntu/app/jenkins/example/docker-compose.yml
ID: web-server-credentails
Kind: SSH Username with private key
Username: ubuntu
Private Key: ~/.ssh/id_rsa (서버 SSH 키)
Jenkins UI에서 GitHub Credentails 생성하는 곳에서 만들어 줍니다. Private Key 같은 경우 Enter directly 체크하여 .pem을 text 편집기로 열어 나온 내용 전부를 넣어줍니다.
여기서는 이전 React + Vite 파이프라인과 다르게 tools를 사용합니다. 이는 Jenkins에서 지원하는 플러그인을 해당 script에서 사용하겠다는 의미입니다.
Jenkins UI > Setting > Tools 에서 JDK와 Gradle을 찾아 설정해 줍니다.
참고로 해당
name에 작성한 이름은 script의 tools에 명시된 이름과 동일해야 합니다.
| 문제 | 원인 | 해결법 |
|---|---|---|
No space left | 디스크 부족 | Pre-cleaning 단계 확인 |
Permission denied | SSH 권한 | Credential Private Key 재입력 |
docker compose not found | 플러그인 미설치 | apt install docker-compose-plugin |
Image load 실패 | tar 손상 | docker load -i 로그 확인 |
장점:
-no-build로 배포 속도 최적화단점:
post 블록에 Slack Notifier Plugin 추가docker compose pull로 이미지 사전 백업이 파이프라인은 Spring Boot → Docker tar → 원격 배포를 완전 자동화된 Spring Boot 배포 플로우를 제공합니다.
젠킨스 서버의 스펙 및 프로젝트 규모에 따라 다르겠지만 필자는 이 구조로 PR → 4분 배포를 안정적으로 운영했습니다.