본문 바로가기

Dev/etc

[Jenkins] Docker를 이용한 Jenkins - Spring Boot 프로젝트 배포

 

 

 

이번 시간에는 Docker 환경에서의 Jenkins 빌드 자동화 구축에 대해 알아보겠습니다.

Docker는 OS, 프로그램 등과 같은 소프트웨어를 컨테이너라는 단위로 묶어 별도의 격리된 프로세스를 할당받아 실행시키는 가상 머신 비슷한 도구입니다.

(비슷한 도구라 표현 한것은 가상 머신과는 기저 단위에서 그 개념이 다르기 때문입니다.)

 

이는 PC에서 게임을 실행시키기 위해서, 게임 CD를 구해 CD-ROM에 CD를 넣어 실행시키는 것과 유사한 사용 과정입니다.

 

호스트 PC가 Ubuntu20.04인 상태에서 Ubuntu 14.04를 사용해야하는 상황을 가정했을 때, Docker를 사용한다면 Docker Hub에서 Ubuntu 14.04 이미지를 받아와 (게임 CD를 구함) 컨테이너에 적재(CD-ROM에 CD를 넣음) 프로세스를 할당(실행) 시킨다면 운영체제를 굳이 바꾸지 않고 Ubuntu 14.04와 동일한 실행 환경을 만들 수 있는 것입니다.

 

이번 포스팅의 과정은 이전 포스팅에서 다루었던 Jenkins 구축 환경과 크게 다르지 않습니다.

중간에 Docker가 연결다리가 되어 준 것이라 생각 하시면 되겠습니다. 이전 포스팅이 궁금하시다면 아래 링크를 참고해 주세요.

2021/01/26 - [Dev/etc] - [Jenkins] Jenkins - Spring Boot 프로젝트 jar 배포

 

[Jenkins] Jenkins - Spring Boot 프로젝트 jar 배포

이번 포스팅에서는 빌드, 배포 자동화 툴인 Jenkins를 사용한 Spring Boot 프로젝트 jar 배포에 대해 다뤄보도록 하겠습니다. *본 포스팅은 깃, 스프링에 대한 기본적인 이해가 없는 상태에서 참고하기

dev-overload.tistory.com

 

 

 

Jenkins 작동 개념도

 

 

이전 포스팅에서는 Git Server를 직접 가상머신을 통해 생성해 주었지만, 이번 포스팅에서는 Git Hub의 은총을 받아보도록 하겠습니다.

 

Build Server는 Docker를 통해 구현하고, Service Server는 처음에는 Heroku로 할지 AWS로 할지 고민이 많았는데, 이 역시 Docker를 통해 가상 환경을 생성하는 것으로 처리하겠습니다.

 

*본 포스팅은 Ubuntu 20.04 환경에서 진행하였습니다.

 

1. Docker 설치

터미널에서 아래의 명령어를 따라 Docker에 필요한 패키지 설치를 준비합니다.

 

$ sudo apt-get update

// 의존성 패키지 설치
$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

// Docker 패키지 인증 키 추가
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

// Docker 저장소 추가
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

// 저장소 업데이트
$ sudo apt-get update

 

위 과정 이 완료 되었다면 sudo apt-cache search docker-ce 명령어로 Docker 패키지가 잡히는지 확인합니다.

 

 

Docker-ce Packages

 

 

사진과 같이 출력한다면 정상 추가된 것으로, 이제  Docker를 설치합니다.

 

// Docker 설치
$ sudo apt-get install docker-ce docker-ce-cli containerd.io

// 사용자를 docker 그룹에 포함
$ sudo usermod -aG docker $USER

 

명령어에서 사용자를 Docker 그룹에 포함시키는 이유는, Docker 명령어를 sudo 권한 없이 실행하기 위함입니다.

 

이로서 Docker를 사용하기 위한 기본적인 절차는 완료되었습니다.

 

 

2. Jenkins 컨테이너 준비

