라즈베리파이 리눅스에서 SpringBoot, Gradle 기반 Rest API서비스 Jenkins로 연동 및 배포하기

livenow·2020년 8월 8일
2
post-thumbnail

안녕하세요.

aws에서 돌리던 서비스를 라즈베리파이로 옮기려고 여러 문서를 찾아봤었는데

Gradle 기반, 깃허브 연동 후 조치 등에 대한 지식이 방대하고 흩어져 있어 정리하게 되었습니다.

사용 시나리오

  1. 로컬로 돌리던 서비스를 원격서버에 배포하려고 한다.

  2. 깃허브 레포지토리에 작업이 끝난 변경사항을 push 하면 jenkins는 push를 감지해 빌드 후 배포를 한다.

  3. 변경사항 push → 서버에 clone → 빌드 → 배포의 과정이

    변경사항 push → 자동 배포로 바뀌게 되는 마법이 일어난다.

Step1. 서버 java 버전 확인

최근 jekins는 java버전 1.8이상이 되어야 제대로된 실행이 가능합니다.

  1. 패키지 최신으로 업그레이드
$ sudo apt-get update
$ sudo apt-get upgrade
  1. 자바 버전 확인
$ java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (build 1.8.0_212-8u212-b01-1+rpi1-b01)
OpenJDK Client VM (build 25.212-b01, mixed mode)
  • 저는 현재 1.8.0_212가 깔려있는 상태입니다.

Step2. 젠킨스(Jenkins)설치

$ wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -
  • deb를 적용하기 위해 /etc/apt/sources.list 에 아래를 추가합니다.
$ vim /etc/apt/sources.list
#추가하기
deb https://pkg.jenkins.io/debian-stable binary/
  • 추가 된 모습

  • 젠킨스 업데이트 후 설치
$ sudo apt-get update
$ sudo apt-get install jenkins

젠킨스 포트 변경

젠킨스의 기본 포트는 8080입니다.

한대의 서버에서 이것저것 설치할 경우 8080는 사용하기 어렵기 때문에 (저 같은 경우 배포 서버를 8080에 두고있습니다.) 이를 잘 사용하지 않는 포트로 변경하겠습니다.

# jenkins config 열기
sudo vim /etc/default/jenkins

그리고 아래와 같이 HTTP_PORT 를 변경합니다.

저는 8081로 수정하였습니다.

이렇게 설정 하신 후 Jenkins를 실행해보시면 포트가 바뀌어서 수행된 것을 확인할 수 있습니다.

$ sudo service jenkins start

주의점

  • 라즈베리파이 설정에서 8081포트를 열어야 외부에서 접근이 가능합니다.

Step3. 젠킨스 실행 후 초기 설정

실행이 원활하게 되었다면 (자바 버전 제대로 설치했다면 문제가 생기지 않음) 브라우저를 열어

http://본인서버localhost:8081/

에 접근합니다.

그러면 아래와 같은 창을 보실 수 있습니다.

cat으로 해당 코드를 확인하여

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

출력 값을 브라우저에 붙여넣습니다.

다음엔 플러그인 설치 화면인데, 저는 모두 설치를 진행하였습니다.

  • 설치중 오류가 나온다면, 다시 다운이라는 버튼이 나오니 모두 설치가 될 때 까지 눌러줍니다.

다음으로 계정 설정이 나오는데, jenkins에 사용할 계정과 비밀먼호를 설정합니다.(이후 접속시 로그인을 해야하기 때문에 까먹으면 안돼요)

설치가 다 완료 되었다면 아래와 같은 화면을 볼 수 있습니다.

로그인을 한다면 밑의 화면을 만날 수 있습니다. 저는 현재 배포중이기에 배포중인 것을 확인할 수 있습니다.

Step4. 깃허브 연동을 위한 SSH 공개키 만들기

젠킨스와 Github연동 시 사용자명과 비밀번호 인증방식은 보안상 추천하지 않습니다.

그렇기 때문에 ssh로 연동하는 법을 배워보겠습니다.

사용자의 SSH키들은 기본적으로 ~/.ssh 디렉터리에 저장되어 있습니다.

