[Jenkins] CI/CD로 할 일 줄이기

양예성·2024년 4월 21일
5

스기~

목록 보기
2/4
post-thumbnail

간단한 CI/CD 설명

CI/CD란 각각의 개발자들이 개발을 하는 개발환경을 사용자가 사용 가능한 서비스로 전달하는 모든 과정을 지속 가능한 형태로 또 가능하다면 자동으로 해서 개발자와 사용자 사이의 격차를 없애는 것이다. 이러한 과정에는 코드를 빌드하고, 테스트하고 배포하는 활동이 있다.

이런 CI/CD 툴에는 여러 종류가 있지만 내가 접해본 CI/CD 툴은 Github Action 만 사용해보았고 이번에 신규 프로젝트를 들어가며 Jenkins를 사용해보고자 한다.

젠킨스를 사용하는 이유는?

코드의 빌드를 자동화 해주는 툴로서. 대표적인 기능으로 다음과 같습니다.

  1. 대시보드 제공하며 여러가지 배포 작업의 상황을 모니터링할 수 있다.
  2. 배포 스크립트 실행가능하며 배포 스크립트를 개발자 로컬에서도 실행할 수 있는데 젠킨스 프로그램을 띄워놓으면 스케줄링을 해준다.
  3. 다양한 플러그인이 있는데 이는 빌드를 하는 환경이 다양하고 언어도 다 다릅니다. 이것을 커버하기 위해 다양한 플러그인을 제공한다.

그리고 이번 프로젝트에선 서버뿐만 아니라 프론트도 CI/CD를 담당할 예정이라 젠킨스로 선택하게 되었다.

스왑 메모리 설정

먼저 우리 프로젝트는 총 4대의 서버를 운영하고 있다.

Oracle cloud 4코어 24기가 메인 서버
EC2 t3.micro 2대 (RabbitMQ, Jenkins)
Lightsail 2코어 2기가 1대 (Frontend)

로 사용예정이다.

EC2 t3.micro(프리티어) 에서 젠킨스를 사용할 예정이기 때문에 스왑 메모리 설정을 해줘야 한다 (안하면 서버가 죽는다..)

설명

코드

순서이다

dd 명령어를 통해 swap 메모리를 해야한다.

(128씩 16개의 공간을 만드는 것이여서 우리의 경우 count를 16으로 할당하는 것이 좋을것 같아 2GB정도로 세팅하였다)

sudo dd if=/dev/zero of=/swapfile bs=128M count=16

스왑 파일에 대한 읽기 및 쓰기 권한을 업데이트

sudo chmod 600 /swapfile

Linux 스왑 영역을 설정

sudo mkswap /swapfile

스왑 공간에 스왑 파일을 추가하여 스왑 파일을 즉시 사용할 수 있도록 수정

sudo swapon /swapfile

절차가 성공했는지 확인

sudo swapon -s

/etc/fstab 파일을 편집하여 부팅 시 스왑 파일을 활성화합니다.

편집기에서 파일을 열고

sudo nano /etc/fstab

파일 끝에 다음 줄을 새로 추가하고 파일을 저장한 다음 종료한 뒤

/swapfile swap swap defaults 0 0

다음과 같이 적용됬는지 확인을 하면

free

다음과 같이 나온다면 성공한거다

젠킨스 설치

1. jdk 설치

다음 명령을 터미널에서 실행한다.

sudo apt update
sudo apt upgrade
sudo apt install openjdk-xx-jdk
xx에는 사용할 jdk 버전을 입력한다.
프로젝트 jdk 버전이 21을 사용중이라 맞출겸 21을 사용하였다

2. 젠킨스 설치

다음 명령을 터미널에서 실행한다.

sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/> /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
systemctl status jenkins 을 입력하면 설치 여부를 확인할 수 있다.

3. 젠킨스 접속 포트 설정

sudo vi /etc/default/jenkins 을 사용하면 포트 등의 젠킨스 설정을 변경할 수 있다. (근데 난 그냥 기본포트를 사용하였다)

이후 sudo service jenkins restart 를 입력해 젠킨스를 재실행한다.
그리고 EC2 인스턴스에서 젠킨스가 실행된 포트의 인바운드 규칙을 추가해준다.

public ip: 설정한 포트(기본 8080)을 브라우저에 입력하면 젠킨스에 접속할 수 있다.

4. 초기 비밀번호 위치

아래 명령어를 사용하면 비밀번호가 나온다 그걸 치고 들어가면 된다

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

그럼 본인 계정 회원가입창이 뜨고
앞으로 사용할 계정을 생성해주면 된다

도커 설치

sudo apt update
sudo apt install docker.io

세팅끝🔥

젠킨스 사용하기

젠킨스를 접속하면 아래와 같은 화면일 것이다.

저기 Item은 내가 미리 만들어둔거라 뜨는거고 원래는 아무것도 없어야 정상이다

Jenkins 관리를 누른뒤

Credentials 에서 환경변수를 설정 할 수 있다.
(예시 깃허브 or yml 파일등)

클릭해서 들어가준다

