Jenkins를 이용한 CD

haaaalin·2023년 11월 23일
post-thumbnail

CI는 다른 프로젝트에서 한 두번 적용해봤지만, 배포는 머리가 안 좋으면 몸이 고생한다고 일일이 aws에 직접 접속해 명령어를 실행하고 있었다. 이번엔 몸이 고생하지 않도록 CD 파이프라인을 구축해보려고 한다.

왜 Jenkins?

가끔 아무 지식이 없어서 갈피를 못 잡을 때 우아한 테크코스에 참여해 개발하고 있는 프로젝트를 모아놓은 github 를 참고한다. 아무래도 좋은 교육을 받고 있으니깐 참고하기 좋다.👍

암튼 해당 github에서 여러 프로젝트를 보니, Jenkins를 사용하길래 Jenkins에 대해 찾아보았다.

Jenkins란

Jenkins의 역사 .. 🚇

'코스케 가와구치' 라는 자바 개발자가 개발 과정에서의 반복된 빌드에 질려, 코드를 커밋하기 전에 해당 코드의 동작 여부를 알 수 있게끔 하고 싶어 방법을 찾다가, 직접 이를 가능하게 하는 서버를 자바로 개발했다고 한다. 그게 바로 '허드슨'이고, 이는 다른 기업에게 확산되었다.

"젠킨스가 아니고 허드슨?" 이라 생각할 수 있지만, 오픈소스 커뮤니티 간의 분쟁으로 'Jenkins'라는 새로운 이름을 달고 나왔다.

Jenkins가 등장하기 전에는

여러 개발자가 보낸 커밋을 일일이 확인하며 오류가 있는 지 확인해야 했고, 이 작업은 시간도 오래 걸리고, 어려운 작업이었다. 따라서 코드 통합 과정과 배포 과정에서 시간이 많이 소모되기 일쑤였다.

Jenkins의 자동화

Jenkins는 이러한 문제들을 해결해 새로운 코드를 원활하게 개발할 수 있고 테스트 및 배포할 수 있는 CI도구다. Jenkins를 적용한 이후부터 각 commit은 CI 서버에서 지속적으로 모니터링되고, 코드 빌드 및 검증 과정을 거칠 수 있어, 전에 발생하던 리소스 낭비를 해결할 수 있다.

CI - Continuous Integration
지속적 통합이라고 불리는 CI는 개발자가 repository에서 소스 코드에 대한 변경 사항을 commit하고, 모든 변경 사항이 지속적으로 빌드되는 프로세스를 일컫는다.

그렇다면 왜 Jenkins인지 🎤

  • 일단 무료이며 오픈소스라는 것이 가장 강력한 장점이다.
  • 또한 모듈 형태라 대부분의 다른 툴과 함께 사용 가능하다.
  • 오픈소스다 보니 사용자 입맛에 맞게 설정할 수 있도록 하는 플러그인만 정말 1000개가 넘는다고 한다. 정말 많은 것을 편리하게 할 수 있다. 👍
  • 이번 프로젝트가 Java로 개발되었기 때문에, Java로 개발된 Jenkins와 플러그인을 사용하며 빌드 설정이 간편할 거라 예상했다.

Jenkins를 더 알아보자

Jenkins 흐름

  1. 개발자가 repository에 변경 사항을 commit한다.
  2. Jenkins CI 서버는 정기적으로 repository를 확인해 새로운 코드를 가져온다.
  3. 빌드 서버는 코드를 실행 파일로 빌드하고, 빌드에 실패한다면 개발자에게 알림을 전송한다.
  4. Jenkins는 빌드된 어플리케이션을 설정된 서버에 배포한다. 이또한 실패한다면 개발자에게 알림을 전송한다.
  5. 최종적으로 문제가 없다면 프로덕션 서버에 배포된다.

Jenkins 단일 서버

이러한 흐름을 단일 Jenkins 서버에서 수행한다면, 문제가 발생할 수 있다. 만약 Jenkins 서버에서 빌드 작업을 수행하고 있는데, 변경 사항이 또 생길 경우엔 이는 대기열에 추가되어 대기할 수 밖에 없다. 또한 만약에 내가 작성한 코드가 macOS가 아닌 windows에서도 잘 동작하는 지 알고 싶을 수 있다. 하지만 이미 실행되고 있는 Jenkins 서버는 리소스가 제한적이라, 다른 환경이 필요한 작업을 추가로 실행하는 건 좋지 않다.🥲

그렇다면 이떄 필요한 것은?

Jenkins의 Master-Slave 구조

Jenkins는 분산 빌드 작업을 수행하고 관리하기 위해 Master-Slave 구조를 사용한다.

Jenkins의 master

