CI는 다른 프로젝트에서 한 두번 적용해봤지만, 배포는 머리가 안 좋으면 몸이 고생한다고 일일이 aws에 직접 접속해 명령어를 실행하고 있었다. 이번엔 몸이 고생하지 않도록 CD 파이프라인을 구축해보려고 한다.
가끔 아무 지식이 없어서 갈피를 못 잡을 때 우아한 테크코스에 참여해 개발하고 있는 프로젝트를 모아놓은 github 를 참고한다. 아무래도 좋은 교육을 받고 있으니깐 참고하기 좋다.👍
암튼 해당 github에서 여러 프로젝트를 보니, Jenkins를 사용하길래 Jenkins에 대해 찾아보았다.
'코스케 가와구치' 라는 자바 개발자가 개발 과정에서의 반복된 빌드에 질려, 코드를 커밋하기 전에 해당 코드의 동작 여부를 알 수 있게끔 하고 싶어 방법을 찾다가, 직접 이를 가능하게 하는 서버를 자바로 개발했다고 한다. 그게 바로 '허드슨'이고, 이는 다른 기업에게 확산되었다.
"젠킨스가 아니고 허드슨?" 이라 생각할 수 있지만, 오픈소스 커뮤니티 간의 분쟁으로 'Jenkins'라는 새로운 이름을 달고 나왔다.
여러 개발자가 보낸 커밋을 일일이 확인하며 오류가 있는 지 확인해야 했고, 이 작업은 시간도 오래 걸리고, 어려운 작업이었다. 따라서 코드 통합 과정과 배포 과정에서 시간이 많이 소모되기 일쑤였다.
Jenkins는 이러한 문제들을 해결해 새로운 코드를 원활하게 개발할 수 있고 테스트 및 배포할 수 있는 CI도구다. Jenkins를 적용한 이후부터 각 commit은 CI 서버에서 지속적으로 모니터링되고, 코드 빌드 및 검증 과정을 거칠 수 있어, 전에 발생하던 리소스 낭비를 해결할 수 있다.
CI - Continuous Integration
지속적 통합이라고 불리는 CI는 개발자가 repository에서 소스 코드에 대한 변경 사항을 commit하고, 모든 변경 사항이 지속적으로 빌드되는 프로세스를 일컫는다.
이러한 흐름을 단일 Jenkins 서버에서 수행한다면, 문제가 발생할 수 있다. 만약 Jenkins 서버에서 빌드 작업을 수행하고 있는데, 변경 사항이 또 생길 경우엔 이는 대기열에 추가되어 대기할 수 밖에 없다. 또한 만약에 내가 작성한 코드가 macOS가 아닌 windows에서도 잘 동작하는 지 알고 싶을 수 있다. 하지만 이미 실행되고 있는 Jenkins 서버는 리소스가 제한적이라, 다른 환경이 필요한 작업을 추가로 실행하는 건 좋지 않다.🥲
그렇다면 이떄 필요한 것은?
Jenkins는 분산 빌드 작업을 수행하고 관리하기 위해 Master-Slave 구조를 사용한다.

Jenkins의 master 서버는 작업하고 있는 github의 repository와 직접 접근하여, 앞서 언급했듯이 변경된 코드가 있는지 확인하고 변경사항이 발생했을 경우 각 slave 서버의 빌드 작동 방식을 결정하고 작업을 할당한다. 또한 각 slave 서버의 빌드 결과를 모아 개발자가 알 수 있도록 알림을 전송하는 모니터링 역할도 수행한다.
각각 다른 환경으로 구성된 slave 서버를 이용해 작성한 코드가 다른 환경에서도 잘 돌아가는 지, 동일한 테스트 케이스를 서로 다른 환경에서 동시에 실행할 수 있다.
일단 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
위의 명령어를 모두 실행하고 나면, http://ec2주소:8080 으로 접속해 Jenkins가 실행되고 있는 지 확인해본다. 접속하면 바로 아래와 같은 화면이 뜬다.

영어로 뭐라 설명이 되어 있는데, 해석해보자면
Jenkins에서 제공하는 관리자 비밀번호가 있는데, log 또는 /var/lib/jenkins/secrets/initialAdminPassword 해당 경로에 들어가면 볼 수 있으니, 이 비밀번호를 입력하라고 한다.
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
시키는 대로 따라해 비밀번호를 얻었다.
비밀번호를 입력하고 들어가면, 아래 화면이 뜨는데 Plugin 설치에 대해 묻고 있다.
=> ✨ Install suggested plugins로 결정


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로 변경해 해결했다.
application.yml 파일에 아래를 추가하면 끝난다.
server:
port: 8081
홈화면의 왼쪽 상단 부분을 보면 새로운 item 이라는 버튼을 눌러 Item 생성을 시작해본다.

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

jenkins에서 기본 틀을 정해주고, 그 안을 채우는 형식이다.
Pipeline은 한 곳에서부터 작업을 시작해 쭉 일렬로 진행하거나, 여러 갈래로 뻗어져 나갔던 작업이 한 곳으로 모이며 마무리되는 것을 의미한다.
일단 Jenkins 사용이 처음이라 Freestyle Project를 선택해 Item을 생성하겠다. 그 외의 타입들은 당장 필요한 타입은 아니라고 판단해 추후에 추가로 학습할 예정이다.
타입을 결정하고 OK 를 누르면, Configuration을 설정할 수 있는 화면이 나온다.


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

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

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

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

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

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

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

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