deploy spring boot / 스프링부트 배포
이번 포스팅에서는 빌드, 배포 자동화 툴인 Jenkins를 사용한 Spring Boot 프로젝트 jar 배포에 대해 다뤄보도록 하겠습니다.
*본 포스팅은 깃, 스프링에 대한 기본적인 이해가 없는 상태에서 참고하기에는 부적절할 수 있음을 알려드립니다.
또한 본 포스팅에서는 Docker를 사용하지 않습니다.
Spring Boot는 패키징을 jar와 war 방식 중 선택할 수 있습니다. war 파일은 tomcat 패키지가 필요하고 jar는 서버가 내장되어있는 패키지입니다. war 방식 배포를 한 번이라도 해보신 분들이라면 본 포스팅을 완주하시면 충분히 응용해 배포가 가능하리라 생각되므로 본 포스팅에서는 다루지 않도록 하겠습니다.
구축에 앞서, 본 예제에서 Jenkins를 사용하기 위한 구축 환경을 살펴보겠습니다.
서버의 운영체제는 우분투를 사용했습니다.

이번 예제에서는 깃 서버, 빌드 서버, 서비스 서버(배포) 각 3대의 가상 머신을 생성해서 진행합니다.
사실 위 각 3개의 역할을 1대의 PC에서 모두 구현해서 사용하는 것도 가능합니다.
하지만 이럴 경우 Jenkins를 사용하는 의미가 다소 떨어지므로 번거롭지만 3대의 PC를 이용해 역할 분배를 해 보겠습니다.
본 포스팅에서 진행하는 구축 방식은 깃 서버를 직접 만들어주고 AWS와 같은 배포 서버보다 설정을 더 해줘야 하는 등 지극히 원시적이고 비 효율적인 방식일 수 있습니다.
내용 중 그 과정을 크게 간소화한 부분도 있지만 각각의 기능들을 직접 컨트롤 해주어야 하기 때문에 파악할 수 있는 부분이 많으리라 생각되므로 한 번쯤 본 포스팅의 방식을 구현해보는 것도 나쁘지 않을 것 같습니다.
각설하고, 위 3대의 PC는 이후 아래와 같이 동작할 것입니다.