아래의 명령어로 Docker Hub에서 Jenkins 이미지를 확보합니다.

// jenkins 이미지 확보
$ docker pull jenkins/jenkins:lts

 

여기서 docker images 명령어를 입력해서 이미지를 잘 받아왔는지 확인합니다.

 

Jenkins Docker Image

 

 

아래 명령어로 이미지를 컨테이너에 적재합니다.

 

$ docker run --name jenkins-docker -d -p 8080:8080 -p 50000:50000 -v /home/jenkins:/var/jenkins_home -u root jenkins/jenkins:lts

 

 

여기서 -d 명령어는 백그라운드 실행, -p 명령어는 컨테이너와 호스트 PC 간 연결을 위해 내부 포트와 외부 포트를 묶은 것입니다.

 

-v 명령어는 이미지의 /var/jenkins_home 디렉터리를 호스트 PC 내에 마운트 하는 명령어입니다.

이것을 하는 이유는, Jenkins 설치 시 ssh 키값 생성, 저장소 참조 등을 용이하게 하기 위함입니다.

 

 

 

위 명령어 수행 시 /home 디렉터리에 jenkins라는 폴더가 생긴 것을 확인할 수 있습니다.

 

netstat으로 포트 할당 정보도 확인합니다. (netstat 명령어가 없는 경우 sudo apt-get install net-tools 설치)

 

 

Jenkins 포트 적용

 

 

포트 할당도 정상 적용된 것을 확인할 수 있습니다.

이제 localhost:8080으로 접속하면 아래와 같은 설치 화면을 보게 됩니다.

 

 

 

 

/var/lib/jenkins/secrets/initialAdminPassword 경로에 있는 패스워드를 입력해야 설치를 진행할 수 있습니다.

이 패스워드는 호스트에 마운트 한 /home/jenkins안에서도 확인할 수 있고 아래와 같이 log 명령어로 확인할 수도 있습니다.

 

$ docker logs jenkins-docker

 

 

 

 

위 숫자 영문자로 조합된 키 값이 Administrator Password입니다. 이 값을 복사해서 붙여 넣어줍니다.

 

 

 

 

권장 설정인 Install suggested plugins를 눌러 설치를 진행합니다.

중간에 필요한 설정을 요구하는데 본 포스팅에서는 기본값으로 쭉 설치를 진행했습니다.

 

모든 과정이 완료되었다면 아래와 같은 페이지를 출력해줍니다.

 

 

Jenkins Dashboard

 

 

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

 

 

 

 

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

 

 

 

설치 가능 탭을 누르고 ssh를 검색, Publish Over SSH를 체크하고 지금 다운로드하고 재시작 후 설치 버튼을 눌러 설치를 진행합니다.

 

이후 더 설정해 주어야 하는 내용이 있지만 Git Hub와 Service Server 세팅을 마치고 나머지를 마저 진행해 주겠습니다.

 

 

3. Service 서버 컨테이너 준비

서버 컨테이너는 Dockerfile을 통해 생성해 주도록 하겠습니다.

Dockerfile은 어떠한 베이스 이미지를 기반으로 사용자에게 필요한 것들을 사전에 세팅해 새로운 이미지를 생성할 때 사용하는 명세서라고 보면 됩니다.

먼저 Dockerfile을 관리하기 위한 디렉터리와 파일을 생성합니다.

 

$ cd
$ mkdir service_server
$ cd service_server
$ vim Dockerfile

 

아래 내용은 본 예제를 위해 간단히 세팅한 Dockerfile입니다. 파일 내 작성합니다.

 

FROM ubuntu:20.04

# default user
ENV USER serve

# packages install
RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y sudo vim net-tools ssh openssh-server openjdk-8-jdk-headless

# Access Option
RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/UsePAM yes/#UserPAM yes/g' /etc/ssh/sshd_config

#user add & set
RUN groupadd -g 999 $USER
RUN useradd -m -r -u 999 -g $USER $USER

