menu

배포자동화 적용- CI/CD 서버구축하기

개요

  최근 스마트스토어 프로젝트를 진행하면서 기능추가 시에 반복적으로 수행해야 하는 작업들이 있었습니다. 개발한 기능을 서버에 배포하기 전에 빌드하고 테스트를 해야했고, 동료 개발자가 개발한 기능을 확인하려면 해당 branch를 clone을 받아서 local에서 실행-테스트 해보곤 했습니다. 이런 작업들은 손이 많이 가고 반복적이었지만 생략할 수 없는중요한 작업들이었습니다.
 이런 불편함을 느끼던 중에 CI/CD라는 개념을 알게 됐고, 저에게 절실히 필요하다고 느껴졌기에 공부하고 적용해보기로 했습니다.

간단하게 CI/CD에 대해서 간단하게 정리해 보면,

CI란?

지속적인 통합(Continuous Integration)이라는 의미로서 개발자가 개발한 코드를 빌드하고 테스트하는 행위를 자동화하는 것을 말합니다. 지속적 통합이라는 단어 자체가 조금 와닿지 않을 수도 있겠지만, 쉽게 풀이하자면 개발자가 새로 추가하거나 수정한 코드를 메인 브랜치에 합치는 것을 자동화한 것이라고 이해하면 될 것 같습니다.

CD란?

지속적 제공(Continuous Delivery)과 지속적 배포(Continuous Deployment)라는 의미로서 빌드와 테스트가 완료된 후 배포하고자 하는 서버에 자동으로 배포하는 것입니다.

보다 자세한 설명은 이곳을 참고하시면 됩니다.


지속적 통합 , 지속적 제공, 지속적 통합

위의 그림처럼 CI(Continuous Intergration)단계에서 빌드-테스트-통합을 수행하고, CD(Continuous Delivery, Continuous Deployment)의 각 단계에서 빌드된 파일을 전송하고 배포하게 됩니다.

 CI를 통해서 개발자들은 코드 변경 사항을 공유 브랜치 또는 트렁크로 병합하는 작업을 자주 그리고 쉽게 수행할 수 있습니다. 어플리케이션에 적용한 변경 사항이 병합되면 이러한 변경 사항이 어플리케이션을 손상시키 이지 않고 자동으로 어플리케이션을 구축하게 되고, 테스트를 자동으로 실행하여 변경 사항이 어플리케이션에 제대로 적용이 되었는지 확인할 수 있습니다.
만일 자동화된 테스트에서 기존 코드와 신규 코드 간의 충돌이 발견되면 CI를 통해 이러한 버그를 발견하고 더 빠르게 자주 수정할 수도 있습니다.



이번 프로젝트에 CI/CD를 적용하기 위해 사용한 방식은 Jenkins와 Github를 연동하는 방식을 사용했습니다.
대략적인 흐름은, 소스를 수정하여 Github에 push하면 Github에서 Jenkins에 event를 발생시켜주고, Jenkins는 Github으로부터 소스를 내려받아 Build를 자동으로 수행하여 Run하게 되는 방식입니다.

Jenkins 서버 설정

Github에서 보내는 event 신호를 받으려면 24시간 대기 중인 Jenkins서버가 필요합니다. 24시간 서버를 사용하려면 당연히 비용이 들게 됩니다. 하지만 일단 학습을 위한 용도이기 때문에 최대한 과금이 없는 방법으로 시도해보고자 했고, 방법을 찾던 중 local에서 jenkins를 실행하고 ngrok이라는 라이브러리를 통하여 사용하는 방법을 찾았습니다. Jenkins의 설치와 ngrok을 이용하여 local에서 Jenkins 서버를 실행하는 것은 jojoldu님의 블로그 글을 참고했습니다.