먼저, 각 개발 PC에서 작업한 프로젝트를 Git Server에 형상관리를 합니다.
다음, Jenkins를 설치한 서버에서 Git의 소스를 Pull 받아 배포 파일로 빌드합니다.
파일 빌드가 완료되었으면 Build Server가 Service 서버에 전달해 실제 배포를 진행하게 됩니다.
각 가상 머신의 스펙은 아래와 같습니다.
서버 역할 | RAM | HDD | OS |
Git Server | 4GB | 20GB | Ubuntu 20.04 Server |
Build Server | 4GB | 20GB | Ubuntu 20.04 Server |
Service Server | 4GB | 20GB | Ubuntu 20.04 Server |
이번 포스팅에서는 GUI는 필요하지 않기 때문에 모두 Ubuntu Server (CLI 환경)를 사용했습니다.
위 3대의 서버는 서로 간 통신이 되어야 하므로 아래의 포스팅을 참고해 가상 머신의 호스트 네트워크를 세팅합니다.
2020/09/22 - [Dev/linux] - [Linux] Ubuntu Linux - Virtual Box에서의 고정 IP 할당
[Linux] Ubuntu Linux - Virtual Box에서의 고정 IP 할당
Virtual Box에 설치한 Ubuntu와 외부 PC 간의 통신을 위한 고정 ip를 할당 방법에 대한 기록입니다. 이 과정을 거치면 독립적인 IP를 통해 호스트 PC 및 다른 가상 머신에서 네트워크를 통한 서로간의 접
dev-overload.tistory.com
1. Git Server, Build Server 초기 세팅
아래의 명령어로 깃 패키지를 설치합니다.
$ sudo apt-get install git openssh-server
2. Git Server - 저장소 생성
사용자 폴더 하위에 jenkins_spring이라는 이름의 폴더를 생성하고 깃 저장소를 초기화합니다.
cd
mkdir jenkins_spring
git init --bare jenkins_spring
이제 외부(호스트 PC)에서 이 저장소에 형상관리를 진행하면 됩니다.
opensssh-server를 설치했기 때문에 외부에서는 아래의 명령어로 깃 저장소를 clone 할 수 있습니다.
git clone ssh://[깃서버 계정명]@[깃 서버 ip]:/home/[깃서버 계정명]/jenkins_spring
자세한 설명은 포스팅이 너무 길어지므로 생략하겠습니다. 혹시 의문점이 있으시면 댓글 주세요.
3. Build Server - Jenkins 설치
Jenkins는 서블릿 컨테이너 위에서 동작하므로 JAVA를 설치해 주어야 합니다.
작성일 기준 Jenkins는 JAVA 8에서 구동하는 것을 권장하고 있습니다.
아래의 명령어로 JAVA와 Jenkins를 설치해줍니다.
# JAVA 설치
sudo apt-get update
sudo apt-get install openjdk-8-jdk-headless
# Jenkins 설치
1. Jenkins apt key 추가
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
2. Jenkins 패키지 저장소 리스트 추가
sh -c 'echo deb http://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
3. Jenkins 설치
sudo apt-get update
sudo apt-get install jenkins
JAVA 환경 변수 점검
더보기
Jenkins는 기본 포트가 8080인데, 만약 이 포트가 다른 서버에서 이미 사용 중이라면 Jenkins는 구동하지 않습니다.
따라서 기본 포트를 변경해보도록 하겠습니다.
아래의 명령어로 설정 파일을 수정합니다.
sudo vim /etc/default/jenkins

HTTP_PORT 부분을 9090으로 변경하고 jenkins를 재시작합니다.
sudo service jenkins restart
이제 호스트 PC에서 http://빌드서버IP:9090에 접속합니다.

cat /var/lib/jenkins/secrets/initialAdminPassword 명령어로 출력하는 문자열을 위 Administrator password에 넣고 진행합니다.

secrets 디렉터리는 사용자 계정으로는 접근할 수 없으므로 sudo su로 루트 계정에 로그인 후 접근합니다.

Install suggested plugins를 눌러 플러그인을 설치합니다.
설치가 완료되면 관리자 계정을 생성합니다.
모든 과정이 완료되었다면 아래와 같은 페이지를 출력해줍니다.

Jenkins
Jenkins에서 Service Server에 접근하려면 Publish Over SSH 패키지를 설치해야 합니다.

Jenkins 관리 버튼을 누르고 플러그인 관리 버튼을 누릅니다.

설치 가능 탭을 누르고 ssh를 검색, Publish Over SSH를 체크하고 지금 다운로드하고 재시작 후 설치 버튼을 눌러 설치를 진행합니다.
이후 더 설정해 주어야 하는 내용이 있지만 Git Server와 Service Server 세팅을 마치고 나머지를 마저 진행해 주겠습니다.
4. Git Server - Build Server SSH 키 등록
Jenkins에서 Git Server에 접근하기 위해서는 Git Server에 Jenkins의 SSH 퍼블릭 키 값이 등록되어 있어야 합니다.
Build 서버에서 아래의 과정으로 SSH 키를 생성해줍니다.
sudo su jenkins
cd
ssh-keygen -t rsa

~/.ssh 경로에 아래와 같이 id_rsa, id_rsa.pub 파일이 생성되었는지 확인합니다.

cat ~/.ssh/id_rsa.pub 명령어로 파일 내용을 출력합니다.

위 키값을(ssh-rsa ~ jenkins@vm) Git Server에 등록해 주어야 합니다.
Git Server에서 아래의 명령어로 파일을 하나 생성해줍니다.
# Git 서버에서 만들어야합니다.
cd
mkdir .ssh
vim authorized_keys
jenkins의 키값을 붙여 넣기 하고 파일을 닫습니다.

