무중단 배포란?
무중단 배포(Zero-Downtime Deployment) : SW 업데이트나 애플리케이션의 새로운 버전을 배포할 때, 서비스의 가용성을 유지하여 사용자에게 영향을 주지 않고 배포하는 방법을 의미한다.
무중단 배포의 주요 개념 )
- 가용성 유지 : 배포 과정 동안 애플리케이션이 계속해서 정상적으로 운영되어 사용자에게 서비스 중단을 일으키지 않는다.
- 점진적 배포 : 새로운 버전의 애플리케이션을 점진적으로 릴리즈하여 전체 시스템에 변경 사항을 적용한다.
- 롤백 가능성 : 문제가 발생할 경우 빠르게 이전 버전으로 롤백할 수 있다.
무중단 배포 전략 )
블루-그린 배포 (Blue-Green Deployment)
개념 : 두 개의 환경(블루와 그린)을 유지하며, 다른 한 쪽에 새 버전을 배포하는 방식.
절차 :
- 새로운 버전을 그린 환경에 배포.
- 그린 환경에서 테스트를 수행.
- 문제가 없으면 트래픽을 블루에서 그린으로 전환.
- 그린 환경이 정상적으로 운영되면 블루 환경을 업데이트하거나 유지보수에 사용.
카나리 배포 (Canary Deployment)
개념 : 새로운 버전을 소수의 사용자에게 먼저 배포하여 테스트하고, 점진적으로 모든 사용자에게 확장하는 방식
절차 :
- 새로운 버전을 소수의 인스턴스에 배포.
- 해당 인스턴스에 소수의 트래픽을 보내어 모니터링.
- 문제가 없으면 점진적으로 배포 범위를 확대.
- 전체 트래픽을 새로운 버전으로 전환.
롤링 배포 (Rolling Deployment)
개념 : 전체 시스템의 일부 인스턴스를 새로운 버전으로 순차적으로 교체하는 방식
절차 :
- 새로운 버전을 하나 또는 소수의 인스턴스에 배포.
- 배포된 인스턴스가 정상적으로 작동하는지 확인.
- 확인 후 나머지 인스턴스를 순차적으로 교체.
- 모든 인스턴스가 새로운 버전으로 교체될 때까지 반복.
블루-그린 배포
def deployApp(targetServerIp, targetServerPort, jarPath, deployPath) {
def runAppCommand = "nohup java -jar $deployPath/inclass-spring-security-0.0.1-SNAPSHOT.jar > nohup.log 2>&1 &"
def checkLogCommand = "grep -q 'Started inclass-spring-security in' $deployPath/nohup.log"
def checkProcessCommand = "pgrep -f inclass-spring-security-0.0.1-SNAPSHOT.jar"
// 서버에 파일을 SCP로 전송
sshagent(['KDT_Project2_AWS']) { // 이 부분에서 'KDT_Project2_AWS'는 Jenkins에 등록한 Credential ID (AWS에서 발급한 키 페어)
// 기존 애플리케이션 프로세스를 종료합니다.
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp 'ps -ef | grep java | grep -v grep | awk \'{print \$2}\' | sudo xargs kill -9 || echo \"No process found\"'"
// 새로운 JAR 파일을 배포하고 애플리케이션을 시작합니다.
sh "scp -o StrictHostKeyChecking=no -P $targetServerPort $jarPath ubuntu@$targetServerIp:$deployPath/"
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp '$runAppCommand'"
}
}
pipeline {
tools {
gradle "GRADLE" // Jenkins에서 설정한 Gradle의 이름
}
agent any
environment {
BLUE_PATH = '/home/ubuntu/blue'
GREEN_PATH = '/home/ubuntu/green'
DEPLOY_PATH = ''
CURRENT_ENV_PATH = '/home/ubuntu/current_env'
}
stages {
stage('Clone') {
steps { // main인지 master인지 확인하고 Pipeline Syntax 돌려서 이중으로 확인해보기
git 'https://github.com/lcl1380/KDT_Project2.git'
}
}
stage('Build') {
steps {
sh 'chmod +x ./gradlew'
sh './gradlew clean build'
}
}
stage('Test') {
steps {
script {
sh './gradlew test'
}
}
}
stage('Blue-Green Deployment Setup') {
steps {
script {
// 현재 사용 중인 환경(블루 또는 그린)을 확인합니다.
def currentEnv = sh(script: "ssh -o StrictHostKeyChecking=no -p 22 ubuntu@10.0.2.207 'cat $CURRENT_ENV_PATH'", returnStdout: true).trim()
// 현재 환경이 블루이면 그린 환경에 배포하고, 그린 환경이 사용 중이면 블루 환경에 배포합니다.
if (currentEnv == 'blue') {
env.DEPLOY_PATH = env.GREEN_PATH
} else {
env.DEPLOY_PATH = env.BLUE_PATH
}
}
}
}
stage('Deploy to A-Private Instances') {
steps {
script {
deployApp('10.0.2.207', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
deployApp('10.0.2.136', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
}
}
}
stage('Deploy to B-Private Instances') {
steps {
script {
deployApp('10.0.5.253', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
deployApp('10.0.5.22', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
}
}
}
stage('Switch Traffic to New Version') {
steps {
script {
// 트래픽을 새로운 환경으로 전환합니다.
def newEnv = (env.DEPLOY_PATH == env.BLUE_PATH) ? 'blue' : 'green'
sh "ssh -o StrictHostKeyChecking=no -p 22 ubuntu@10.0.2.207 'echo $newEnv > $CURRENT_ENV_PATH'"
// 로드 밸런서 또는 DNS 설정을 업데이트하여 새로운 환경으로 트래픽을 전환합니다.
// (이 부분은 특정 설정에 따라 다르므로, 예시로 주석 처리합니다)
// sh "ssh -o StrictHostKeyChecking=no -p 22 ubuntu@load_balancer 'update_lb_config $newEnv'"
}
}
}
}
post {
success {
echo "배포가 성공적으로 완료되었습니다."
}
failure {
script {
echo "배포 중 실패가 발생했습니다. 롤백을 시작합니다."
// 롤백을 위해 현재 환경을 확인합니다.
def currentEnv = sh(script: "ssh -o StrictHostKeyChecking=no -p 22 ubuntu@10.0.2.207 'cat $CURRENT_ENV_PATH'", returnStdout: true).trim()
def rollbackEnv = (currentEnv == 'blue') ? 'green' : 'blue'
env.DEPLOY_PATH = (rollbackEnv == 'blue') ? env.BLUE_PATH : env.GREEN_PATH
// 이전 버전으로 롤백합니다.
deployApp('10.0.2.207', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
deployApp('10.0.2.136', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
deployApp('10.0.5.253', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
deployApp('10.0.5.22', 22, 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar', env.DEPLOY_PATH)
// 트래픽을 롤백된 환경으로 전환합니다.
sh "ssh -o StrictHostKeyChecking=no -p 22 ubuntu@10.0.2.207 'echo $rollbackEnv > $CURRENT_ENV_PATH'"
// 로드 밸런서 또는 DNS 설정을 업데이트하여 롤백된 환경으로 트래픽을 전환합니다.
// (이 부분은 특정 설정에 따라 다르므로, 예시로 주석 처리합니다)
// sh "ssh -o StrictHostKeyChecking=no -p 22 ubuntu@load_balancer 'update_lb_config $rollbackEnv'"
}
echo "롤백이 완료되었습니다."
}
}
}
- Blue-Green Deployment Setup 단계:
- 현재 사용 중인 환경(블루 또는 그린)을 확인, 이를 위해 SSH 명령어를 사용하여 현재 환경 상태를 읽어옴
- 현재 사용 중인 환경(블루 또는 그린)을 확인하여 DEPLOY_PATH를 설정
- 현재 환경이 블루이면 그린 환경에 배포하고, 그린 환경이 사용 중이면 블루 환경에 배포함
- Deploy to A-Private Instances 및 B-Private Instances 단계:
- deployApp 함수에 새로운 매개변수인 deployPath를 추가하여, 선택된 배포 경로에 애플리케이션을 배포함
- deployApp 함수를 호출하여 설정된 DEPLOY_PATH에 배포함
- Switch Traffic to New Version 단계:
- 배포가 완료되면 트래픽을 새로운 환경으로 전환함
이를 위해 현재 환경 상태 파일을 업데이트하고, 필요에 따라 로드 밸런서 또는 DNS 설정을 업데이트함
- 배포가 완료되면 트래픽을 새로운 환경으로 전환함
- post 섹션에 롤백 로직 추가:
- 배포가 실패하면 롤백을 시작함
- 현재 환경을 확인하고 롤백할 환경을 설정함
- 이전 버전으로 롤백하고, 트래픽을 롤백된 환경으로 전환함
롤링 배포
def deployApp(targetServerIp, targetServerPort) {
def jarPath = 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar'
def deployPath = '/home/ubuntu'
def runAppCommand = "nohup java -jar $deployPath/inclass-spring-security-0.0.1-SNAPSHOT.jar > nohup.log 2>&1 &"
def checkLogCommand = "grep -q 'Started inclass-spring-security in' $deployPath/nohup.log"
def checkProcessCommand = "pgrep -f inclass-spring-security-0.0.1-SNAPSHOT.jar"
// 서버에 파일을 SCP로 전송
sshagent(['KDT_Project2_AWS']) { // 이 부분에서 'KDT_Project2_AWS'는 Jenkins에 등록한 Credential ID (AWS에서 발급한 키 페어)
// 기존 애플리케이션 프로세스 종료
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp 'ps -ef | grep java | grep -v grep | awk \'{print \$2}\' | sudo xargs kill -9 || echo \"No process found\"'"
// 새로운 JAR 파일 배포 및 애플리케이션 시작
sh "scp -o StrictHostKeyChecking=no -P $targetServerPort $jarPath ubuntu@$targetServerIp:$deployPath/"
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp '$runAppCommand'"
}
}
def rollbackApp(targetServerIp, targetServerPort) {
def previousJarPath = 'build/libs/previous-inclass-spring-security-0.0.1-SNAPSHOT.jar'
def deployPath = '/home/ubuntu'
def runAppCommand = "nohup java -jar $deployPath/previous-inclass-spring-security-0.0.1-SNAPSHOT.jar > nohup.log 2>&1 &"
// 이전 버전의 JAR 파일을 배포하고 애플리케이션을 시작
sshagent(['KDT_Project2_AWS']) { // 이 부분에서 'KDT_Project2_AWS'는 Jenkins에 등록한 Credential ID (AWS에서 발급한 키 페어)
// 기존 애플리케이션 프로세스 종료
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp 'ps -ef | grep java | grep -v grep | awk \'{print \$2}\' | sudo xargs kill -9 || echo \"No process found\"'"
// 이전 버전 JAR 파일 배포 및 애플리케이션 시작
sh "scp -o StrictHostKeyChecking=no -P $targetServerPort $previousJarPath ubuntu@$targetServerIp:$deployPath/"
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp '$runAppCommand'"
}
}
pipeline {
tools {
gradle "GRADLE" // Jenkins에서 설정한 Gradle의 이름
}
agent any
stages {
stage('Clone') {
steps { // main인지 master인지 확인하고 Pipeline Syntax 돌려서 이중으로 확인해보기
git 'https://github.com/lcl1380/KDT_Project2.git'
}
}
stage('Build') {
steps {
sh 'chmod +x ./gradlew'
sh './gradlew clean build'
}
}
stage('Test') {
steps {
script {
sh './gradlew test'
}
}
}
stage('Deploy A-Private Instance') {
steps {
script {
try {
// 첫 번째 인스턴스 배포
deployApp('10.0.2.207', 22)
// 두 번째 인스턴스 배포
deployApp('10.0.2.136', 22)
} catch (Exception e) {
// 오류 발생 시 롤백
rollbackApp('10.0.2.207', 22)
rollbackApp('10.0.2.136', 22)
throw e
}
}
}
}
stage('Deploy B-Private Instance') {
steps {
script {
try {
// 첫 번째 인스턴스 배포
deployApp('10.0.5.253', 22)
// 두 번째 인스턴스 배포
deployApp('10.0.5.22', 22)
} catch (Exception e) {
// 오류 발생 시 롤백
rollbackApp('10.0.5.253', 22)
rollbackApp('10.0.5.22', 22)
throw e
}
}
}
}
}
post {
success {
echo "배포가 성공적으로 완료되었습니다."
}
failure {
echo "배포에 실패했습니다. 롤백을 수행했습니다."
}
}
}
- deployApp 함수: 새 버전의 애플리케이션을 서버에 배포하고 실행
- rollbackApp 함수: 이전 버전의 애플리케이션을 서버에 배포하고 실행
- stage('Clone'): Git 저장소에서 소스 코드를 클론
- stage('Build'): Gradle을 사용하여 애플리케이션을 빌드
- stage('Test'): 빌드된 애플리케이션을 테스트
- stage('Deploy A-Private Instance'): 첫 번째 프라이빗 인스턴스 그룹에 롤링 배포를 수행
- stage('Deploy B-Private Instance'): 두 번째 프라이빗 인스턴스 그룹에 롤링 배포를 수행
- post 블록: 파이프라인 실행 후 성공 또는 실패에 따라 메시지를 출력
원본 파이프라인 코드
def deployApp(targetServerIp, targetServerPort) {
def jarPath = 'build/libs/inclass-spring-security-0.0.1-SNAPSHOT.jar'
def deployPath = '/home/ubuntu'
def runAppCommand = "nohup java -jar $deployPath/inclass-spring-security-0.0.1-SNAPSHOT.jar > nohup.log 2>&1 &"
def checkLogCommand = "grep -q 'Started inclass-spring-security in' $deployPath/nohup.log"
def checkProcessCommand = "pgrep -f inclass-spring-security-0.0.1-SNAPSHOT.jar"
// 서버에 파일을 SCP로 전송
sshagent(['KDT_Project2_AWS']) { // 이 부분에서 'KDT_Project2_AWS'는 Jenkins에 등록한 Credential ID (AWS에서 발급한 키 페어)
// Terminate any existing application process
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp 'ps -ef | grep java | grep -v grep | awk \'{print \$2}\' | sudo xargs kill -9 || echo \"No process found\"'"
// Deploy the new JAR file and start the application
sh "scp -o StrictHostKeyChecking=no -P $targetServerPort $jarPath ubuntu@$targetServerIp:$deployPath/"
sh "ssh -o StrictHostKeyChecking=no -p $targetServerPort ubuntu@$targetServerIp '$runAppCommand'"
}
}
pipeline {
tools {
gradle "GRADLE" // Jenkins에서 설정한 Gradle의 이름
}
agent any
stages {
stage('Clone') {
steps { // main인지 master인지 확인하고 Pipeline Syntax 돌려서 이중으로 확인해보기
git 'https://github.com/lcl1380/KDT_Project2.git'
}
}
stage('Build') {
steps {
sh 'chmod +x ./gradlew'
sh './gradlew clean build'
}
}
stage('Test') {
steps {
script {
sh './gradlew test'
}
}
}
stage('Deploy A-Private Instance') {
steps {
script {
deployApp('10.0.2.207', 22)
deployApp('10.0.2.136', 22)
}
}
}
stage('Deploy B-Private Instance') {
steps {
script {
deployApp('10.0.5.253', 22)
deployApp('10.0.5.22', 22)
}
}
}
}
post {
success {
echo "This will run when the run finished successfully"
}
failure {
echo "This will run if failed"
}
}
}
띄우고 배포만 한 파이프라인 코드
'🌥️Cloud Study🌥️ > AWS' 카테고리의 다른 글
[ AWS ] 아틸러리를 이용한 부하 테스트 및 부하 분산 확인(I/O bound) (1) | 2024.07.23 |
---|---|
[ AWS Terraform ] Terraform 코드 실행 후 할 일 (0) | 2024.07.22 |
[ AWS-Terraform ] 테라폼을 이용한 아키텍처 구축 (0) | 2024.07.18 |
[ AWS ] 배포 파이프라인 만들기 (0) | 2024.07.17 |
[ AWS ] 보안그룹-인바운드 규칙 편집, 개별 인스턴스 설정 (0) | 2024.07.16 |