ngrok이 필요한 이유는 Github와 Jenkins를 연동하기 위해 url을 입력할 때 localhost url은 사용할 수가 없기 때문입니다. ngrok은 local에서도 외부 ip을 사용하는 것처럼 해주는 역할을 합니다. ngrok에 대한 자세한 설명은 이곳에 이미 잘 정리됐기 때문에 이곳을 보시면 됩니다.
==> Local서버에서 Naver Cloud Platform으로 전환하였습니다. 사실 스마트스토어 프로젝트는 다수의 서버 (CI/CD서버, WAS서버, DB서버 2대(master와 slave), redis-cache 서버, redis-session 스토리지 서버, nginx 서버)가 필요하기 때문에 무작정 과금을 하며 클라우드 서버를 사용하자니 청구될 금액이 걱정이었습니다. 하지만 개발이 진행되면서 전체적인 아키텍쳐를 실제로 구성해야만 하는 상황이 왔고 local에서 구성하기에는 한계가 있다고 생각되어, 결국에는 클라우드 서버 환경을 택해야 했습니다.
AWS의 프리티어도 있지만, NCP(Naver Cloud Platform)를 택한 이유는 NCP는 가입 시에 10만원 상당의 크레딧을 주며, f-lab 제휴를 통해 추가 크레딧을 지급 받았기 때문에 다수의 서버를 충분히 원하는 만큼 추가하고 사용할 수 있었기 때문입니다.

Jenkins Pipleline

Jenkins 설치가 완료됐다면, 이제 본격적으로 Jenkins가 할 일들을 설정할 수 있습니다. DashBoard > ‘새로운 Item’ 메뉴에 들어가면 어떤 방식으로 Jenkins를 구성할 것인지 선택할 수 있습니다. 저의 경우 JenkinsFile방식을 사용하기 위해 Jenkins Pipeline 방식을 선택했습니다.

Jenkins Pipeline이란? Jenkins Pipeline이란 생성되어 있는 젠킨스 Job들을 순차적, 혹은 병렬적으로 실행시키거나 특별하게 작성한 스크립트로 이벤트들을 연속적으로 실행시키는 기능입니다. 즉, 젠킨스 파이프라인을 실행하면 Jenkins의 Job들을 일일이 수동으로 실행하지 않고, 미리 정해둔대로 순차적, 병렬 실행이 가능합니다.

아래는 제가 작성한 JenkinsFile과 deploy.sh입니다.

pipeline {
    agent any
    
    tools{
    	maven "M3"
        jdk "java 1.8"
    }
 
    stages {
        // 빌드 
        stage('build'){
        	steps {
                sh 'mvn -Dmaven.test.failure.ignore=true install clean package' 
            }
        }
        // was로 jar파일을 전송
        stage('copy app file to was'){
        	steps{
        		sh 'scp -P 1235 /var/lib/jenkins/workspace/smartstore_isaac2/target/smart-store-0.0.1-SNAPSHOT.jar root@xxx.10.45.18:/root/smart-store/app/smart-store.jar'
        	}
        }
        // was에 접속 후, deploy 쉘 파일을 실행
        stage('connect to deploy server and deploy'){
        	steps{
        		sh 'ssh -p 1235 -i /var/lib/jenkins/.ssh/id_rsa root@xxx.10.45.18 "/root/smart-store/app/deploy.sh"' 
        	}
        }
    }
}

JenkinsFile



echo "now start bash script in deploy server"

REPOSITORY=/home/flab/smart-store/app

cd $REPOSITORY

CURRENT_PID=$( pgrep -f smart-store/app/smart-store.jar )

echo "PID is $CURRENT_PID"

if [ -z $CURRENT_PID ]; then
	echo " There is no application running. "
else
	echo " kill -15 $CURRENT_PID "
	kill -15 $CURRENT_PID 
	sleep 3
fi

echo " application start "
JAR_NAME=$(ls $REPOSITORY | grep 'smart-store' | tail -n 1 )

echo "jar name : $JAR_NAME"

nohup java -jar -javaagent:/home/flab/pinpoint-agent-2.3.0/pinpoint-agent-2.3.0/pinpoint-bootstrap-2.3.0.jar -Dpinpoint.applicationName=smartstore -Dpinpoint.agentId=smst0002 -Dprofiler.transport.grpc.collector.ip=101.101.208.216 /home/flab/smart-store/app/smart-store.jar >> nohup.out 2>&1 &
echo "success"

deploy.sh

마무리

CI/CD환경을 처음 구축해보다보니 많은 시행착오가 있었습니다.
서버 간 통신을 위해 로그인, 파일전송, 인증 관련 이슈가 계속해서 나타났기 때문에, 예상보다 더욱 많은 시간이 걸렸던 것 같습니다. 일주일에 가까운 시간을 할애하며 삽질을 거듭한 끝에 CI/CD에 성공할 수 있었습니다. 이제 빌드-테스트-배포의 작업에서 보다 자유롭게 되어, 개발에 더욱 집중할 수 있을 것 같습니다. 읽어주셔서 감사합니다.