Jenkins Pipeline 구축

구본식·2023년 1월 14일
1
post-thumbnail

앞서 공부한 CI/CD 개념과 Jenkins를 사용하여 CI/CD를 위한 Jenkins Pipeline을 단계적으로 구현해볼것이다. 처음이라 많이 부족한거 같다🤣

Jenkins Pipeline 설계

Jenkins Pipeline을 크게 아래와 같이 설계해보았다.
Prepare:git clone -> Build:Gradle -> Test:프로젝트에 작성해논 단위,통합 테스트 -> Deploy:AWS EC2 서버 배포

추가적으로 Github Webhooks를 이용한 빌드 유발로 Jenkins의 Pipeline시작점이 시작되도록 연결해보았다.


1. Jenkins, github 연결

1.1 Credentials 등록

Jenkins가 나 대신 공유 레포지토리에서 git clone을 하려면 해당 공유 레포지토리에 대한 접근 권한이 필요하다.
Jenkins Credential에 필요한 리소스 private key들을 등록한후 추후 사용하는 것이 안전하기 때문에 Credentials에 등록해보았다.

github 엑세스 토큰 발급은 검색을 하게 되면 상세히 쉽게 찾아볼수 있기 때문에 발급 부분은 넘어가도록 하겠다.
기본적으로 엑세스 토큰의 유효 기간을 무한대로 설정해놓았다. 또한 엑세스 토큰을 한번 발급하면 잊어버렸을 경우 찾을수 없기 때문에 따로 저장해두는것이 좋다.

이렇게 발급한 엑세스 토큰을 Jenkins Credentialskind:Username with password, ID: 추후 Jenkins에서 사용될 key이름으로 설정하여 아래와 같이 등록하였다.

1.2 Pipeline Script

또한 이렇게 등록한 엑세스 토큰생성한 Pipeline ProjectScript에 아래와 같이 작성하였다.

