프로젝트 코드를 develop 브랜치에 합치면서 통합 오류로 인해 자주 버그를 수정하곤 했습니다. 또한 여러 대의 서버에 배포를 수작업으로 진행하면서 매번 build하고 jar를 수행하고 이러한 과정을 했습니다. 이는 매우 불필요한 시간 낭비라고 생각이 들었습니다. 이를 해결하기 위해 CI / CD를 적용했습니다.
CI란 지속적인 통합(Continuous Integration) 으로 프로젝트를 진행하며 코드를 "통합"하면서 자동화된 테스트와 빌드를 수행합니다. 이렇게 함으로써 오류를 빨리 찾을 수 있게 됩니다. 오류를 빨리 찾는다는 것은 그만큼 빠르게 버그 수정이 가능하게 됩니다.
CD란 지속적인 전달 Continuous Delivery, 지속적인 배포 Countinuos Deploy 두가지 의미로 혼용해서 사용합니다.
지속적인 배포(Continuous Deploy)는 자동화된 배포를 계속 지원합니다. 예를 들어 개발자가 코드를 변경하면 Git 레포지토리에서부터 프로덕션용 서버까지 바로 자동으로 "배포"되도록 하는 것을 말합니다.
지속적인 전달 (Continuous Delivery)은 코드의 변경사항이 버그 테스트를 거쳐 리포지토리(Github 과 같은 곳)에 자동으로 "업로드"되는 것을 뜻하며, 배포는 수동으로 진행합니다.
젠킨스 파이프라인은 젠킨스에서 CI / CD 모든 과정을(통합, 빌드, 테스트 , 배포) 자동화하도록 지원하는 기술입니다. Jenkinsfile을 작성함으로써 파이프라인이 코드로 구현될 수 있습니다.
젠킨스 파이프라인을 만들고 백엔드에 배포되기까지 다음과 같은 과정을 거칩니다.
def ssh_publisher(SERVER_CONFIG) {
sshPublisher(
continueOnError: false,
failOnError: true,
publishers:[
sshPublisherDesc(
configName: "${SERVER_CONFIG}",
verbose: true,
transfers: [
sshTransfer(
sourceFiles: "build/libs/enjoy-delivery.jar, deploy.sh",
remoteDirectory: "project"
)
]
)
]
)
}
pipeline {
agent any
tools {
gradle 'Gradle 7.2'
}
options {
timeout(time: 1, unit: 'HOURS')
}
environment {
SOURCECODE_JENKINS_CREDENTIAL_ID = '...'
SOURCE_CODE_URL = 'https://github.com/f-lab-edu/enjoy-delivery/'
RELEASE_BRANCH = 'develop'
SERVER_LIST = 'server1,server2'
}
stages {
stage('clone') {
steps {
git url: "$SOURCE_CODE_URL",
branch: "$RELEASE_BRANCH",
credentialsId: "$SOURCECODE_JENKINS_CREDENTIAL_ID"
sh "ls -al"
}
}
stage('backend build') {
steps {
sh "pwd"
sh "gradle clean test"
echo "build.."
sh "gradle build -s"
}
}
stage('server deploy') {
steps {
echo "deploy"
echo "${SERVER_LIST}"
script {
SERVER_LIST.tokenize(',').each {
echo "SERVER: ${it}"
ssh_publisher("${it}")
sh "chmod +x ./deploy.sh"
sh "./deploy.sh"
}
}
}
}
}
}
작성한 젠킨스 파이프라인입니다. 다음과 같은 과정을 거칩니다.
(public over SSH 플러그인을 사용해 원격 서버 2개를 server1, server2로 미리 등록했습니다.)
#!/bin/bash
REPOSITORY=build/libs
PROJECT_NAME=enjoy-delivery
echo "> 현재 구동중인 애플리케이션 pid 확인"
CURRENT_PID=$(pgrep -f ${PROJECT_NAME}*.jar)
echo "현재 구동 중인 애플리케이션 pid: $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then
echo "> 현재 구동 중인 애플리케이션이 없으므로 종료하지 않습니다."
else
echo "> kill -15 $CURRENT_PID"
kill -15 $CURRENT_PID
sleep 5
fi
echo "> 새 애플리케이션 배포"
JAR_NAME=$(ls $REPOSITORY | grep ${PROJECT_NAME} | tail -n 1)
echo "> JAR Name: $JAR_NAME"
nohup java -jar $REPOSITORY/$JAR_NAME 2>&1 &
deploy.sh은 현재 구동중인 어플리케이션이 있으면 종료하고, 새로운 애플리케이션을 백그라운드로 배포합니다.
실제로 사용해본 결과 코드를 합칠 때 오류를 빨리 알아챌 수 있어서 편리하고, 자동 배포가 가능해서 매우 편리했습니다. 번거로운 통합 오류를 찾거나 배포 과정을 거치지 않게 되어 문제를 해결할 수 있게 되었습니다.