$ ls ~/.ssh
id_rsa  id_rsa.pub
  • .pub 파일은 공개키이고 다른 파일은 개인키입니다.

만약 파일이 없거나, ~/.ssh 디렉터리가 없다면 ssh-keygen 프로그램을 사용하여 키를 생성해야합니다.

$ ssh-keygen
 Generating public/private rsa key pair.
 Enter file in which to save the key (/home/pi/.ssh/id_rsa):
 Created directory '/home/pi/.ssh'.
 Enter passphrase (empty for no passphrase):
 Enter same passphrase again:
 Your identification has been saved in /home/pi/.ssh/id_rsa.
 Your public key has been saved in /home/pi/.ssh/id_rsa.pub.
 The key fingerprint is:

저는 디렉터리와 키를 따로 설정하지 않았습니다.( 기본 디렉터리, 키 사용시 암호없음 )

젠킨스로 배포하려는 Repository > Settings > Deploy keys → Add deploy key 선택

저는 현재 만들어진 상태입니다.

add를 누르고

위에서 확인한 ssh키값을 복사합니다.

$ cat ~/.ssh/id_rsa.pub

title엔 적절한 이름을,

key는 cat으로 확인한 값을 넣고 저장합니다.

젠킨스에도 연동을 해줘야겠죠 Jenkins 관리 > Manage Credentials에 들어갑니다.

Global credentials에 들어간다음

Add Credentials를 눌러 줍니다.

비밀키를 등록하기 위해 비밀키를 복사합니다.

$ cat ~/.ssh/id_rsa

그러면

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbDnBpQHJhc3BiZXJyeXBpAQIDBA==
...
...
...
-----END OPENSSH PRIVATE KEY-----

이러한 형식의 값들을 볼 수 있는데

Begin 과 End를 포함한 것을 아래와 같은 설정으로 넣어줍니다.

Kind

  • 인증 방식을 선택합니다.
  • 여기선 비밀키 방식을 선택해야 Github과 공개키/비밀키로 인증이 가능합니다.
  • Username
    • 각 젠킨스 Job에서 보여줄 인증키 이름 입니다.
    • 저는 github로 설정하였습니다.
  • Private Key
    • 좀전에 복사한 비밀키를 그대로 붙여넣습니다.

Step5. 연동 준비

1. Jenkins job이 GitHub의 푸시 이벤트를 받게 하기

사용자의 GitHub OAuth 토큰을 이용해 Jenkins가 자동으로 Hooks & Service를 등록하게 하겠습니다.

우선 Jenkins 관리 > 시스템 설정 에서 Github 세팅을 찾습니다

  • Name : 적절한 이름을 지어줍니다.
  • API URL : 자신이 사용하는 GitHub 주소입니다.
  • Credentials 오른편의 Add를 눌러 GitHub Personal access 토큰을 입력합니다.
  • Manage hooks를 클릭해줍니다.

GitHub Personal access 토큰은 자신의 GitHub계정의 Settings > Developer Settings > Personal access tokens 에서 Generate new token으로 토큰을 생성합니다.

아래와 같이 진행합니다.

  • Note에 적절한 이름을 적어줍니다.
  • repo와 admin:rep_hook을 선택하고 토큰을 생성합니다.

주의 생성되고 나오는 토큰을 저장해두세요, 처음 생성할때만 알려줍니다.

다시 Jenkins로 돌아가서 Credentials에 add를 눌러줍니다.

  • kind는 Secret text로 지정합니다
  • Secret : 깃허브에서 발급받은 토큰값
  • ID와 Description은 인증정보를 구별할 수 있는 적당한 값으로 입력합니다.

설정이 완료되고 Test connection으로 연결이 잘 되는지 확인합니다.

2. 자바 설정

젠킨스 서버에서 자바 버전을 알아야 하기때문에 지정을 해줍니다.

젠킨스 관리 > Global Tool Configuration에 들어갑니다.

add jdk를 눌러주고 라즈베리파이에 설치된 Jdk name과 JAVA_HOME을 적어줍니다. 현재 저는 밑에와 같이 되어있습니다.

