본문 바로가기
우리 FISA

우리FISA 클라우드 서비스 개발 - EC2, Docker, Jenkins를 사용한 Spring-boot 어플리케이션 배포

by dvid 2023. 7. 30.

CI/CD 파이프라인 구축

수업에서 Jenkins를 활용해 Spring 어플리케이션을 배포했다.
프로젝트 진행 시 설정 정보가 담겨있는 application.yml은 보통 git에 올리지 않고 따로 관리한다.
이때 어떻게 배포할지 고민했고, 아래의 과정으로 application.ymlgithub에 올리지 않고, CI/CD 파이프 라인을 구축해보자.

Docker 설치

sudo apt update
sudo apt-get install ca-certificates curl gnupg

Add Docker’s official GPG key:

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

Use the following command to set up the repository:

echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Docker Engine 설치

sudo apt update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo chmod 006 /var/run/docker.sock

설치 완료

ubuntu@ip-172-31-13-228:~$ docker -v
Docker version 24.0.5, build ced0996

Jenkins 설치

EC2 Free-tier에서 Jenkins를 사용하면 메모리가 부족해 정상 작동이 안될 수 있다.

ubuntu@ip-172-31-13-228:~$ free -h
              total        used        free      shared  buff/cache   available
Mem:          966Mi       218Mi       130Mi       0.0Ki       617Mi       587Mi
Swap:            0B          0B          0B

swap 메모리사용을 통해 이를 해결하자.

sudo dd if=/dev/zero of=/swapfile bs=128M count=16
  • dd: 루트파일 시스템에 스왑 파일 생성
  • bs: Block 크기
  • count: Block 수
  • 지정한 블록 크기는 인스턴스에서 사용가능한 메모리보다 작아햐 한다.
sudo chmod 600 /swapfile
sudo mkswap /swapfile

Setting up swapspace version 1, size = 2 GiB (2147479552 bytes)
no label, UUID=43d7dc37-535f-4a81-942d-c9cd46d1a284

스왑영역 설정

sudo swapon /swapfile
  • swap 공간에 swap file을 추가하여 swap file 사용하도록 설정
sudo  swapon -s

Filename                Type        Size    Used    Priority
/swapfile                                  file        2097148    0    -2
  • 성공 여부 확인
sudo vi /etc/fstab

LABEL=cloudimg-rootfs   /        ext4   defaults,discard        0 1
LABEL=UEFI      /boot/efi       vfat    umask=0077      0 1
/swapflie swap swap defaults 0 0
  • 부팅 시 swap file 활성화
ubuntu@ip-172-31-13-228:~$ free -h
              total        used        free      shared  buff/cache   available
Mem:          966Mi       197Mi       163Mi       0.0Ki       605Mi       605Mi
Swap:         2.0Gi          0B       2.0Gi

total 2GB로 변경되었다.

Docker에 Jenkins 이미지 설치

docker pull jenkins/jenkins:jdk11
ubuntu@ip-172-31-13-228:~$ docker images
REPOSITORY        TAG       IMAGE ID       CREATED      SIZE
jenkins/jenkins   jdk11     1dd84990e937   3 days ago   463MB

Docker에서 Jenkins 실행

ubuntu@ip-172-31-13-228:~$ docker run -d -p 80:8080 --name jenkins jenkins/jenkins:jdk11
e749a14300bb9303a6720df0c46c34c44710bb5b95e31775092025cd850258cb
ubuntu@ip-172-31-13-228:~$ docker ps -a
CONTAINER ID   IMAGE                   COMMAND                  CREATED         STATUS         PORTS                                              NAMES
e749a14300bb   jenkins/jenkins:jdk11   "/usr/bin/tini -- /u…"   5 seconds ago   Up 2 seconds   50000/tcp, 0.0.0.0:80->8080/tcp, :::80->8080/tcp   jenkins

이제 public ip로 접근 가능하다.

jenkins shell 이동

docker exec -it jenkins /bin/bash

로그인 secret 확인

cat /var/jenkins_home/secrets/initialAdminPassword

Install suggested plugins

플러그인 설치 후 skip and countunue as admin

Web Project 생성

private repository 만든 후 깃에 push.
참고로 yml파일은 .gitignore에 추가하지 않고 push 했다.

Jenkins에서 git에 접근을 위한 credential 생성

Developer settings → Personal Access tokens → Tokens(classic) → Genereate new token(classic)

토큰 이름 지정 후 repo, admin:repo_hook 체크

  • ghp_ 로 시작하는 토큰 보관

Jenkins관리 -> System -> Github

  • Credential 두 개 생성

secret: 아까 발급받은 ghp_로 시작하는 토큰 Secret에 기입