5. Service Server 세팅
아래의 명령어로 java 패키지를 설치합니다.
sudo apt-get install openjdk-8-jdk-headless
위 예시에서는 Spring boot 프로젝트가 java8로 작성되었다는 것을 전제하고 있습니다.
따라서 작성한 프로젝트가 java8이 아닐 경우 그에 맞게 설치하면 됩니다.
젠킨스에서 Service Server에 접근하려면 Git Server의 id_rsa.pub 키값을 Service Server의 authorized_keys에 등록해야 합니다.
# Service Server
mkdir ~/.ssh
echo "Git Server의 id_rsa.pub" > ~/.ssh/authorized_keys
"Git Server의 id_rsa.pub" 이 구문을 그대로 쓰시면 안됩니다. 사용자의 Git Server의 .ssh/id_rsa.pub 키값을 써야합니다.
여기서, .ssh 폴더와 authorized_keys파일의 접근 권한을 각각 700, 600으로 설정해주지 않으면 jenkins에서 인증이 기능하지 않습니다.
아래의 명령어로 접근 권한을 변경해줍니다.
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
6. Jenkins에 Service Server SSH 접근 설정

Jenkins 대시보드에서 Jenkins 관리 > 시스템 설정을 누릅니다.
스크롤을 가장 아래로 내려 Publish over SSH 항목을 찾습니다.
만약 항목이 없다면 주소창에서 jenkinsurl:9090/restart에 접속해 Jenkins를 재시작합니다.

SSH Servers에 추가 버튼을 눌러 Name, Hostname, Username, Remote Directory 그리고 Passpharse / Password를 입력합니다. name은 Jenkins에서 식별하기 위한 서버 이름이므로 아무 이름이나 지정해도 무방합니다.
대신 Hostname과 Username은 정확히 입력해야 합니다. Hostname은 Service Server의 IP주소를, Username은 서버의 유저 계정을 입력합니다.
Passpharse / Password은 계정의 비밀번호입니다.

Jenkins SSH 접속 테스트
하단의 Test Configuration을 클릭해서 접속 상태를 체크합니다. Success라는 문구가 보이면 정상적으로 연결 세팅이 완료된 것입니다.
7. Jenkins 프로젝트 추가

대시보드에서 새로운 Item 버튼을 눌러 신규 프로젝트를 추가합니다.

예시에서는 jenkins_spring이라는 이름의 프로젝트를 추가하겠습니다.
Freestyle project를 클릭하고 하단에 OK 버튼을 누릅니다.

소스코드 관리 항목에서 Git에 체크하고 Repository URL을 추가합니다.
1번 항목의 Git Server에서 생성한 jenkins_spring의 주소를 입력합니다.
아마 지금은 접속이 허용되지 않는다고 에러를 뱉을 겁니다.

Credentials의 Add 버튼 - Jenkins 항목을 눌러 접속 계정을 세팅합니다.
위 Jenkins는 인증키에 대한 그룹으로 이후 추가 삭제가 가능합니다.

Git server의 접속 정보를 입력합니다 Username은 Jenkins에서의 식별 이름입니다.

만약 이 단계에서 에러코드 128번을 반환한다면 본 포스팅의 4번을 진행하다 문제가 생겼을 가능성이 높습니다.
Git Server의 authorized_keys 값에 jenkins의 키값이 제대로 등록되었는지, .ssh 폴더와 하위 폴더의 접근 권한 설정이 올바른지 다시 한번 살펴주세요.

스크롤을 내려 Build 항목에서 프로젝트 빌드를 정의해야 합니다.
Add build step을 눌러 Invoke Gradle script를 선택합니다.

