Jenkins pipeline 고도화
한 2년차에 회사에서 이슈가 하나 있었다.
그렇게 큰 회사는 아니다보니, 개발자가 직접 운영에 배포하고 있다는 문제점이 있었다.
그리고 실 운영중인 서비스에 대해 특정 배포시간대가 아닌 그냥 일반적으로 배포한다는 점이 있었다.
그래서 팀장님과 협의하고, 다른 팀과 협의하여 이렇게 바꿨었다.
개발 배포용 계정 공유
운영은 DevOps 에서만 진행 (아직도 안하고 있음.. 언제 할거니....?)
처음에 작성한 Pipeline과는 살짝 다르게, 간단하게 Slack에 Notification을 보내는 내용만 추가를 했었다.
pipeline {
agent any
environment {
GITLAB_CREDENTIALS_ID = 'UNKNOWN'
PROD_REMOTE_DIR = 'UNKNOWN'
BUILD_ARTIFACT = 'UNKNOWN'
DATE_FORMAT = 'yyyyMMddHHmmss'
}
stages {
stage('Start & Send Slack Notification') {
steps {
slackSend (channel: '#UNKNOWN',
color: 'warning',
message: "Jenkins started: Job ${env.JOB_NAME} [#${env.BUILD_NUMBER}] (${env.BUILD_URL})")
}
}
stage('Update to lastest commit') {
steps {
checkout changelog: false,
poll: false,
scm: scmGit(branches: [[name: '*/main']],
extensions: [],
userRemoteConfigs: [[
credentialsId: "${GITLAB_CREDENTIALS_ID}",
url: 'UNKNOWN'
]])
}
}
stage('Build') {
steps {
sh 'chmod 744 ./gradlew'
sh './gradlew clean build -x test'
}
}
stage('Transfer to Prod UNKNOWN Server') {
steps {
sh "scp -o ProxyJump=UNKNOWN ${BUILD_ARTIFACT} UNKNOWN:${PROD_REMOTE_DIR}"
}
}
stage('Deploy to Prod UNKNOWN Server') {
steps {
script {
def currentDate = new Date().format(DATE_FORMAT)
sh """
ssh -J UNKNOWN UNKNOWN '
cd ${PROD_REMOTE_DIR} &&
../stop.sh &&
mv ../UNKNOWN ../UNKNOWN_${currentDate} &&
mv ./UNKNOWN ../UNKNOWN &&
nohup ../start.sh > /dev/null 2>&1 &'
"""
}
}
}
}
post {
success {
slackSend (channel: '#UNKNOWN',
color: 'good',
message: "Jenkins Success: Job ${env.JOB_NAME} [#${env.BUILD_NUMBER}] (${env.BUILD_URL})")
}
failure {
slackSend (channel: '#UNKNOWN',
color: 'danger',
message: "Jenkins Failed: Job ${env.JOB_NAME} [#${env.BUILD_NUMBER}] (${env.BUILD_URL})")
}
}
}
순서는 이렇다.
1. Start & Send Slack Notification : 빌드 시작 알림 발송
2. Update to latest commit : GitLab에서 최신 Commit 내용을 pull & scmGit을 이용한 checkout
3. Build : chmod를 통해 gradlew 실행 권한 부여, 테스트 제외 (-x test)
4. Transfer to Prod UNKNOWN Server : scp를 사용해 빌드 된 파일을 UNKNOWN 서버로 전송 (ProxyJump로 Bastion 서버 이용)
5. Deploy to Prod UNKNOWN Server : 서버 종료 및 현재 날짜 기반 백업, 빌드 파일로 nohup Background 실행
6. post block : 빌드 성공 & 실패시 Slack 알림 발송
대충 이런식으로 알람을 받고 있다.
나름 흐름 자체는 깔끔한 것 같은데.. 에러 핸들링과 로깅이 조금 부족한 것 같아 수정해봤다.
sh 안에 내용은 이렇게 바꿔봤다.
sh """
set -e
ssh -J ${BASTION_HOST} ${PROD_SERVER} '
cd ${PROD_REMOTE_DIR} &&
echo "Stopping application..." &&
${REMOTE_DEPLOY_DIR}/stop.sh || echo "stop.sh failed" &&
if [ -f "${warFullPath}" ]; then
echo "Backing up current WAR to ${remoteBackupPath}" &&
mv "${warFullPath}" "${remoteBackupPath}"
fi &&
echo "Deploying new WAR..." &&
mv "${PROD_REMOTE_DIR}/${warFile}" "${REMOTE_DEPLOY_DIR}/${warFile}" &&
echo "Restarting app..." &&
nohup ${REMOTE_DEPLOY_DIR}/start.sh > /dev/null 2>&1 &
'
"""
뭔가 깔끔해진 느낌이 있기는 한데, environment가 지져분해 보이기는 한다.
다만, 다른 Pipeline을 추가하기가 쉬워진 부분은 있는 것 같다.
그리고 검색하다가 알게 된 내용으로 timestamp format 버그의 위험이 있다고 한다.
def currentDate = new Date().format(DATE_FORMAT, TimeZone.getTimeZone('Asia/Seoul'))
사실 처음에도 있었지만 서버 SSH에서 직접 실행해서 잊었었던 내용도 있었다.
scp -o StrictHostKeyChecking=no -o ProxyJump=UNKNOWN ...
StrictHostKeyChecking도 no로 추가했다.
나중에는 rollback 기능을 추가하고, Shared Library로 분리해서 재사용이 가능하게 만들어야 겠다.