Jdk name 확인

$ java -version
openjdk version "1.8.0_212"
  • 버전의 이름앞에 jdk를 붙여줌
    • ex) jdk1.8.0_212

JAVA_HOME 설정

# javac 위치 확인
$ which javac
/usr/bin/javac
# 실제 위치 확인
$ readlink -f /usr/bin/javac
/usr/lib/jvm/java-8-openjdk-armhf/bin/javac
  • 따라서 $JAVA_HOME은 /usr/lib/jvm/java-8-openjdk-armhf으로 설정되야 합니다.
# profile을 연다.
$ sudo vim /etc/profile

맨밑에 추가해줍니다

# 환경변수
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-armhf
# profile reload
$ source /etc/profile 

profile을 reload 하고 서버 재시작후 확인 가능합니다.

$ echo $JAVA_HOME
/usr/lib/jvm/java-8-openjdk-armhf

이를 아래의 JAVA_HOME에 추가해줍니다.

3. Git 설정

젠킨스 관리 > Global Tool Configuration에서 동일하게 Git 설정을 해줍니다.

위와같이 설정을 해줍니다.

드디어 길고긴 설정의 과정이 끝났습니다.

Step6. 프로젝트 시작

Jenkins Job 생성

새로운 item > Freestyle project를 클릭합니다.

이름에는 적절한 이름을 적어주세요 (저는 github_deploy로 지정하였습니다)

General

  • General 에선 GitHub project를 클릭한 후 Url에 배포하려는 소스가 있는 레포 즉, 위에서 ssh값을 입력한 레포지토리의 URL을 적습니다.

    • 필요한 경우 concurrent 빌드 실행 또한 체크해주세요. (빌드가 현재 실행중이더라도 추가적인 빌드를 가능하게 해줍니다.). 체크하지 않는다면 커밋이 올라오면서 실행되는 webhook에 의한 빌드가 실행되질 않습니다.

소스 코드 관리

  • 소스 코드 관리 에선 Git을 체크한 후 Repository URL 에 위의 url을 동일하게 적으시면 됩니다.

  • Credentials은 위에서 추가하였기 때문에 클릭하시면 아래와 같이 보일 것입니다.

    • Branches to build는 누군가 master 브렌치에 push 할 때 해당 Job이 동작하게 하는 것입니다.
    • 추가적으로 develope 이나 다른 브렌치를 추가할 수 있습니다.

빌드 유발

  • 빌드 유발 에서는 GitHub hook trigger for GITScm polling 항목을 선택합니다.

    • 이는 젠킨스가 repository로 부터 GitHub hook Push를 받는다는 얘기입니다.

여기까지 설정을 하고 저장을 합니다.

저장 후 위에서 등록한 Url의 Git repository > Settings > Webhooks 에 들어가면 Jenkins가 자동으로 등록한 Webhooks를 볼 수 있습니다.

이제 Jenkins Job에서 GitHub Hook Log라는 메뉴가 생긴 것을 확인할 수 있습니다.

branch에서 푸시하면 hook 이벤트를 수신한 것을 직접 확인 할 수 있게 되었습니다.

Build

  • 다시 구성으로 돌아가 build 부분으로 갑니다.

저는 여기서 밑의 그림과 같이 세팅을 하였습니다.

  • 제가 생각한 시나리오는 이렇습니다.
    • 현재 Jenkins를 통해 빌드를 한 상태이다 (서비스가 구동중이다. )
    • 작업이 끝나고 버전업이 된 서비스를 master에 push한다.
    • push이벤트를 webhook을 통해 받은 Jenkins는 Gradle로 build를 한 후 배포를 한다.
  • 여기서 제가 격은 문제점은
    • 필요한 경우 concurrent 빌드 실행 을 체크 하여 빌드가 실행되고 있을 때 추가로 실행되려 하지만 현재 실행중인 서비스의 포트를 동시에 쓸 수 없음.
      • 이는 Gradle build전, kill 옵션을 통해 실행중인 서비스를 죽였습니다.
    • 아무것도 실행되고 있지 않을때 위와 같은 구성이면 첫 Excute shell 의 kill이 parameter를 받지 못해 실행 오류가 생김
      • 아무런 서비스를 실행하고 있지 않다면, kill 옵션이 있는 excute shell을 없애고 Jenkins에서 Build Now 를 실행 합니다.
      • 실행 후 다시 구성에 들어가 kill 옵션을 추가 합니다.