Use Gradle Wrapper를 선택합니다.
Invoke Gradle을 선택할 경우 Jenkins에 Global Toll Configuration에서 Gradle 패키지를 설치해야 사용할 수 있습니다.
Use Gradle Wrapper의 경우 Gradle을 설치할 필요가 없습니다.
Make gradlew executable을 체크해 권한 문제로 실행이 안 되는 상황을 방지합니다.
Tasks에 clean build를 입력하면 Build 정의는 한차례 완료된 것입니다.
다음으로 빌드가 완료된 파일을 어떻게 Service Server로 보내 배포하는지 정의해야 합니다.

Add build step에서 Send files or execute commands over SSH를 선택합니다.
만약 이 항목이 보이지 않는다면 2번 과정에서 Publish Over SSH 플러그인이 제대로 설치되지 않은 것입니다.

Name은 5번 항목에서 추가한 SSH Server의 이름이 리스트 형태로 나타납니다.
Transfers에서 실질적으로 빌드한 파일을 Service Server로 보내고 배포하는 행위를 정의하게 됩니다.
Build Server의 Jenkins 계정의 루트 경로는 /var/lib/jenkins입니다.
Jenkins에서 Gradle Build를 하게 될 경우 /var/lib/jenkins/build/libs 에 jar 파일이 위치하게 되므로
Source files를 build/libs/*jar로 정의합니다.
Remote directory는 파일을 전송받는 Service Server의 경로를 의미합니다.
5번 항목에서 SSH 접속 루트를 /home/overload_service로 정의했으므로 결과적으로 Remote directory는
/home/overload_service/spring_project/target이 됩니다.
Service Server에서 미리 spring_project/target 디렉터리를 생성해 줍니다.
또한 프로젝트 구동 시의 로그 파일을 저장하기 위한 logs 디렉터리도 함께 만들어줍니다.
# Service Server
mkdir -p ~/spring_project/target
mkdir -p ~/spring_project/logs
이제 Exec command를 작성해주어야 합니다.
이곳에는 빌드가 완료되어 jar파일이 Service Server에 전달되고 난 후 실행되는 스크립트를 작성하게 됩니다.
스크립트를 직접 작성해도 되고 .sh파일을 미리 준비했다 그것을 실행하는 형태로 해도 무방합니다.
본 예제에서는 스크립트 구문을 직접 입력하는 방식으로 진행하겠습니다.
아래의 스크립트를 Exec commands에 입력합니다.
echo "PID Check..."
CURRENT_PID=$(ps -ef | grep java | grep jenkins_test_spring* | awk '{print $2}')
echo "Running PID: {$CURRENT_PID}"
if "$CURRENT_PID" [ -z CURRENT_PID ] ; then
echo "Project is not running"
else
kill -9 $CURRENT_PID
sleep 10
fi
echo "Deploy Project...."
nohup java -jar /home/overload_service/spring_project/target/jenkins_test_spring-0.0.1-SNAPSHOT.jar >> /home/overload_service/spring_project/logs/jenkins_test_spring.log &
echo "Done"
기존에 실행되고 있는 프로젝트 프로세스를 죽이고 jar폴더를 실행하는 스크립트입니다.
저장하고 대시보드로 이동합니다.
8. 프로젝트 빌드 & 배포

생성한 프로젝트에서 Build Now를 클릭하면 빌드와 배포가 이루어집니다.

Console Output에서 BUILD SUCCESSFULL을 띄워주면 빌드는 완료된 것입니다.
이제 배포 서버에서 배포까지 완료되었는지 살펴보겠습니다.

8082번 포트를 할당받아 java 프로세스가 올라가 있습니다.
8082번 포트는 사전에 제가 임으로 설정한 포트번호입니다.
한번 위 포트로 접속해보겠습니다.

간단하게 생성한 프로젝트가 잘 올라가 있는 것을 확인할 수 있습니다.

로그 파일도 정상 적용된 것을 확인할 수 있습니다.
이것으로 Jenkins - Spring Boot 프로젝트 jar 배포 포스팅을 마치겠습니다.