본문 바로가기
TIL/Java | Spring Boot

[Spring boot] Jenkins를 이용한 spring boot 자동 배포 CI

by yeon_zoo 2022. 4. 29.

프로젝트를 진행하면 보통 백엔드 쪽에서 완성되지 않더라도 서버를 우선 배포해야 클라이언트 측에서 쉽게 테스트 해보면서 개발할 수 있다. 지금까지 spring boot 프로젝트는 로컬에서 빌드한 jar 파일을 직접 업로드하고 실행하는 방식으로 배포해왔다. 깃에 올린 코드를 클라우드 서버(ec2나 gcp vm)에서 풀 받아서 서버 내에서 빌드를 하는 방법도 있었지만, 무료로 제공하는 인스턴스의 성능은.. 프로젝트가 조금만 무거워져도 빌드하기를 무거워했다. GCP에서 직접 빌드하는 게 로컬에서 빌드하고 업로드하는 것보다 시간이 오래 걸리게 되자 귀찮더라도 이런 방식을 취하게 되었다. 

 

졸업 프로젝트에서도 그렇게 진행하자니 정말 많이 귀찮기도 했고, 언젠가 공부하려고 했던 CI라면 지금 하자 라는 마음에 jenkins를 사용해 보게 되었다. 설정해야 할 것도 많고 플러그인 설치까지 많은 부분을 직접 수행해줘야 하지만 깃헙에 올린 코드로 바로 빌드해주는 것을 보면 '왜 이제서야 이 방법을 선택했을까..' 하는 생각이 든다. 3일간.. 밤새고.. 서버 갈아엎으면서 노력한 보람이 있는 시간이다. 🥲

0. jenkins 설치 및 구동

먼저 jenkins를 ec2에 설치한다. 

wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | 
  sudo apt-key add -
sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/ > \
/etc/apt/sources.list.d/jenkins.list'
sudo apt update && sudo apt upgrade
sudo apt-get install jenkins

이후 Jenkins의 포트를 수정해준다. jenkins도 자바를 사용하기 때문에 기본 포트가 8080이다.

sudo vi /etc/default/jenkins

#아래 부분을 사용할 포트로 변경
HTTP_PORT=8080

jenkins를 시작시키고 잘 작동되고 있는지 확인해보자. 

sudo service jenkins restart

# 정상여부 확인
sudo systemctl status jenkins

이후 jenkins에서 알아서 지정해준 초기 비밀번호를 확인해야 한다. 

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

jenkins를 설치한 서버의 ip 주소에 포트 번호를 붙여서 접속해보면 jenkins 서버가 켜진 것을 확인할 수 있다. 초기 비밀번호를 입력하고 들어가면 이후에 재접속할 때 이런 화면을 만날 수 있다. 


1. Jenkins plugin 설치

많은 블로그 튜토리얼들을 따라가봤지만 초반에는 plugin을 설치하지 않아서 '왜 다르지..?' 하고 당황을 많이 했던 것 같다. 나는 초기에 install recommended plugins를 안 하고 넘어가서 더 그랬던 것 같다. recommended plugin들은 자주 쓰이는 것이니 설치해두면 좋을 것 같다. 

 

추가적인 plugin 설치는 Manage Jenkins > Manage Plugins에서 할 수 있다.

 

내가 설치한 Plugin들은 다음과 같다. (아마 스프링부트를 배포하려고 한다면 비슷한 정도의 plugin만 필요할 것 같다. 추가로 필요하면 얼마든지 설치하면 된다)

  • Github plugin (깃헙 웹훅을 이용하려면 필요)
  • Gradle plugin (Gradle 프로젝트라서 이용)
  • Publish over SSH (빌드된 파일을 jenkins 서버에서 배포 서버로 이동시켜주는 역할)
  • Amazon EC2 plugin

이렇게 하면 Manage Jenkins 에 이런 Manage Credentials 항목이 생겼을 것이다.

그렇다면 깃헙과 연동할 준비는 끝났다!


2. Github Webhook 설정

먼저 깃헙 내에서 내 아이디에 대한 토큰을 발급받는다. github 내 settings > Developer settings에서 Personal access tokens를 발급받는다. 이 때 repo_hook, repo 범위로 선택했다. 

그리고 personal access tokens를 jenkins에 credential로 등록한다.

Manage Jenkins > Security > Manage Credentials 에서 다음과 같이 등록하면 된다.

  • Domain : Global credentials (unrestricted) 선택
  • Kind : Secret text 선택
  • Secret : 위에서 생성한 토큰 입력
  • ID : github 아이디

이렇게 설정해두고 configuration 에서 아래 내용들을 적어 넣는다. 사실 채울 건 Name 부분하고 Credentials를 선택해주는 것 외엔 없다. 채운 후 Test Connection을 통해서 정상적으로 credential이 작동한다는 것을 확인해볼 수 있다. 

 

그리고 내가 배포하려는 코드가 담긴 레포지토리 > Settings > Webhooks 에 가서 아래 내용을 추가해준다. 


3. Jenkins 프로젝트 생성

jenkins 새로운 프로젝트를 만들어 준다

프로젝트 명은.. 알아보기 쉽게 작성해주면 되고 Freestyle project를 선택해주었다. 프로젝트 내에선

Source Code Management 안에서 깃헙 레포지토리 url을 추가해준다. 아래 브랜치 설정을 통해서도 특정 브랜치에 있는 파일로 빌드를 진행하게 할 수 있다. 이번 프로젝트에서는 배포할 파일을 release 브랜치로 관리하기로 해서, 이렇게 넣어줬다.

저장한 후 build now 버튼을 눌러 보면 정상적으로 빌드된 것을 확인할 수 있다. 