username: github 이메일
password: 아까 발급받은 ghp_로 시작하는 토큰 Secret에 기입

Test Connection

credential에 secret text로 만든 토큰 설정 후 test connection

Jenkins Pipeline 생성

pipeline syntax 클릭

아래와 같이 스크립트 입력

지금 빌드 클릭 후 클론 여부 확인

docker exec -it jenkins /bin/bash

cd /var/jenkins_home/workspace/{파이프 라인 이름}

ls

git에 push된 프로젝트가 존재하는지 확인해야 한다.

Jenkins에서 빌드

아까 application.yml을 제외하고 git에 push 했다.

이 설정 파일을 Jenkins 빌드 시 주입하고자 Jenkins서버에 만들어두자.

docker exec -itu0 jenkins /bin/bash

apt-get update
apt-get upgrade
apt-get install vim

vi /var/jenkins_home/workspace/settings/application.yml

나의 설정파일 내용은 아래와 같다.

spring:
  datasource:
    url: jdbc:h2:~/test
    driverClassName: org.h2.Driver
    username: sa
    password:

  h2:
    console:
      enabled: true
      path: /h2-console
stage('build with gradle') {        
    steps {
        sh 'cp /var/jenkins_home/workspace/settings/application.yml src/main/resources/application.yml'
        sh 'chmod +x gradlew'
        sh './gradlew clean build'
    }
}

.gitignore 설정을 잘못해서 17트 만에 성공했다.

이제 서버에 배포 및 실행만 남았다.

SSH 접근 위한 credential 추가

jenkins관리 -> credentials -> Stores scoped to Jenkins의 system 클릭 -> Global credentials -> add credentials

EC2에 접근하는 pem키 등록

총 3개 credentials

ssh plugin (SSH Agent) 설치 후 jenkins 컨테이너 재시작

docker restart jenkins

spring server 설정

Spring 서버가 동작할 EC2에 들어가 jdk를 설치하고 jar파일을 실행할 쉘 스크립트를 작성하자.

apt-get update
sudo apt install openjdk-11-jdk

cd ~

mkdir mydemo

vi deploy.sh

#!/bin/bash

pid=$(pgrep -f mydemo)

if [ -n "${pid}" ]
then
        kill -15 ${pid}
        echo kill process ${pid}
else
        echo no process
fi
chmod +x ./mydemo/demo-0.0.1-SNAPSHOT.jar
nohup java -jar ./mydemo/demo-0.0.1-SNAPSHOT.jar >> application.log 2> /dev/null &

Jenkins Script for Deploy

stage('deploy') {
    steps {
        sshagent(credentials: ['jenkins-ssh-key']) {
            sh '''
                ssh -o StrictHostKeyChecking=no ubuntu@{spring server ip} uptime
                scp /var/jenkins_home/workspace/spring-pipeline/build/libs/*.jar ubuntu@{spring server ip}:/home/ubuntu/mydemo
                ssh -t ubuntu@{spring server ip} chmod +x ./deploy.sh
                ssh -t ubuntu@{spring server ip} ./deploy.sh
            '''
        }
    }
}

배포 결과

github webhook을 통한 CI/CD

payload url: Jenkins Server IP
Content type: application/json

jenkins 구성 -> GitHub hook trigger for GITScm polling 체크

아래 6개 규칙을 Jenkins 서버 EC2 보안그룹에 추가

유형 프로토콜 포트 범위 소스 IP 주소 이름
HTTP TCP 80 사용자 지정 192.30.252.0/22 github-webhook-1
HTTP TCP 80 사용자 지정 185.199.108.0/22 github-webhook-2
HTTP TCP 80 사용자 지정 140.82.112.0/20 github-webhook-3
HTTP TCP 80 사용자 지정 143.55.64.0/20 github-webhook-4
HTTP TCP 80 사용자 지정 2a0a:a440::/29 github-webhook-5
HTTP TCP 80 사용자 지정 2606:50c0::/32 github-webhook-6

ping 확인

webhook을 설정하고 push 하면 아래와 같이 push 태그의 webhook을 보낸다.

Jenkins에서는 이를 감지하여 script에 작성된 내용을 자동으로 수행하게 된다.

완료

위와 같은 process로 CI/CD 파이프라인을 구축해보았다.

 

프로젝트 시 Docker를 활용할 것 같다. 그냥 서버가 아닌 Docker에 배포하는 방법도 찾아봐야 할 것 같다.

 

또한, 운영서버와 로컬 서버를 나누면서 application.yml 파일을 따로 관리해야 할 텐데, 설정파일을 더 효율적으로 관리할 수 있는 방법을 고민해봐야할 것 같다.

 

 

참고

댓글