RUN sed -ri '20a'$USER'    ALL=(ALL) NOPASSWD:ALL' /etc/sudoers

#set root & user passwd
RUN echo 'root:root' | chpasswd
RUN echo $USER':serve123' | chpasswd

# java 환경변수
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

ENTRYPOINT sudo service ssh restart && bash

USER $USER

 

 

베이스 OS는 Ubuntu 20.04를 사용했고, jdk와 vim을 설치했습니다.

그리고 이후 Jenkins에서 사용할 사용자 계정과 ssh 접속 키를 관리하기 위한 .ssh 디렉터리도 같이 생성해주었고, 프로젝트 배포를 위해 JAVA 환경 변수를 미리 지정해두었습니다.

 

참고로 사용자 계정은 serve, 패스워드는 serve123입니다. 이후 사용될 정보이니 꼭 기억해주세요.

 

이제 이 Dockerfile을 이용해 이미지를 빌드해야 합니다.

 

$ docker build -t service-server .

마지막 . 까지 입력해야 합니다.

 

 

 

 

현재 PC에 Ubuntu20.04 이미지가 없기 때문에 먼저 받는 것을 확인할 수 있습니다.

빌드 과정은 시간이 다소 걸립니다.

 

빌드가 완료되면 아래의 명령어로 이미지를 컨테이너에 적재합니다.

앞으로 이 서비스 컨테이너의 이름을 spring-server라 부르겠습니다.

 

$ docker run --name spring-server -itd -p 9000:9000 -p 9022:22 service-server:latest

-p 옵션 중 9000번은 Spring Boot에게 할당할 포트고, 9022번 포트는 Jenkins에서 ssh로 접근하기 위한 포트입니다.

 

 

spring-server docker container

 

 

docker ps로 프로세스를 확인해보면 9000번, 9022번 포트가 개방된 서버 컨테이너가 올라가 있습니다.

 

4. Jenkins, Github - SSH 키 생성 및 등록

Jenkins에서 Github에 접근하기 위해서는 SSH 키를 깃허브에 등록해야 합니다.

Docker에서 Jenkins를 적재할 때, -v 옵션으로 /home/jenkins/에 마운트를 해둔 상태이므로 호스트 PC에서 ssh키 생성을 대행할 수 있습니다.

아래의 명령어를 차례로 입력합니다.

 

$ sudo mkdir /home/jenkins/.ssh
$ sudo chmod 700 /home/jenkins/.ssh
$ sudo ssh-keygen -t rsa

진행하면 아래의 저장 경로와 키파일 이름을 설정하는 구간이 나옵니다. 다음과 같이 지정합니다.
Enter file in which to save the key (/root/.ssh/id_rsa): /home/jenkins/.ssh/id_rsa

 

 

호스트 PC에서의 ssh-key 발급 대행

 

 

이제 발급받은 ssh key를 GitHub에 등록해야 합니다. 등록하는 키는 퍼블릭 ssh key 값인 id_rsa.pub의 값입니다.

키값은 sudo 권한이 없으면 접근할 수 없으므로 다음의 순서대로 키 값을 확보합니다.

 

$ sudo -s
# cat /home/jenkins/.ssh/id_rsa.pub

- 출력 결과
ssh-rsa AAAAB3NzaC1yc2 ...

 

 

Jenkins Public SSH Key 확보

 

ssh-rsa로 시작하는 키 값을 미리 복사해 둡니다.

 

 

5. GitHub에 Jenkins의 Public SSH Key 등록

이제 브라우저에서 깃허브에 로그인합니다.

 

 

 

우측 상단 프로필을 누르면 여러 메뉴가 나오는데 이중에 Settings에 들어갑니다.

 

 

 

 

 

좌측 메뉴에서 SSH and GPG keys를 누르면 키값 등록을 위한 패널이 나오는데, 여기서 SSH Keys 항목의 New SSH key 버튼을 눌러 새 키값을 추가해줍니다.

 

 

 

 