Jenkins의 master 서버는 작업하고 있는 github의 repository와 직접 접근하여, 앞서 언급했듯이 변경된 코드가 있는지 확인하고 변경사항이 발생했을 경우 각 slave 서버의 빌드 작동 방식을 결정하고 작업을 할당한다. 또한 각 slave 서버의 빌드 결과를 모아 개발자가 알 수 있도록 알림을 전송하는 모니터링 역할도 수행한다.

Jenkins의 slave

각각 다른 환경으로 구성된 slave 서버를 이용해 작성한 코드가 다른 환경에서도 잘 돌아가는 지, 동일한 테스트 케이스를 서로 다른 환경에서 동시에 실행할 수 있다.

Jenkins로 CI/CD를 구축해보자

1️⃣ EC2 Jenkins 설치

일단 Jenkins를 띄워놓을 서버가 필요한데, 현재 서버 어플리케이션을 구동 중인 EC2에다 설정을 해볼 예정이다.

Amazon Linux 2023 기반 EC2 기준
EC2에 접속해 아래 명령어를 차례대로 입력하면 Jenkins 설치가 완료된다. 자세한 건 아래 링크를 참고하면 된다.
https://www.jenkins.io/doc/tutorials/tutorial-for-installing-jenkins-on-AWS/#downloading-and-installing-jenkins

[ec2-user ~]$ sudo yum update –y
[ec2-user ~]$ sudo wget -O /etc/yum.repos.d/jenkins.repo \
    https://pkg.jenkins.io/redhat-stable/jenkins.repo
[ec2-user ~]$ sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
[ec2-user ~]$ sudo yum upgrade
[ec2-user ~]$ sudo dnf install java-17-amazon-corretto -y
[ec2-user ~]$ sudo yum install jenkins -y
[ec2-user ~]$ sudo systemctl enable jenkins
[ec2-user ~]$ sudo systemctl start jenkins

2️⃣ Jenkins 실행

1. 비밀번호 입력

위의 명령어를 모두 실행하고 나면, http://ec2주소:8080 으로 접속해 Jenkins가 실행되고 있는 지 확인해본다. 접속하면 바로 아래와 같은 화면이 뜬다.

영어로 뭐라 설명이 되어 있는데, 해석해보자면
Jenkins에서 제공하는 관리자 비밀번호가 있는데, log 또는 /var/lib/jenkins/secrets/initialAdminPassword 해당 경로에 들어가면 볼 수 있으니, 이 비밀번호를 입력하라고 한다.

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

시키는 대로 따라해 비밀번호를 얻었다.

2. Jenkins Plugin 설치

비밀번호를 입력하고 들어가면, 아래 화면이 뜨는데 Plugin 설치에 대해 묻고 있다.

  • Install suggested plugins: 디폴트로 설정된 Plugin 모두 설치(프로그램을 설치할 때 항상 보게 되는 기본 설정 느낌)
  • Select plugins to install: 필요한 것만 설치 -> Jenkins 고수만 선택 가능할 것 같다.

=> ✨ Install suggested plugins로 결정

3. Admin User 생성

4. Jenkins 설치 완료 !

Admin 계정까지 생성하면 Jenkins 설정이 모두 끝나 홈화면을 보여준다.

근데 이미 벌써 알림이 와있길래 확인을 해보니, Java 11 기반의 Jenkins는 2024년 9월 30일 이후로 지원을 하지 않으니 참고하라는 안내가 와있었다.😑 현재 Java 11과 Java 17을 둘 다 지원하고 있었지만, 11은 종료할 예정이라니,, 일단 당장 일어나는 일이 아니니 다음 과정을 실행해 보자.

⛔️ 포트 번호 문제..

지금 Jenkins를 설치하고 실행하는 EC2는 현재 백엔드 어플리케이션과 Nginx, Redis를 모두 실행하고 있는 인스턴스였다. (돈이 없는 학생이다 😂) 따라서 Sprint Boot 어플리케이션의 기본 포트가 8080이라 Jenkins와 겹치는 문제가 발생했다.

Jenkins의 기본 포트 번호를 변경하기 위해 config 파일을 찾아봤지만, port 번호가 나와있는 파일을 찾지 못했고, 구글링을 해봐도 방법을 찾을 수 없었다.(Amazon Linux 2023 기반 EC2의 레퍼런스는 너무도 적으니, 다른 OS를 추천한다 ..)

따라서 Spring Boot 어플리케이션의 기본 포트를 8081로 변경해 해결했다.

  • 8081: Spring boot 어플리케이션 실행 중
  • 8080: Jenkins 실행 중

📣 스프링 부트 기본 포트 변경 방법

