오늘의하루

Jenkins로 Maven 프로젝트 CI/CD - FreeStyle Project & PipeLine 본문

Spring/mini Project

Jenkins로 Maven 프로젝트 CI/CD - FreeStyle Project & PipeLine

오늘의하루_master 2024. 2. 10. 11:47

CICD란 지속적인 통합과 지속적인 배포를 말한다.

Jenkins는 이런 기능을 보다 편리하게 할 수 있는 Tool이다.

이번 연습에서는 Docker를 사용하지 않고 Jenkins만 사용했다.


EC2 프리티어 사용할 경우 필수

EC2 프리티어에서 Jenkins를 바로 사용하는 경우 메모리 부족으로 서버가 멈추는 현상이 발생한다.

이때는 스왑 메모리를 사용하여 메모리를 늘려서 이러한 현상을 막을 수 있다.

이건 유료 서비스가 아니기 때문에 마음 편히 사용하셔도 좋습니다.

 

Step1-2. EC2 프리티어 Jenkins 서버 터지는 현상 해결

CI / CD를 연습하기 위해 Jenkins에 대해 공부를 시작했는데 빌드시 서버가 터지는 일이 너무 많아서 매번 EC2 인스턴스를 중지하고 다시 실해을 매번 반복하다 Swap이라는것을 알게되었다. 이를 적용

jangto.tistory.com


Ubuntu에 Jenkins 다운로드

Jenkins에 다운로드는 그 어디보다 Jenkins 공식 홈페이지에서 잘 정리해두었다.

Jenkins의 포트 확인

Jenkins를 설치하면 기본적으로 Port 8080에 매칭되어 있는데 이를 그래도 사용해도 되고 변경해도 된다.

변경 할 때는 아래와 같은 경로로 들어가서 변경 및 확인이 가능하다.

$ sudo vim /lib/systemd/system/jenkins.service


# Port to listen on for HTTP requests. Set to -1 to disable.
# To be able to listen on privileged ports (port numbers less than 1024),
# add the CAP_NET_BIND_SERVICE capability to the AmbientCapabilities
# directive below.
Environment="JENKINS_PORT=8080"

만약 여기서 포트 번호를 변경했다면 아래와 같이 재시작을 해줘야 적용이 된다.

$ sudo systemctl daemon-reload
$ sudo systemctl restart jenkins

daemon-reload를 하는 이유는 Jenkins를 포함한 서비스를 systemd를 통해 관리할 때, 서비스를 변경 또는 설정을 적용할 때는 systemd에게 해당 변경 사항을 알려줘야 하는데 이를 daemon-reload를 통해 systemd에게 새로운 설정 파일이나 변경된 설정을 다시 로드하도록 지시한다.

FreeStyle Project & PipeLine

이 두가지는 결과적으로 같은 역할(CI/CD)를 할 수 있도록 만들어 주지만 FreeStyle Project는 비교적 단순한 CI/CD를 할 수 있도록 도와주고 PipeLine은 비교적 복잡한 CI/CD를 할 수 있도록 만들어 준다.

FreeStyle Project

Maven Project의 경우에는 Jenkins 관리의 Tools에서 Maven installations에서 Maven을 추가해줘야한다.별도의 다운로드는 받을 필요없이 해당 Maven의 이름과 버전을 지정해주면 끝이다.

 

1. 소스코드 관리 - Git 여기에는 clone할 Repository URL을 입력해준다.ex) https://github.com/dukbong/-PracticalStudy.git

 

2. Branch Specifier를 지정 해준다.

만약 특정 브랜치의 소스코드를 가져와야 하는게 아니라면 별도의 지정을 할 필요는 없다.

 

3. 빌드 유발을 선택 해준다.

빌드 유발이란 언제 해당 Jenkins Project를 실행할것인지를 정하는 것이다.

1. Build whenever a SNAPSHOT dependency is built
기본 설정이며 POM에 설정되어 있는 모든 의존적인 프로젝트들에 대해서도 같이 빌드를 진행하겠다는 설정
2. 빌드를 원격으로 유발
외부에서 URL을 통해 빌드를 진행할 수 있도록 설정
3. Build after other project are built
다른 프로젝트가 빌드되었을 때 같이 빌드되도록 설정
4. Build periodically 
분, 시간, 일, 주, 월 등을 지정해주면 해당 시당에 빌드가 되도록 설정
5. GitHub hook trigger for GITScm polling
깃허브의 이벤트가 발생할때 마다 빌드되도록 하는 설정
6. Poll SCM
스케줄러처럼 주기적으로 감시하여 변경된 사항이 존재한다면 빌드를 수행하는 설정이며 Build periodically에서 사용하듯이 사용가능하다.

 

4. GitHub의 webhook 설정

1번에서 clone할 Repository의 Settings에 가서 webhook을 누른 후 Add webhook을 선택하여 추가해준다.

이때 추가하는 방법은 아래와 같다.

http://3.35.55.86:8080/github-webhook/

여기서 주소는 각자의 EC2의 Instance의 public IPv4 주소를 넣고 포트의 경우 지정한 포트를 작성후 꼭 마지막에 /github-webhook/을 작성해줘야 한다.

 

5. Build Step 지정

가장 중요한 부분이며 이 부분을 어떻게 작성하는지 따라 수많은 기능을 할 수 있다.

우선 Clone할 프로젝트가 Maven이라는 가정하고 Invoke top-level Maven targets을 선택 후 Tools에서 생성한 Maven의 이름을 선택해준다.

그리고 Goals에는 mvn clean packge를 작성하여 jar 또는 war파일을 만들 수 있도록 만들어줘야 한다.

