[Jenkins] - spring boot project pipeline 생성하기

2024. 11. 11. 10:05현대 오토에버 SW 스쿨 - 클라우드/버전관리_Jenkins

SMALL

1. Spring Boot Project Pipeline

Spring Boot Project Pipeline 를 생성하고 Git Hub 와 연동

1) check out : SCM 으로부터 코드를 내려받는 동작

  • git remote 사이트로부터 내려받을 때
git url: 'URL', branch: '브랜치이름'
  • New Item 을 선택해서 이름을 결정하고 Pipeline 을 선택
# script 만 작성
pipeline{
    agent any
    stages{
        stage("checkout"){
            steps{
                 git url: "https://github.com/itggangpae/JenkinsPipeline.git", branch: "master"
            }
        }
    }
}

 

2) 코드를 작성하고 로컬에서 단위 테스트

  • 단위 테스트를 하기 위해서 build.gradle 파일의 dependenciese 항목
    -> JUnit 라이브러리의 의존성을 추가하고 Rebuild 를 수행해서 의존성 라이브러리 다운로드
// https://mvnrepository.com/artifact/junit/junit
testImplementation 'junit:junit:4.13.2'
  • 테스트 클래스 생성: src/test 디렉터리에 생성 (Calculator Test)
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class CalculatorTest {
   private Calculator calculator = new Calculator();

   @Test
   public void testSum(){
       assertEquals(Integer.valueOf(5), calculator.sum(2, 3));
   }
}
  • 테스트 수행 : ./gradlew test --> 현재 상태는 error
  • Calculator 클래스 구현
import org.springframework.stereotype.Service;

@Service
public class Calculator {

   public Integer sum(Integer a, Integer b){
       return a+b;
   }
}
  • 테스트 수행 -> 테스트 성공
  • Controller 클래스 생성 ( CalculatorController)
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class CalculatorController {
   private final Calculator calculator;

   @RequestMapping("/")
   String sum(@RequestParam("a") Integer a, @RequestParam("b") Integer b) {
       return String.valueOf(calculator.sum(a, b));
   }
}
  • 실행 (./gradlew bootRun) 후 브라우저에서 확인 
    http://localhost:8080/?a=10&b=7
  • 코드 push

 

3) Jenkins 에서 컴파일과 테스트

  • script 에 3개의 stage 를 추가하고 빌드를 수행
 stage("permission"){
            steps{
                 sh "chmod +x ./gradlew"
            }
        }
        stage("compile"){
            steps{
                 sh "./gradlew compileJava"
            }
        }
        stage("test"){
            steps{
                 sh "./gradlew test"
            }
        }

 

4) Code Coverage 

  • 코드 전체를 대상으로 테스트를 진행하고 검증이 완료된 부분을 식별하는 것
  • 코드를 업데이트 할 때 필수 항목
  • 로컬에서 코드 커버리지 수행
    • 코드 커버리지 도구를 build.gradle 의 plugins 항목에 추가 
    • 빌드 다시 수행 (id 'jacoco')
  • 코드 커버리지 기준을 build.gradle 에 설정 후 빌드를 다시 수행
jacocoTestCoverageVerification{
   violationRules{
       rule{
           limit{
               minimum = 0.2
           }
       }
   }
}
  • 수행 및 보고서 발행 -> 코드 push
./gradlew test jacocoTestCoverageVerification

./gradlew test jacocoTestReport
  • 파이프라인에 stage 로 추가
stage("test coverage"){
    steps{
         sh "./gradlew test jacocoTestCoverageVerification"
         sh "./gradlew test jacocoTestReport"
    }
}

 

4) 정적 코드 분석

<?xml version="1.0" ?>

<!DOCTYPE module PUBLIC
       "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
       "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">

<module name="Checker">
   <module name="TreeMaker">
       <module name="ConstantName" />
   </module>
</module>

# 로컬에서 수행
./gradlew checkstyleMain

 

2. trigger 와 알림