들어가면 원래는 아무것도 없을껀데 미리 만든것 때문에 난 존재하는거다.
걍무시하삼

global 을 클릭하여 깃허브 계정 환경변수를 만들어보자 (필수)

Add credentials

Username with password

순서대로 설명하자면

Scope : 이 환경변수를 사용할 범위를 지정한다
Username : 유저 이름을 넣으면 된다.
(지금은 깃허브 세팅중이니 깃허브유저 이름을 넣으면 된다.)
Password : github access token 을 넣으면 된다.
(참고로 github access token 권한에 비공개 레포 권한이 있어야한다.)
ID : 이 환경변수를 부를때 사용할 ID 지정한다.
(github라고 적어두자)
Description : 이 환경변수에 대한 설명이다.
(알아서 잘 적길 바란다)

다 세팅했으면 create 누르자.

같은 방법으로 docker 도 세팅해준다

Username : docker hub 이름
Password : docker hub access token
ID : docker
Description : 설명

완료했음 도커와 깃허브 세팅은 완료된거다

yml 파일을 환경변수로 설정하여 숨겨보자(선택)

깃허브 private Repo 에 yml or properties 파일이 올라가있다면 안해도 된다.

똑같이 credentials 에 글로벌에서 Add credentials을 누른다

Kind에서 Secret file 선택하면 아래와 같이 나올것이다.

File 은 yml 파일 혹은 properties 파일을 선택하면 되고

ID는 이 환경변수 파일을 부를 명칭을 적으면 되고
Description은 그냥 설명 적음 된다.

난 ID를 application 으로 하였다.

SSH 연결 세팅하기

젠킨스 서버 말고 난 메인서버가 따로 있기때문에 그 서버 접속 세팅을 해줘야한다.

먼저 젠킨스 Dashboard -> jenkins 관리 -> Plugins 에 가서

Available plugins를 눌러 아래 처럼 Publish Over SSH 를 다운받는다.

다운받은뒤 Dashboard -> jenkins 관리 System Configuration 에서 System 클릭한다.

내리다보면 Publish over SSH가 있다

.pem 파일 혹은 SSH파일을 cat 명령어로 확인하여 Key 부분에 입력해주고

키 암호화 해서 넣는법은 잘 모르겠다.. 혹시 아시는분 있으면 댓글로 알려주세요

이부분은 각자 설정에 맞게 입력해주자.

Name : 서버 이름 (나중에 configName으로 사용됨)
Hostname : 서버 ip 주소
Username : 서버 접속 유저 이름

정도만 설정하면 된다.

Item 생성하기

새로운 아이템 생성을 누른 다음

Item 이름 설정과 파이프라인 선택하고 OK를 클릭한다.

설명은 대충 알아볼 수 있도록 적어주고

GitHub project 를 클릭해서 CI/CD를 적용할 레포 링크를 가져오자

그 다음 GitHub hook trigger GITScm polling 을 클릭하자
(이건 깃허브 후크를 통하여 이벤트가 발생 [푸시] 했을때 빌드를 하겠단 소리다)

클릭한 뒤 깃허브로 돌아가서 젠킨스와 연동할 프로젝트를 켜준다.
그 뒤 Settings 클릭 -> Webhooks 클릭

Add webhook 클릭

아래와 같은 화면이 나오면

참고로 update webhook 이 아닌 addWebhook 만 나올것이다. 절대로 다시 만들기 귀찮아서 기존꺼 킨거 아님!!

Payload URL : 당신의 젠킨스서버 URL : 포트 / github-webhook / 를 적어주면 된다.

Ex : http://127.0.0.0:8080/github-webhook/

뒤에 / 꼭 넣어주자

나머진 사진과 같이 설정하면 된다.
(Just the push event 꼭 하자)
안하면 모든 이벤트마다 빌드가 되는 머리아픈일이 발생한다...

다시 젠킨스로 돌아가서 파이프라인 스크립트만 작성하면 거의 다한거다.

여기에 아래 코드를 붙혀넣어주자