pipeline {
    agent any
    stages {
        stage('Prepare') {
            agent any
            steps {
                git branch: 'main', 
                credentialsId: 'github_access_token', 
                url: 'https://github.com/YNCB/backEnd.git'
            }
            post {
                failure{
                    error "Fail Cloned Repository"
                }
        }
    }
}

git clone을 받아올 uribranch을 적고 앞서 등록한 credentialsId를 적는다.
PostSections은 앞에 Steps가 종료되고 실행하게 되는데 만약 실패시 failure가 실행하게 되고 error옵션을 통해 뒤의 작업들을 실행시키지 않고 종료되도록 작성하였다.

또한 Script를 작성할때 Pipeline Syntax를 기능을 이용하면 자동으로 스크립트를 완성해주어 편하게 사용할수 있다.

➕ Jekins, github webhooks 사용

webhooks이란?
특정 이벤트(git push,commit등)가 발생하였을때 서비스나 응용프로그램으로 알림을 보내주는 기능이다.
웹훅을 사용하지 않으면 깃허브 공유 레포지토리에 업데이트가 발생하였는지 직접 주기적으로 확인을 하여야 된다. 웹훅을 사용하게 되면 깃허브의 변경사항을 Jenkins에게 알람을 보내주기 때문에 직접 레포지토리의 변경사항을 주기적으로 확인할 필요가 없어진다.

github webhooks를 사용하여 특정 이벤트를 감지하게 되면 Jenkins Pipeline의 시작점으로 연결시켜 시작하도록 추가하였다.

  • Payload URL
    • 레포지토리에 이벤트가 발생하였을 때 알림을 줄 URL이다.
    • 젠킨스 서버주소/github-webhook/을 추가하 작성한다.
    • 또한 localhost:13707와 같이 개인 젠킨스 로컬주소를 입력하게 정상동작을 하지 않기 때문에 ngrok 어플리케이션을 사용하여 외부에서 접근할수 있는 도메인을 사용하였다.
  • Content type - application/json

추가적으로 Jenkins Pipeline project에 아래와 같이 체크를 한다.
그렇게 되면 github에 이벤트가 발생하였을때 jenkins에 설정한 github 레포지토리와 일치하는지 확인한후 일치하다면 pipeline의 Script를 실행하게 된다.


2. Jenkins, Gradle build

2.1 설정

jenkins가 프로젝트를 build,Test하기 위해서는 프로젝트에 해당하는 빌드 도구들을 설정해두어야된다.
해당 프로젝트는 Springboot를 이용하였고 Springboot는 JDK기반이기 때문에 JDK설정과
빌드 도구인 GradleJenkins Global Tool Confituration에 등록해줘야된다.

Jenkins 자체에서 필요한 JDK를 다운받을수 있도록 설정할수도 있지만 이미 PC에 JDK가 있기 때문에 JDK 경로를 설정해두었다.


빌드 도구로 사용될 GradleGradle Plugin을 먼저 설치한후 설치한 Gradle의 설정에서 사용할 버전을 선택하였다.

2.2 Pipeline Script

pipeline {
    agent any
    stages {
        stage('Prepare') {
            agent any
            steps {
                git branch: 'main', 
                credentialsId: 'github_access_token', 
                url: 'https://github.com/YNCB/backEnd.git'
            }
            post {
                failure{
                    error "Fail Cloned Repository"
                }
            }
        }
        stage('Build') {
            agent any
            steps{
                bat '''
                    echo 'start bootJar'
                    ./gradlew clean bootJar
                    '''
            }
            post{
                failure{
                    error 'Fail Build'
                }
            }
        }
    }
}

빌드는 앞서 레포지토리에서 clone해오 소스코드에 포함되어있는 gradlew wrapper를 활용하였다.
bootJar명령어를 사용하여 소스코드의 .jar파일을 만들었다.

현재 Jenkins server를 로컬에서 실행시키고 로컬은 Microsoft이기 때문에 Script를 실행하기 위해 bat를 사용하였다.
다음시간에는 Docker를 공부한후 프로젝트에 적용할 예정이다. Docker를 이용하여 프로젝트,jenkins를 뛰울때는 sh명령어를 사용하면된다.(Linux서버이기 때문에)
얼른 Docker를 공부해야겠다.🤣

또한 Build 실패시 Post Sections을 통해 종료되도록 하였다.


3. Jenkins, Gradle Test

pipeline {
    agent any
    stages {
        stage('Prepare') {
            agent any
            steps {
                git branch: 'main', 
                credentialsId: 'github_access_token', 
                url: 'https://github.com/YNCB/backEnd.git'
            }
            post {
                failure{
                    error "Fail Cloned Repository"
                }
            }
        }
        
        stage('Build') {
            agent any
            steps{
                bat '''
                    echo 'start bootJar'
                    ./gradlew clean bootJar
                    '''
            }
            post{
                failure{
                    error 'Fail Build'
                }
            }
        }
        
        stage('Test') {
            agent any
            steps{
                withGradle{
                    bat './gradlew test'
                }
            }
            post{
                failure{
                    error 'Fail Test'
                }
            }
        }
    }
}

별 차이 없이 Gradle wapper를 이용하여 프로젝트에 작성해논 단위, 통합 테스트들이 실행되도록 스크립트 job으로 추가하였다.


4. Jenkins, AWS EC2서버 Deploy

4.1 Publish over ssh 플러그인 설정

build, test가 마친후 Jenkins에서 빌드한 jar파일을 배포 서버인 AWS EC2에 전달해야 된다.
Jenkins pluginpublish over ssh 플러그인을 사용하여 해보았다.

먼저 플러그인을 설치한후 ssh을 통해 파일을 보내기전 pem키의 정보를 등록해야된다.

key로 AWS EC2의 pem키를 넣어준다.

  • syntax에서 참조할 수 있는 이름
  • 빌드된 파일을 전송할 서버의 private ip
  • username(ubuntu로 만든 aws ec2에서 따로 설정을 바꾸지 않았다면 ubuntu가 default입니다.)
  • 파일이 도착할 베이스 디렉토리를 적어줍니다.

4.2 Pipeline Script

Pipeline SyntaxsshPublisher simple step 이용하여 Script를 만들어보았다.

pipeline {
    agent any
    stages {
        stage('Prepare') {
            agent any
            steps {
                git branch: 'main', 
                credentialsId: 'github_access_token', 
                url: 'https://github.com/YNCB/backEnd.git'
            }
            post {
                failure{
                    error "Fail Cloned Repository"
                }
            }
        }
        
        stage('Build') {
            agent any
            steps{
                bat '''
                    echo 'start bootJar'
                    ./gradlew clean bootJar
                    '''
            }
            post{
                failure{
                    error 'Fail Build'
                }
            }
        }
        
        stage('Test') {
            agent any
            steps{
                withGradle{
                    bat './gradlew test'
                }
            }
            post{
                failure{
                    error 'Fail Test'
                }
            }
        }
        
        stage('Deploy'){
            agent any
            steps{
                sshPublisher(
                    publishers: 
                    [
                        sshPublisherDesc(
                            configName: 'deploy-server', 
                            transfers: 
                                [
                                    sshTransfer(
                                        cleanRemote: false, 
                                        excludes: '', 
                                        execCommand: 'sh /home/ubuntu/start_server.sh', 
                                        execTimeout: 120000, 
                                        flatten: false, 
                                        makeEmptyDirs: false, 
                                        noDefaultExcludes: false, 
                                        patternSeparator: '[, ]+', 
                                        remoteDirectory: '/deploy3', 
                                        remoteDirectorySDF: false, 
                                        removePrefix: 'build/libs', 
                                        sourceFiles: 'build/libs/*.jar')], 
                                        usePromotionTimestamp: false, 
                                        useWorkspaceInPromotion: false, 
                                        verbose: true
                                        )
                                ]
                        )
            }
            post{
                failure{
                    error 'Fail Deploy'
                }
            }
        }
        
    }
}
  • gradle wrapper의 빌드 결과물이 build/libs/에 위치하므로 sourceFilesbuild/libs/*jar로 작성하였다.
  • 빌드된 파일을 서버에 어떤 디렉토리에 위치할것인지를 remoteDeirectory에 작성해 놓았다.
  • 전송을 마치고 나서 실행할 shell명령어execCommand에 작성해두었다.

    start_server.sh쉘은 미리 EC2 서버에 정의해둔 파일이다. 간단하게 살펴보겠다.

    현재 실행하고 있는 .jar 프로세스를 종료한후 배포를 마친 디렉토리에서 새로운 .jar파일을 백그라운드로 실행하는 스크립트이다. 추가로 log도 설정해둔 모습이다.


현재는 단순히 CI/CD를 위해서 Jenkins를 사용하여 pipeline에 맞게 구현해보았다. 다음시간에 Docker를 사용하여 가상컨테이너 환경을 만들어본후 무중단 배포까지 되도록 공부한후 구현해 볼 예정이다.
공부 많이 해야겠다.🤣

참고자료 : https://sihyung92.oopy.io/e5300d92-1a4e-40f4-b927-a93b2bbb17d2

profile
백엔드 개발자를 꿈꾸며 기록중💻

0개의 댓글