1) 트리거

  • 빌드를 자동으로 시작하는 동작을 pipeline trigger 
  • 트리커의 유형
    • 외부 트리거
    • 폴링 SCM 트리거
    • 스케쥴 빌드 트리거
  • 외부 트리거
    • 노티파이어가 호출되면 jenkins 가 빌드를 시작하는 방식
    • 노티파이어의 역할을 할 수 있는 것은 다른 파이프라인 빌드나 SCM 시스템의 원격 script
    • 깃헙 - 트리거 > jenkins
  • github 을 이용해서 jenkins 에 trigger
    • jenkins 에 github 플러그인을 설치
    • jenkins 용 비밀키를 생성
    • github webhook 을 설정하고 jenkins 의 주소와 키를 저장
  • 폴링 SCM 트리거
    • jenkins 가 주기적으로 SCM 을 호출하고 repository 에 push 가 발생했는지를 체크
    • 변경 시 빌드를 다시 진행
    • jenkins 가 SCM 에 접속할 수 없는 방화벽 네트워크 안에 있는 경우 유용함
    • 빌드 시간이 길고 커밋이 자주 발생해서 서버에 과부하가 초래되는 겨웅
  • Jenkinsfile 을 이용한 파이프라인으로 변경
    • 프로젝트에 jenkinsfile 을 생성하고 이전에 수행했던 작업을 작성
      pipeline{
         agent any
         stages{
             stage("permission"){
                 steps{
                      sh "chmod +x ./gradlew"
                 }
             }
             stage("compile"){
                 steps{
                      sh "./gradlew compileJava"
                 }
             }
             stage("test"){
                 steps{
                      sh "./gradlew test"
                 }
             }
             stage("test coverage"){
                 steps{
                      sh "./gradlew test jacocoTestCoverageVerification"
                      sh "./gradlew test jacocoTestReport"
                 }
             }
         }
      }

      • Jenkins 에 접속해서 SCM 에서 GIT -> URL 설정
      • 빌드해서 빌드가 제대로 수행되는지 확인
    • Polling SCM 설정
      • 구성에서 PollSCM 체크
      • 스케쥴 입력 - 분 시간 일 월 요일
      • 33 * * * * 을 설정하면 33분에 수행
      • commit 이 동일하지 않을 때만 수행
    • Build periodically 옵션
      • 설정 방법 - polling SCM 과 동일
      • 변경된 내용이 없어도 무조건 Build 를 다시 수행
      • commit pipeline 에서는 거의 사용하지 않고 밤에 실행되는 복잡한 통합 테스트에서 사용

 

2) push 가 발생했을 때 build

  • git hub 에서 token 발급 - jenkins 에 등록해서 연동
  • jenkins 와 Git hub 연결
    • Dashboard 화면에서 Jenkins 관리를 클릭
    • System 을 클릭하고 화면을 하단으로 스크롤
      -> Git Hub 항목 -> Add GitHub Server -> 이름 설정 -> Credential -> Add > Jenkins 
    • Domail 은 두고 Kind 를 Secret test 로 변경 후 ID 에 토큰 이름 지정
      -> Secret 에 토큰 값 설정 -> Add -> Test Connection 수행
    • Jenkins Item 에서 트리거로 Github hook trigger for GITScm polling 을 선택

3) 트리커 생성 옵션

  • Build periodically : 주기적으로 무조건 Build
  • Github hook trigger for GITScm  polling: Github 의 브랜치에 push 발생 시 수행 -> Credential 설정
  • poll SCM: 주기적으로 빌드, 변경 내역이 있을 때만 빌드 다시 수행

 

3. 알림

1) 개요

  • jenkins 에서는 다양한 방식으로 빌드 상태를 알려줄 수 있음
  • 기본은 email 이지만 plugin 설치 시 다른 방식으로 알림을 보낼 수 있음

2) email 을 전송

  • SMTP 서버 확보 후 설정에 추가
  • script 에서 mail to 명령으로 전송

 

4. 팀 단위 개발 전략

1) 개발 워크플로우

  • 트렁크 기반 워크플로우
    • 트렁크 또는 마스터라고 불리는 하나의 중앙 레포지토리에 프로젝트의 모든 변경 사항이 저장되는 구조
    • 모든 팀원들은 중앙 레포지토리를 복제해서 자신의 컴퓨터에 로컬 복사본을 만들어서 사용
    • 변경된 내용은 중앙 레포지토리로 직접 커밋
  • 브랜치 워크플로우
    • 코드를 각기 다른 브랜치에 저장
    • 개발자가 신규 기능을 개발하는 경우 :: 트렁크에서 분기된 전용 브랜치 생성 -> 해당 브랜치에 기능 구현과 관련된 모든 변경 사항을 저장
    • 메인 코드를 건드리지 않고 기능 개발 가능
  • 포크 워크플로우
    • 오픈 소스 커뮤니티에서 사용
    • 각 개발자는 각자의 서버 레포지토리를 갖는 구조

5. 자동 인수 테스트

