Spring Boot 파이프라인 스크립트

DevBadger·2026년 1월 21일

Jenkins

목록 보기
6/6
post-thumbnail

이전 포스트(Git 연동 & Webhook, 브랜치/PR 트리거 설계)에서 Jenkins와 GitHub Webhook 연동으로 develop 브랜치 PR merge 시 자동 트리거 설정을 완료했습니다. 이번 글에서는 Spring Boot 프로젝트를 Docker 이미지로 빌드 → tar 전송 → 원격 배포하는 파이프라인을 설명합니다.

파이프라인 전체 구조

파이프라인은 5단계로 구성되어 있습니다.

  1. Pre-cleaning: Docker 디스크 정리
  2. 자동 버전 태깅: Git 태그 추출 및 빌드명 설정
  3. JAR 빌드: Gradle bootJar 생성
  4. SSH tar 전송: 이미지 tar 원격 전송
  5. 원격 배포: docker-compose 재실행

주요 특징

  • Docker 용량 자동 정리 (Pre-cleaning)
  • Git 태그 듀얼 태깅 (example:latest + example:v1.2.3)
  • 대용량 tar 전송 (이미지 빌드 생략)
  • docker-compose 통합 배포

전체 Jenkinsfile 스크립트

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"
    }
  }
}

단계별 상세 설명

1. 디스크 용량 최적화 (Pre-cleaning)

sh "docker image prune -f"                    // Dangling 이미지 삭제
sh "docker builder prune -f --filter 'until=24h'" // 24시간 경과 캐시 삭제

로컬/원격 모두 적용으로 No space left on device 오류 방지합니다.

2. 자동 버전 태깅

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

3. JAR 빌드

sh "./gradlew clean bootJar -x test"
  • clean : 이전 빌드 삭제
  • bootJar : 실행가능 fat JAR 생성
  • -x test : 배포용 테스트 생략

4. SSH로 산출물(tar) 전송

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)만 전송, 소스코드 제외

5. 원격 배포 자동화

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 로드 이미지만 사용

배포 서버 필수 설정

1. docker-compose.yml

원격 서버의 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

2. Jenkins Credential 등록

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 편집기로 열어 나온 내용 전부를 넣어줍니다.

3. Jenkins 서버 Tools 플러그인

여기서는 이전 React + Vite 파이프라인과 다르게 tools를 사용합니다. 이는 Jenkins에서 지원하는 플러그인을 해당 script에서 사용하겠다는 의미입니다.

Jenkins UI > Setting > Tools 에서 JDK와 Gradle을 찾아 설정해 줍니다.

  • JDK
    • name : JDK 21
    • Install automatically 체크
    • Version : 21버전 중에 선택
  • Gradle
    • name : Gradle 8.13
    • Install automatically 체크
    • Version : Gradle 8.13 선택

참고로 해당 name에 작성한 이름은 script의 tools에 명시된 이름과 동일해야 합니다.

트러블슈팅

문제원인해결법
No space left디스크 부족Pre-cleaning 단계 확인
Permission deniedSSH 권한Credential Private Key 재입력
docker compose not found플러그인 미설치apt install docker-compose-plugin
Image load 실패tar 손상docker load -i 로그 확인

장단점

장점:

  • Pre-cleaning으로 안정적 디스크 관리
  • Git 태그 듀얼 태깅으로 롤백 용이
  • -no-build로 배포 속도 최적화
  • post cleanup으로 Jenkins 에이전트 정리

단점:

  • tar 전송 시간 (이미지 크기 의존)
  • SSH 네트워크 의존성
  • 단일 서버 구조 확장성 제한

도입 시 고려사항

  1. Slack 알림post 블록에 Slack Notifier Plugin 추가
  2. 백업docker compose pull로 이미지 사전 백업
  3. 보안: SSH 키 로테이션 및 IP 화이트리스트

마무리

이 파이프라인은 Spring Boot → Docker tar → 원격 배포를 완전 자동화된 Spring Boot 배포 플로우를 제공합니다.
젠킨스 서버의 스펙 및 프로젝트 규모에 따라 다르겠지만 필자는 이 구조로 PR → 4분 배포를 안정적으로 운영했습니다.

profile
개발 오소리

0개의 댓글