CI\CD
Github, Jenkins, Docker를 활용한 CI/CD - 3
KJihun
2025. 5. 19. 09:20
728x90
이전 글에서 CI를 통해 Docker-compose.xml, dockerfiles 및 .jar file 이 jenkins 아카이브에 저장되었었다.
아카이브에 저장된 데이터들을 토대로 CD를 구현하기 위해 새로운 pipeline을 만든다.
1. 배포할 .jar 파일명
2. public IP 명
과금문제로 인해 IP가 바뀌게 되므로 이 두가지 값을 매개변수로 지정하여 사용할 예정이다.
.jar
매개변수 명 : 이후에 작성할 script에서 호출하기 위한 이름
Default Value : 초기 파라미터 값을 지정해준다. 잦은 변경이 일어나지 않을경우 유용하다
설명 : 해당 파라미터가 어떤 파라미터인지 알아볼 수 있도록 설명하기 위한 부분
위와 동일하지만 public IP는 자주 바뀌기 때문에 default Value를 지정해주지 않았다.
매개변수가 더 필요할 경우 매개변수 추가를 클릭하여 추가할 수 있다.
Script 전체코드
pipeline {
agent any
parameters {
string(name: 'JAR_FILE_NAME', defaultValue: 'build/libs/demo-0.0.1-SNAPSHOT.jar', description: '복사된 .jar 파일명을 입력하세요 (예: build/libs/demo-0.0.1-SNAPSHOT.jar)')
string(name: 'EC2_PUBLIC_IP', defaultValue: '', description: '배포 대상 EC2 퍼블릭 IP를 입력하세요 (예: 13.209.65.95)')
}
environment {
EC2_USER = 'ubuntu'
REMOTE_DIR = '/home/ubuntu/deploy'
REMOTE_JAR_PATH = '/home/ubuntu/deploy/app.jar'
PROPERTIES_CREDENTIAL_ID = 'properties'
PROPERTIES_FILE_NAME = 'application.properties'
}
stages {
stage('Copy Artifact from CI') {
steps {
copyArtifacts(
projectName: 'CI',
selector: [$class: 'StatusBuildSelector', stable: false],
filter: "${params.JAR_FILE_NAME}",
target: '.'
)
copyArtifacts(
projectName: 'CI',
selector: [$class: 'StatusBuildSelector', stable: false],
filter: 'docker-artifacts.tar.gz',
target: '.'
)
}
}
stage('Copy Secret Properties') {
steps {
withCredentials([file(credentialsId: "${env.PROPERTIES_CREDENTIAL_ID}", variable: 'SECRET_PROPS')]) {
sh '''
cp $SECRET_PROPS ${PROPERTIES_FILE_NAME}
'''
}
}
}
stage('Fix Artifact Permission') {
steps {
echo '🔧 docker-artifacts.tar.gz 파일 권한 및 소유자 변경'
sh '''
chown jenkins:jenkins docker-artifacts.tar.gz || true
chmod 644 docker-artifacts.tar.gz || true
'''
}
}
stage('Validate Input') {
steps {
script {
if (params.JAR_FILE_NAME == '') {
error '❌ JAR_FILE_NAME 파라미터가 비어있습니다.'
}
if (!fileExists(params.JAR_FILE_NAME)) {
error "❌ 파일 '${params.JAR_FILE_NAME}'이 복사되지 않았습니다."
}
if (!fileExists('docker-artifacts.tar.gz')) {
error "❌ docker-artifacts.tar.gz 파일이 복사되지 않았습니다."
}
if (!fileExists(PROPERTIES_FILE_NAME)) {
error "❌ ${PROPERTIES_FILE_NAME} 파일이 존재하지 않습니다."
}
if (params.EC2_PUBLIC_IP == '') {
error '❌ EC2_PUBLIC_IP 파라미터가 비어있습니다.'
}
}
}
}
stage('Deploy to EC2') {
steps {
sshagent(['EC2_pem']) {
sh """
scp -o StrictHostKeyChecking=no docker-artifacts.tar.gz ${EC2_USER}@${params.EC2_PUBLIC_IP}:${REMOTE_DIR}/
scp -o StrictHostKeyChecking=no ${params.JAR_FILE_NAME} ${EC2_USER}@${params.EC2_PUBLIC_IP}:${REMOTE_JAR_PATH}
scp -o StrictHostKeyChecking=no ${PROPERTIES_FILE_NAME} ${EC2_USER}@${params.EC2_PUBLIC_IP}:${REMOTE_DIR}/
printf '%s\n' '
set -xe
cd ${REMOTE_DIR} || { echo "❌ 디렉토리 이동 실패: ${REMOTE_DIR}"; exit 1; }
echo "📍 docker-artifacts.tar.gz 압축 해제 중..."
tar -xvzf docker-artifacts.tar.gz || { echo "❌ 압축 해제 실패"; exit 1; }
echo "📍 Java 설치 확인 중..."
if ! command -v java &> /dev/null; then
echo "⚠ Java 미설치 감지. 설치 시작..."
sudo apt update && sudo apt install -y openjdk-17-jre-headless || { echo "❌ Java 설치 실패"; exit 1; }
echo "✅ Java 설치 완료"
else
echo "✅ Java 설치되어 있음"
fi
echo "📍 Docker 설치 확인 중..."
if ! command -v docker &> /dev/null; then
echo "⚠ Docker 미설치 감지. 설치 시작..."
sudo apt update && sudo apt install -y docker.io || { echo "❌ Docker 설치 실패"; exit 1; }
sudo systemctl start docker || { echo "❌ Docker 시작 실패"; exit 1; }
sudo systemctl enable docker || { echo "❌ Docker 자동시작 설정 실패"; exit 1; }
sudo usermod -aG docker ubuntu || { echo "⚠ Docker 그룹 추가 실패"; }
echo "✅ Docker 설치 완료"
else
echo "✅ Docker 설치되어 있음"
fi
echo "📍 Docker Compose 설치 확인 중..."
if ! command -v docker-compose &> /dev/null; then
echo "⚠ Docker Compose 미설치 감지. 설치 시작..."
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-\$(uname -s)-\$(uname -m)" -o /usr/local/bin/docker-compose || { echo "❌ Docker Compose 다운로드 실패"; exit 1; }
sudo chmod +x /usr/local/bin/docker-compose || { echo "❌ Docker Compose 권한 변경 실패"; exit 1; }
echo "✅ Docker Compose 설치 완료"
else
echo "✅ Docker Compose 설치되어 있음"
fi
echo "🔁 docker-compose 실행"
if [ -f docker-compose.yml ]; then
sudo docker-compose down || echo "⚠ docker-compose down 실패 (무시)"
sudo docker-compose up -d || echo "⚠ ddocker-compose up 실패 (무시)"
echo "✅ docker-compose 실행 완료"
else
echo "ℹ docker-compose.yml 파일이 없어 실행하지 않음음"
fi
if [ ! -f ${REMOTE_DIR}/${PROPERTIES_FILE_NAME} ]; then
echo "❌ ${PROPERTIES_FILE_NAME} 파일이 누락됨"
exit 1
fi
CURRENT_PID=\$(pgrep -f app.jar || true)
if [ -z "\$CURRENT_PID" ]; then
echo "ℹ 실행 중인 app.jar 프로세스가 없습니다."
else
echo "🔻 app.jar 실행 PID: \$CURRENT_PID"
kill -15 \$CURRENT_PID || true
sleep 5
if pgrep -f app.jar > /dev/null; then
echo "⚠ 정상 종료 실패, 강제 종료"
kill -9 \$CURRENT_PID || true
fi
echo "✅ 기존 app.jar 프로세스 종료 완료"
fi
echo "▶ 새 JAR 실행 중..."
nohup java -jar ${REMOTE_JAR_PATH} \\
--spring.config.location=${REMOTE_DIR}/${PROPERTIES_FILE_NAME} \\
> ${REMOTE_DIR}/app.log 2>&1 &
sleep 10
if ! pgrep -f "app.jar" > /dev/null; then
echo "❌ JAR 프로세스 실행 실패"
cat ${REMOTE_DIR}/app.log || echo "⚠ 로그 없음"
echo "⚠ JAR 실행 실패 무시하고 진행"
else
echo "✅ JAR 프로세스 정상 실행 중"
fi
' | ssh -o StrictHostKeyChecking=no ${EC2_USER}@${params.EC2_PUBLIC_IP} bash -xe
"""
}
}
}
stage('Check App Status') {
steps {
sshagent(['EC2_pem']) {
sh """
ssh -o StrictHostKeyChecking=no ${EC2_USER}@${params.EC2_PUBLIC_IP} bash -c '
if pgrep -f "app.jar" > /dev/null; then
echo "✅ 앱 실행 중"
else
echo "❌ 앱 실행 실패"
echo "=== app.log 내용 ==="
cat ${REMOTE_DIR}/app.log || echo "⚠ 로그 없음"
echo "=== 끝 ==="
exit 1
fi
'
"""
}
}
post {
always {
echo 'Check App Status stage finished.'
}
}
}
}
post {
success {
echo '🚀 배포 완료'
}
failure {
echo '❌ 배포 실패'
}
}
}