application.yml 파일에 아래를 추가하면 끝난다.

server:  
  port: 8081

3️⃣ Item 생성하기

1. Item type 결정

홈화면의 왼쪽 상단 부분을 보면 새로운 item 이라는 버튼을 눌러 Item 생성을 시작해본다.

버튼을 누르면 아래처럼 이름과 item 타입을 고르도록 되어 있는데 뭐가 되게 많다 .. 하나씩 일단 알아보자.

Freestyle project

jenkins에서 기본 틀을 정해주고, 그 안을 채우는 형식이다.

  • 장점: jenkins가 기본 틀을 정해주니 진입장벽이 낮고 낮은 만큼 사용하는 사람이 많아 레퍼런스가 많다.
  • 단점: 커스터마이징이 제한적이고, 병렬처리를 지원하지 않는다.

Pipeline

Pipeline은 한 곳에서부터 작업을 시작해 쭉 일렬로 진행하거나, 여러 갈래로 뻗어져 나갔던 작업이 한 곳으로 모이며 마무리되는 것을 의미한다.

  • 장점: 커스터마이징의 폭이 넓고, 병렬처리를 지원한다. 또한 다수의 형상관리 repository와 연계해 사용할 수 있다.
  • 단점: 반대로 진입장벽이 높아, 레퍼런스 또한 많이 없다.

일단 Jenkins 사용이 처음이라 Freestyle Project를 선택해 Item을 생성하겠다. 그 외의 타입들은 당장 필요한 타입은 아니라고 판단해 추후에 추가로 학습할 예정이다.

타입을 결정하고 OK 를 누르면, Configuration을 설정할 수 있는 화면이 나온다.

2. Github 연동

  • Repository URL: 먼저 CD 파이프라인을 구축하고 싶은 repository의 url을 입력해준다. 이때 주의해야할 것은 git clone 할 때의 url이 아니라는 점이다.
  • Credentials: Github와 연동될 수 있도록 Gtihub 연동 정보를 넣어야 한다.

+Add 를 눌러 Github 계정 정보를 입력해 생성 후, 선택해준다.

Build할 브랜치를 설정해야 한다. 더 쉽게 브랜치를 설정할 수 있도록, Git Parameter 플러그인을 깔고 시작하자.

Git Parameter 플러그인을 설치하고 나면, 설정하던 아이템으로 돌아가, "이 빌드는 매개변수가 있습니다" 설정을 추가해야 한다.

그리고 아래처럼 매개변수를 넣어주면 끝이다.

3. 빌드 설정

우리는 Git Repository에 이벤트가 발생할 때 자동으로 배포되길 원하므로, Github의 hook을 사용해야 한다. 아래처럼 빌드 유발 옵션에 GitHub hook trigger for GITScm pollin을 선택해주고,

해당 Github Repository로 달려가 Webhook을 추가해준다.

  • Payload URL: Jenkins 접속 주소/github-webhook
  • Content type: application/json

Trigger 선택은 3가지가 있는데, push 이벤트만 필요하기 때문에 Just the push event로 결정 후, Web hook 설정을 완료한다.

3️⃣ Build Script 작성하기

1. gradle 설정

일단 build 설정 이전에 gradle 설정을 해야 한다.
Dashboard > Jenkins 관리 > Global Tool Configuration > Gradle installation 로 들어와 아래처럼 현재 개발하고 있는 프로젝트와 같은 gradle 버전을 추가한다.

2. build 설정

gradle로 하려다가, 현재 Jenkins 서버가 곧 운영 서버라 shell 명령어를 실행하는 게 더 익숙하기도 하고, 배포용 deploy.sh 파일을 만들어 놓은 상태라 이를 이용하기 위해 gradle이 아닌 shell을 실행하도록 했다.
하지만 아래의 글을 참고하면, 거의 모든 설정을 바꾼 것을 알 수 있다.

Jenkins를 이용한 SSH EC2 접속

이미 EC2 내에 deploy.sh 스크립트를 작성해놨기 떄문에, ec2에 접속 해당 shell script를 실행하도록 하는 작업 밖에 없지만, 나중에 추가되고 더욱 구조적으로 구성할 계획이다.

pipeline {
    agent any
    tools {
        gradle 'graphy'
    }
    stages {
        stage('Deploy') {
            steps {
                sshagent(credentials: ['aws_key']) {
                    sh '''
                        ssh -o StrictHostKeyChecking=no ec2-user@{ec2 private IP 주소} uptime
                        ssh -t ec2-user@{ec2 private IP 주소} app/deploy.sh
                    '''
                }
            }
        }
    }
}

참고


profile
한 걸음 한 걸음 쌓아가자😎

0개의 댓글