1) 인수 테스트

  • 요구사항대로 기능이 구현되었는지 확인하는 과정
  • 테스트 :: 전체 시스템을 사용자 관점에서 시험하는 블랙박스 테스트 포함
  • 인스 테스트 자동화가 어려운 이유
  • 사용자 참여 : 테스트를 만들 때 사용자와 함께 작성
  • 의존성 통합 
    • 시스템이 전체적으로 잘 동작하는지를 확인해야 함
    • 따라서 테스트할 애플리케이션은 모든 의존성을 포함해서 실행해야 함
  • 스테이징 환경 
    • 기능 및 비기능 테스트를 확실히 수행하고자 하면 스테이징 환경이 프로덕션 환경과 일치해야 함
  • 애플리케이션의 동일성 
    • 애플리케이션은 한 번만 빌드해야 하고 프로덕션에서도 동일한 바이너리를 사용해야 함
    • 도커 이미지를 만들어서 배포
    • 실행 환경이 달라서 발생할 수 있는 문제를 제거해야 하기 때문
  • 릴리스 준비
    • 애플리케이션이 인수 테스트를 통과하면 사용자에게 바로 제공할 수 있도록 준비 되어야 함

 

2) 도커 레지스트리

  • 도커 레지스트리는 도커 이미지 저장소로 도커 이미지를 push 하거나 pull 할 수 있는 서버 애플리케이션
  • 도커 허브는 클라우드 기반의 공식 도커 레지스트리
  • 소프트웨어 레포지토리 or 아티팩트 레포지토리
    :: 애플리케이션을 개발하는 곳에서는 별도의 서버를 만들어서 소프트웨어 패키지를 저장하고 불러오거나 검색할 수 있도록 함
  • 애플리케이션을 배포하는 곳에서 별도의 레포지토리를 만드는 이유
    • 파일 크기
    • 버전 관리
    • 리비전 매핑 : 산출물과 바이너리가 정확히 매핑
    • 패키징 : 산출물은 압축된 형식으로 저장
    • 접근 제어 : 소스 코드에 직접 접근할 수 있는 사용자와 바이너리에 접근할 수 있는 사용자를 구분하는 것 가능
  • 도커 레지스트리 사용
    • 클라우드 방식의 도커 레지스트리
    • Docker Hub
      • 대체 서비스
        AWS 의 ECR
        GCP 의 Artifact Registry
        Azure 의 Container Registry
        Gitlab 의 Container Registry
    • 자체 호스팅 방식의 도커 레지스트리
      • 사내 네트워크가 아닌 외부에 소프트웨어를 보관하는 것을 금지하는 경우 사용

3) 도커 레지스트리 구축

  • 도커 레지스트리 애플리케이션을 설치
# 레지스트리 시작
docker run -d -p 5000:5000 --restart=always --name registry registry
 

Registry

The Docker Hub registry implementation

docs.docker.com

  • 인증서를 발급받은 경우 registry 실행 
    인증서 파일 (domain.crt 와 domain.key 파일) 을 컨테이너의 certs 디렉토리로 이동한 후 수행
docker run -d -p 5000:5000 --restart=always --name registry -v ‘pwd’/certs:/certs -e REGISTRY_HTTP_ADDR=0.0.0.0:443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key registry
  • 접속 제한 추가

4) Spring Boot 프로젝트를 도커 이미지로 만들기

  • 프로젝트를 빌드 : 실행 가능한 파일을 생성
./gradlew clean build
# 빌드에 성공을 하면 build/libs 에 실행 파일이
# 프로젝트이름-0.0.1-SNAPSHOT.jar 라는 이름으로 만들어짐
  • Dockerfile 을 루트 디렉터리에 생성하고 작성
FROM bellsoft/liberica-openjdk-alpine:17

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]
  • Gradle Build 를 수행 : ./gradlew clean build
  • Docker Build 를 수행 : docker build -t jenkinspipeline .
  • 이미지 확인 : docker images
  • Jenkinsfile 에 stage 추가 
stage("Gradle Build"){
   steps{
      sh "./gradlew clean build"
   }
}

stage("Docker Build"){
   steps{
       sh "docker build -t jenkinspipeline ."
  }
}

5) 이미지를 레지스트리로 푸시

  • 이미지를 레지스트리로 푸시를 하고자 하는 경우
    • 레지스트리이름/이미지이름:태그 형태로 만들어야 함
  • 로그인을 요구하는 레지스트리라면 Credential 을 추가
  • 직접 구축한 레지스트리를 사용하고자 하는 경우 daemon.json 파일에 레지스트리를 추가
{
	"insecure-registries":["54.180.146.200:5000"],
	"debug": true,
	"experimental": false
}
반응형
LIST