키값의 이름은 본 예제에서는 jenkins-docker로 등록했습니다.

복사한 키 값을 붙여 넣고 Add SSH key 버튼을 누르면 키값 등록이 완료됩니다.

 

 

등록된 key 값

 

 

 

6. spring-server에 Jenkins의 Public SSH Key 등록

Jenkins에서 빌드한 프로젝트를 spring-server에 배포하기 위해서 jenkins의 Public SSH Key를 등록해주어야 합니다.

아래의 명령어로 spring-server 컨테이너 터미널에 접근 후 키 값을 ~/.ssh/authorized_keys에 넣어줍니다.

 

$ docker exec -it spring-server /bin/bash
# vim ~/.ssh/authorized_keys

Jenkins의 Public SSH Key 값 입력

 

 

7. Jenkins에 Service Server SSH 접근 설정

 

 

 

Jenkins 대시보드에서 Jenkins 관리 > 시스템 설정을 누릅니다.

스크롤을 가장 아래로 내려 Publish over SSH 항목을 찾습니다.

만약 항목이 없다면 주소창에서 jenkinsurl:9090/restart에 접속해 Jenkins를 재시작합니다.

 

 

 

 

SSH Servers에 추가 버튼을 눌러 Name, Hostname, Username, Remote Directory 그리고 Passpharse / Password, Port를 입력합니다. name은 Jenkins에서 식별하기 위한 서버 이름이므로 아무 이름이나 지정해도 무방합니다.

대신 Hostname과 Username은 정확히 입력해야 합니다. Hostname은 Service Server의 IP주소를, Username은 서버의 유저 계정을 입력합니다.

예제에서는 Dockerfile에 의해 미리 사용자 계정을 생성해 두었으므로 serve를 이 Username에 넣어주고 Passpharse / Password에 serve123을 넣어줍니다.

포트 번호는 컨테이너의 ssh 포트 22번을 호스트의 9022로 연결시켰으므로 9022를 넣어줍니다.

입력해야 할 필드를 정리하자면 아래와 같습니다.

 

  • Name: Jenkins에서 식별할 서버 이름(아무 이름이나 넣어도 됩니다.)
  • Hostname: 호스트 PC의 ip주소를 넣습니다.
  • Username: spring-server의 사용자 계정 serve를 넣습니다.
  • Remote Directory: spring-server의 사용자 루트인 /home/serve를 넣어줍니다. 
  • Passphrase / Password: spring-server의 사용자 패스워드 serve123을 넣습니다.
  • Port: 호스트로 개방시킨 9022번 포트를 넣습니다.

 

 

Jenkins SSH 접속 테스트

 

 

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

 

8. GitHub 저장소 추가

 

Spring boot Source code

 

본 예제에서는 Jenkins에서 연동할 저장소의 이름을 jenkins_spring으로 생성하고 형상관리를 해 주었습니다.

여기서 Code라 적혀있는 녹색 버튼을 눌러 저장소의 주소를 미리 확보해둡니다.

 

 

 

여기서 주의할 점은 저장소 주소를 SSH 형식으로 가져와야 사용할 수 있습니다.

 

9. Jenkins에 프로젝트 등록

 

 

 

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

 

 

 

 

 

예시에서는 jenkins_spring이라는 이름의 프로젝트를 추가하겠습니다.

Freestyle project를 클릭하고 하단에 OK 버튼을 누릅니다.

 

 

 

 

소스코드 관리 항목에서 Git에 체크하고 7번 항목에서 확보한 GitHub의 저장소 주소를 추가합니다.

아마 지금은 접속이 허용되지 않는다고 아래와 에러를 뱉을 겁니다.

 

 

GitHub 연결 실패

 

 

 

 

Credentials의 Add 버튼 - Jenkins 항목을 눌러 접속 계정을 세팅합니다.

(위 Jenkins는 인증키에 대한 그룹으로 이후 추가 삭제가 가능합니다.)

 

 

 

 