다시 이미지로 돌아가겠습니다.

밑의 구성은 Add build step에서 추가할 수 있습니다.

  • 첫 번째, Excute Shell:

    • 8080포트에 서비스가 실행중 일 때, 같은 포트를 쓰려하면 에러를 내며 실행이 되지않습니다.

      ps -ef | grep ""/build/libs/에 들어가는 jar 버전 이름"" | grep -v 'grep' | awk '{print $2}' | xargs kill -9
      
      ex)
      ps -ef | grep myservice | grep -v 'grep' | awk '{print $2}' | xargs kill -9
    • 프로세스 중 Gradle build를 통해 생성된 jar파일을 실행한 프로세스를 찾아 kill 의 파라미터로 넘김

  • 두 번째, Invoke Gradle script 에선 Repository에 포함되어있는 Gradle을 사용할 것이기 때문에 Use Gradle Wrapper를 선택합니다.

    • Make gradlew executable을 선택합니다.
      • 권한 에러 없이 빌드가 잘된다고 합니다.
    • Wrapper location${workspace} 를 작성합니다.
      • Wrapper location은 Wrapper 경로로 jenkins gradle plugin이 지원하는 변수 ${workspace}로 설정합니다.
      • workspace는 현재 프로젝트의 workspace경로입니다.
        • 저는 /var/lib/jenkins/workspace/ 에 레포지토리가 저장이 되어있습니다.
    • Tasks에 clean, build를 작성합니다
      • ./gradlew clean, ./gradlew build와 같은 역할을 수행합니다.
  • 세 번째, Excute Shell

    • Gradle build 된 jar 파일을 배포하는 과정입니다.

      nohup java -jar \
          -Dspring.config.location=classpath:src/main/resources/application.yml,/var/lib/jenkins/workspace/중요 키값 yml 파일\
          -Dspring.profiles.active=real \
          build/libs/myservice-0.1.jar
    • 위와 같은 실행 옵션을 설명하겟습니다.

      • nohup java -jar

        • 사용자가 로그아웃해도 백그라운드로 실행되게 하는 명령어
      • -Dspring.config.location=classpath:

        깃허브에 올리지 못하는 주요 파일( DB 키값,

        Oauth 키값) 등을 서버에 yml로 저장 후 그 path를 지정하면, 프로젝트 실행시 import 됩니다.

      • -Dspring.profiles.active=real

        • 서버에 저장한 application-real 이란 파일을 active 하겟다는 의미입니다.
      • build/libs/myservice-0.1.jar java -jar을 실행할 jar의 위치를 적습니다. workspace가 지정되어있기 때문에 현재와 같이 적어도 무방합니다.

설정을 마치고 저장을 누릅니다.

이제 모든 설정이 완료되었습니다!

  • Build Now를 누르고 싶은 마음이 굴뚝 같겠지만, 우선 구성의 kill 옵션이 있는 첫번째 Excute shell을 없애고 다시 저장합니다. (옵션을 복사해주세요)

  • Build Now를 누르고 다시 구성에 들어가 Add build stpe의 ExcuteShell을 추가한 뒤 위에서 복사한 kill 옵션을 다시 작성하고 첫번째로 옮깁니다.

  • 결론적으로, 서비스는 계속 구동되어지고 master에 push 가 되면 자동적으로 현재 실행중인 서비스를 중단시키고 새로운 변경이 있는 서비스를 실행시킵니다.

결론

아직 저도 배우는 단계라 일단 되게 해보자는 마음으로 했던 과정들입니다.

이후에 더 좋은 방법이 있으면 추가하겠습니다.

긴글 읽어주셔서 감사합니다.

profile
경험의 연장선

1개의 댓글

comment-user-thumbnail
2020년 8월 30일

참고좀 하겠습니다👍

답글 달기