이때 오류가 나는 경우 Repository를 봤을때 mvn이나 gradle이 있는지를 확인하고 만약 없다면 고급을 누른 후 POM에 해당 Repository의 pom.xml이 있는 경로를 작성해줘야 한다.

여기까지 하면 해당 Repository에 이벤트가 발생하면 자동으로 Build를 하게 되고 이를 CI라고 한다.

 

6. Build Step 추가 (CD)

Execute Shell을 추가 하여 Shell Script를 작성해준다.

#!/bin/bash
# 1 : jar파일이 있는 위치를 작성
jar_file="target/demo-0.0.1-SNAPSHOT.jar"

# 2 : 새롭게 빌드된 jar파일을 실행하기 위해 기존 pid 제거
pid=$(pgrep -f "demo-0.0.1-SNAPSHOT.jar")
if [ -n "$pid" ]; then
    kill -9 $pid
    echo "실행 중인 프로세스를 종료했습니다."    
else
    echo "실행 중인 프로세스가 없습니다."
fi

# 3 : jar파일을 백그라운드로 실행
if [ -f "$jar_file" ]; then
    JENKINS_NODE_COOKIE=dontKillMe nohup java -jar $jar_file &
    echo "JAR 파일을 실행했습니다."
else
    echo "JAR 파일이 존재하지 않습니다."
    exit 1
fi

Jenkins 이용시 알아둬야 할 내용 중 FreeStyleProject와 PipeLine 두가지 모두 빌드 종료 시 빌드 중 생성된 자식 프로세스는 제거(Kill)이 된다는 사실이다.

이 말은 빌드 중 jar파일을 실행했고 빌드가 종료되면 실행되었던 jar파일을 종료시킨다는 것이다.

이를 막기 위해서는 JENKINS_NODE_COOKIE=dontKillMe를 꼭 앞에 붙여줘야한다.

그러면 해당 PID는 제거 하지 않는다.

구글에서 검색하다보면 가끔 BUILD_ID=dontKillMe를 쓰는 글을 볼 수 있는데 이는 권장되지 않는 방법이다.
BUILD_ID는 Jenkins에서 빌드를 유일하게 식별하기 위한 값으로 사용되므로 임의로 변경하면 Jenkins에서 빌드를 식별하는 데 문제가 발생할 수 있지만 JENKINS_NODE_COOKIE는 Jenkins 노드(아이템)에서 실행 중인 프로세스를 강제로 종료하지 않도록 하는 환경 변수이기 때문에 JENKINS_NODE_COOKIE를 사용하는 것을 권장한다

 

PipeLine

1. Build Trigger를 GitHub hook trigger for GITScm polling로 지정해준다.

 

2. credentialsId를 만든다.

GitHub Profile의 Setting에서 Developer Settings에서 Personal access tokens를 만들어준다.

이름은 마음대로 지정한 후 아래와 같이 두가지만 체크한 후 저장하여 토큰 값을 저장해준다.

이제 다시 Jenkins 관리에서 Credentials 메뉴 클릭한다.

여기서 Add credentials를 한 후 아래와 같이 작성

 

3. Pipeline Script 작성

pipeline {
    agent any

    tools {
        # Tools에서 지정한 Maven 이름
        maven "Maven_TEST"
    }

    stages {
        stage('Checkout') {
            steps {
                // 어떤 브랜치를 기준으로 할지 지정
                git branch: 'main',
                // 2번에서 지정한 credentials id
                credentialsId: 'github_access_token',
                url: 'https://github.com/dukbong/-PracticalStudy.git'
            }
        }
        stage('Build') {
            steps {
                // 만약 Repository 메인에 mvn이 있다명 dir 구문 생략 가능
                // 현재 예시에는 demo 폴더 안에 mvn이 있기 때문에 dir을 사용
                dir('demo') {
                    sh "mvn clean package"
                }
            }

            post {
                success {
                    echo "프로젝트 클린 빌드 성공!"
                }
            }
        }
        stage('test') {
            steps {
                dir('demo') {
                    sh "mvn test"
                }
            }

            post {
                success {
                    echo "프로젝트 테스트 성공!"
                }
            }
        }
        stage("run") {
            steps {
                    // script{} 대신 ''' 사용 가능
                    sh '''
                    CURRENT_PID=$(ps -ef | grep java | grep demo | awk '{print $2}')
                    if [ -z ${CURRENT_PID} ] ; then
                        echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
                    else
                        echo "> 실행중인 어플리케이션 : $CURRENT_PID"
                        kill -9 $CURRENT_PID
                        sleep 3
                    fi
                    
                    echo "> 배포 작업 시작"
                    // JENKINS_NODE_COOKIE=dontkillMe
                    // Jenkins 노드에서 실행 중인 프로세스를 강제로 종료하지 않도록 하는 환경 변수
                    JENKINS_NODE_COOKIE=dontKillMe nohup java -jar  demo/target/*SNAPSHOT.jar &
                    '''
            }
            post {
                success {
                    echo "런 성공!"
                }
            }
        }
    }
}

이 외에도 Pipeline script from SCM을 선택해서 jenkinsfile을 생성하여 만들 수 도 있다.

Pipeline script from SCM을 이용할 경우 장점 중 하나는 아래와 같이 CheckOut을 줄일 수 있다는 점도 있다.

stage('Checkout') {
    steps {
        script{
            checkout scm
        }            
    }
}

이미 설정에서 Git Repository와 accessToken 등 정보를 설정하기 때문에 사용가능하다.

Comments