pipeline {
    agent any
    
    stages {
        stage('Clone Repository') {
            steps {
                git branch: '자신의 브랜치', url: "젠킨스 빌드할 레포나 플젝 깃허브 주소"
            }
        }
        
        //이건 아까 yml 파일 설정한 사람만 사용하자 
        stage('Add Env') {
            steps {
                script {
                    sh 'ls -R admin'
                    sh 'chmod -R 777 src/main/resources/'
                    withCredentials([file(credentialsId: 'application', variable: 'application')]) {
                        sh 'cp $application src/main/resources/application.yml'
                    }
                }
            }
        }

        stage('Build Project') {
            steps {
                sh './gradlew clean build -x test'
            }
        }

        stage('Docker Login') {
            steps {
                withCredentials([usernamePassword(credentialsId: 'docker', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
                    sh "echo \$DOCKER_PASSWORD | docker login -u \$DOCKER_USERNAME --password-stdin"
                }
            }
        }

        stage('Build Docker Image') {
            steps {
            	// 아래 빌드 플랫폼은 각자 알맞는 플랫폼으로 설정하자
                sh 'docker build --platform linux/arm64/v8 -t 도커 유저이름/이미지이름 .'
            }
        }

        stage('Push Docker Image') {
            steps {
                sh 'docker push 도커 유저이름/이미지이름'
            }
        }

        stage('SSH Connection') {
            steps {
                script {
                    sshPublisher(
                        continueOnError: true,
                        failOnError: true,
                        publishers: [
                            sshPublisherDesc(
                                configName: 'server', 
                                verbose: true,
                                transfers: [
                                    sshTransfer(
                                        execCommand: '''
                                            if docker ps -a | grep -q server; then
                                                docker stop 이미지 이름
                                                docker rm 이미지 이름
                                            fi
                                            docker pull 도커 유저이름/이미지이름
                                            docker run -d --name 이미지이름 -p 80:80 도커 유저이름/이미지이름
                                        ''',
                                        execTimeout: 120000 // Adjust as needed
                                    )
                                ]
                            )
                        ]
                    )
                }
            }
        }
    }
    
    post {
        always {
            sh 'docker image prune -f'
            script {
                sshPublisher(
                    continueOnError: true,
                    failOnError: true,
                    publishers: [
                        sshPublisherDesc(
                            configName: 'ssh publish over 에서 설정한 이름 기입',
                            verbose: true,
                            transfers: [
                                sshTransfer(
                                    execCommand: 'docker image prune -f',
                                    execTimeout: 60000 // Adjust timeout as needed
                                )
                            ]
                        )
                    ]
                )
            }
        }
    }
    
}

깃허브 액션을 사용해보았음 비슷하다 느낄수도 있다.

각 stage 별로 작업을 분리해 뒀고
순서대로 보자면

Clone Repository : 레포를 클론해온다.
Add Env : yml or properties 파일을 resources 아래 넣는다.
(아까 설정 안한사람은 빼도 상관없음)
Build Project : 클론해온 파일을 바탕으로 .jar 파일을 빌드한다.
Docker Login : 도커에 로그인 한다.
Build Docker Image : 도커파일을 빌드한다.
Push Docker Image : 도커허브에 도커파일을 푸시한다.
SSH Connection : 도커파일을 돌릴 원격 서버에 접속한다.
POST : 부분이 도커를 실행할 서버에서 도커 파일을 받고 돌리는 과정이 들어있다.

저렇게 설정을 완료하였음 저장을 누르고 빠져나오자.

근데 아무 작동이 안되는데요??

지금 빌드를 누르면 빌드가 시작될 것이다.

아래와 같이 빨간게 하나도 없다면 성공한 것이다.

다음과 같이 실패한 경우 마우스를 호버하면 로그를 볼 수 있다.
로그를 바탕으로 오류를 고치면 정상 작동 할 것이다.

이로서 나도 jenkins를 활용한 CI/CD 유경험자가 되었다!!!

(보너스) 디코로 빌드 성공/실패 여부 알림받기

젠킨스 디코 플러그인으로 빌드 성공/실패에 대하여 알람을 받을 수 있다.

Dashboard -> jenkins 관리 -> plugins 에서

Discord Notifier 를 다운받는다.

다음 디코로 넘어가 웹훅 URL을 발급받는다.
디코 접속 -> 서버설정 -> 연동 -> 새 웹후크 -> 메시지 보낼 위치 설정 및 URL 복사

다시 젠킨스로 돌아와 아이템 -> 구성 -> 파이프 라인 스크립트에서 아래 코드를 추가하자

아래 코드는 Post {} 범위 안에 넣어야한다.

success {
                discordSend description: "빌드 성공시 나올 상단 제목", 
                  footer: "빌드 성공시 나올 내용", 
                  link: env.BUILD_URL, result: currentBuild.currentResult, 
                  title: "빌드 성공시 제목", 
                  webhookURL: "웹후크 URL"
            }
        failure {
                discordSend description: "빌드 실패시 나올 상단 제목 ", 
                  footer: "빌드 실페시 나올 내용", 
                  link: env.BUILD_URL, result: currentBuild.currentResult, 
                  title: "빌드 실패시 제목", 
                  webhookURL: "웹후크 URL"
        }

설정을 다했음 빌드를 해보자

잘 작동한다면 디코로 알람이 갈 것이다.

이로서 당신도 Jenkins 를 활용한 CI/CD 유경험자로 거듭났다!

여담

작동이 안 되거나 문제가 있다면 댓글 달아줘요.. 만들면서 정리한 게 아니라 프로젝트 일정 쳐내랴 학교 생활하랴 바빠서.. 시간 날 때마다 만든 거라 틀린 정보가 있을 수도 있어요

깃헙 액션이 세팅 부분이나 러닝커브 부분에선 압도적이지만
젠킨스를 잘 활용한다면 깃헙 액션보다 훨씬 좋을 것 같다 생각합니다.

일주일전에 젠킨스 세팅을 마쳤는데 벌써 66번이나 작동하였는데
과연 프로젝트 끝날 때까지 얼마나 작동할지 궁금하네요

2개의 댓글

comment-user-thumbnail
2024년 4월 25일

잘 봤습니day

1개의 답글