Kind를 SSH Username with private key로 설정합니다.

그러면 아래와 같이 입력 폼이 바뀌게 됩니다.

 

 

 

 

여기서 필수적으로 입력해야 할 필드는 ID, Username, Private Key입니다.

Id는 Jenkins 컨테이너의 유저 계정입니다. (기본적으로 jenkins입니다.)

그리고 Username은 Jenkins 대시보드에서 키값을 식별하기 위한 이름으로 아무 이름이나 지정해도 무방합니다.

 

가장 중요한 부분은 Private key입니다. Enter dicrectly에 체크하고 Private SSH Key를 입력해야 합니다.

호스트 PC 기준으로 /home/jenkins/.ssh/id_rsa 파일의 내용입니다. (id_rsa.pub이 아닙니다.)

 

 

 

 

 

 

Jenkins 인증 키 추가

 

키값을 추가하면 Credentials 리스트에 방금 추가한 키 값이 나타납니다.

 

 

 

 

키값을 추가하자 에러 메시지가 사라진 것을 확인할 수 있습니다.

여기서, ssh 연동일 경우 추가로 Name, Refspec, Branch 관련 정보를 추가해주어야 합니다.

아래와 같이 작성합니다.

 

 

 

Name: origin

Refspec: +refs/pull/*:refs/remotes/origin/pr/*

Branch Specifier (blank for 'any'): */**

 

 

 

 

 

 

스크롤을 내려 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 정의는 한차례 완료된 것입니다.

 

다음으로 빌드가 완료된 파일을 어떻게 spring-server로 보내 배포하는지 정의해야 합니다.

 

 

 

 

Add build step에서 Send files or execute commands over SSH를 선택합니다.

만약 이 항목이 보이지 않는다면 2번 과정에서 Publish Over SSH 플러그인이 제대로 설치되지 않은 것입니다.

 

 

 

 

Name은 5번 항목에서 추가한 SSH Server의 이름이 리스트 형태로 나타납니다.

 

Transfers에서 실질적으로 빌드한 파일을 spring-server로 보내고 배포하는 행위를 정의하게 됩니다.

 

Build Server의 Jenkins 계정의 루트 경로는 /var/lib/jenkins입니다. 

Jenkins에서 Gradle Build를 하게 될 경우 /var/lib/jenkins_home/build/libs 에 jar 파일이 위치하게 되므로

Source files를 build/libs/*jar로 정의합니다.

 

Remote directory는 파일을 전송받는 Service Server의 경로를 의미합니다.

7번 항목에서 SSH 접속 루트를 /home/serve로 정의했으므로 결과적으로 Remote directory는

/home/serve/spring_project/target이 됩니다.

 

spring-server에서 미리 /home/serve/spring_project/target 디렉터리를 생성해 줍니다.

또한 프로젝트 구동 시의 로그 파일을 저장하기 위한 logs 디렉터리도 함께 만들어줍니다.

 

# spring-server (docker container)

//Docker를 통해 Bash에 접근합니다.
$ docker exec -it spring-server /bin/bash

//컨테이너에 접근한 상태, 계정은 serve입니다.
$ cd
$ 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/serve/spring_project/target/jenkins_test_spring-0.0.1-SNAPSHOT.jar >> /home/serve/spring_project/logs/jenkins_test_spring.log &

echo "Done"

 

기존에 실행되고 있는 프로젝트 프로세스를 죽이고 jar폴더를 실행하는 스크립트입니다.

저장하고 대시보드로 이동합니다.



10. 프로젝트 빌드 & 배포

 

 

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

 

 

 

 

Console Output에서 BUILD SUCCESSFULL을 띄워주면 빌드는 완료된 것입니다.

 

 

이제 배포 서버에서 배포까지 완료되었는지 살펴보겠습니다.

 

 

 

 

9000번 포트를 할당받아 java 프로세스가 올라가 있습니다.

한번 위 포트로 접속해보겠습니다.

 

 

 

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