이후 나는 gradle로 build 하기 위해서 ./gradlew build 명령어를 추가해주었다. 여기에 다양한 명령어를 추가할 수도 있다. 우선은 나는 배포가 되는지부터 보고 싶으니 넘어갔다. 


4. jar 파일을 jenkins서버에서 배포 서버로 넘기기

먼저 플러그인 설치 (아직 안 했다면)

이후 key를 등록해야 한다.

 

먼저 다음 명령어로 ssh 접근 키를 생성한다. 옵션들을 많이 줬는데 이렇게 하지 않고 기본 rsa 키를 생성하면 해싱 알고리즘 때문인지 Invalid한 키라고 에러가 떴다. 

ssh-keygen -t rsa -b 4096 -m PEM

ssh 접근 키로 수많은 에러와 마주했을 때, 찾아본 결과 .ssh/ 폴더에는 700, .ssh/authorized_keys에는 600의 권한을 줘야 한다고 한다. 따라서 다음과 같이 작성해준다.

chmod 700 /root/.ssh/
chmod 600 /root/.ssh/authorized_keys

그리고 나서는 authorized_keys 에 공개키 (id_rsa.pub)을 추가해줘야 한다. 

cat id_rsa.pub #이렇게 공개키를 확인하고 복사한 다음
vi authorized_keys # 이 파일 내부에 붙여넣고 저장한다

다음으로는 key가 발급된 디렉토리가 터미널에 떴을텐데 그 위치로 이동해서 key 값을 확인해준다. 

cat id_rsa

결과로 출력된 키(id_rsa)는 Manage Jenkins > Configuration 에서 다음과 같이 추가해준다.

 

이후 jenkins configuration에서 test configuration 을 눌러 success가 뜨면 성공이다.


5. 빌드 후 코드 실행 자동화로 run 상태 만들기

드디어 마지막 과정이다. project 내의 post-build actions에서 이렇게 코드를 추가해주면 된다. 

Source files는 빌드된 파일이 있는 경로와 빌드된 파일

remove prefix는 빌드된 파일의 경로를 제거하고 싶을 때 쓰는 것이다. 

그리고 아래에 실행할 명령어를 적어주면 된다. 초반에 설정해뒀을 때는 java -jar로 실행하는 명령어만 추가했는데, 새로 빌드되고 나면 기존 포트를 죽이고 다시 파일을 실행시켜야 하기 때문에 pid 번호를 찾아 죽이는 코드도 추가해줬다. 

sudo kill -9 $(sudo lsof -t -i:8081)
nohup java -jar ....

 

 


 

그 외 에러 사항 1 - AWS EC2 프리티어.. 메모리 부족

jenkins를 이용하니 미쳐 날뛰는 CPU 이용률을 볼 수 있었다. 특히 초반 서버 세팅에서 나는 AWS ec2 프리티어를 사용하고 있었기 때문에 1GB ram으로는 젠킨스까지 돌린다는 것은 거의 불가능하다고 한다. (그리고 경험해보니 그랬다.) 

그래도 아주 불가능한 것은 아니다. swap 메모리를 ubuntu에서 강제로 할당해줄 수 있기 때문에 swap 메모리를 사용하면 CPU를 어느정도 비워줄 수 있다. (대신 swap 메모리 - 디스크 내에 위치 - 를 사용하다 보니 불가피하게 속도가 좀 느려진다) 보통 CPU가 1 GB이면 스왑 메모리는 약 2 GB 정도 두면 된다고 한다. 설정하는 법은 아주 쉽고 AWS 에서도 안내 하고 있다.

https://aws.amazon.com/ko/premiumsupport/knowledge-center/ec2-memory-swap-file/

 

스왑 파일을 사용하여 Amazon EC2 인스턴스의 스왑 공간으로 메모리 할당

1.    dd 명령을 사용하여 루트 파일 시스템에 스왑 파일을 생성합니다. 명령에서 bs는 블록 크기이고 count는 블록 수입니다. 스왑 파일의 크기는 dd 명령의 블록 크기 옵션에 블록 수 옵션을 곱

aws.amazon.com

 

그 외 에러 사항 2 - AWS EC2 프리티어.. 디스크 공간 부족

하지만 잘 나가다가도.. 또 다른 문제에 부딪혔다. 이번엔 디스크 공간이 부족하다는 것이다.

기본 제공하는 8 GB 짜리였는데 이것도 열심히 알아보다가 이 부분까지 해결하기는 어렵기도 하고, 그 와중에 설정을 잘못 건들여서 ec2 인스턴스의 ssh 접속 키를 잃는 바람에 그냥 GCP로 넘어왔다.

 

GCP가 무료로 제공하는 성능은 AWS 무료로 제공하는 것과 큰 차이 없지만, 그래도 GCP는 3개월간 크래딧을 사용해볼 수 있어서 좋다. GCP로 사용하다가 무료 크래딧 기간이 종료되면 그 다음에는 VM 성능을 좀 낮춰서 GCP에 유료로 쓸까 생각도 하고 있다. 이번 서버 배포로 AWS와 GCP의 차이점을 여실히 느낄 수 있었는데(GCP는 ssh 연결 창-직렬 콘솔-을 ubuntu까지 제공하는데 이게 엄청 편하다, 그리고 손쉽게 인스턴스를 일시 중지하고 머신구성을 바꿀 수도 있다. 이 기능은 AWS 에서도 가능한 것으로 아는데, UI 상 탐험하다가 본 적은 없다. 무엇보다 가장 크게 느끼는 점은 GCP의 한글 지원이 AWS 보다 훨씬 이해가 쉽고 명확하게 표현되어 있다...) 기회가 된다면 이것도 정리해 봐도 좋을 것 같다. 

댓글