[ AWS ] 파이프라인을 이용한 무중단 배포

2024. 7. 23. 18:02·🌥️Cloud Study🌥️/AWS

 

무중단 배포란?

무중단 배포(Zero-Downtime Deployment) : SW 업데이트나 애플리케이션의 새로운 버전을 배포할 때, 서비스의 가용성을 유지하여 사용자에게 영향을 주지 않고 배포하는 방법을 의미한다.

 

무중단 배포의 주요 개념 )

  1. 가용성 유지 : 배포 과정 동안 애플리케이션이 계속해서 정상적으로 운영되어 사용자에게 서비스 중단을 일으키지 않는다.
  2. 점진적 배포 : 새로운 버전의 애플리케이션을 점진적으로 릴리즈하여 전체 시스템에 변경 사항을 적용한다.
  3. 롤백 가능성 : 문제가 발생할 경우 빠르게 이전 버전으로 롤백할 수 있다.

 

무중단 배포 전략 )

블루-그린 배포 (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
'🌥️Cloud Study🌥️/AWS' 카테고리의 다른 글
  • [ AWS ] 아틸러리를 이용한 부하 테스트 및 부하 분산 확인(I/O bound)
  • [ AWS Terraform ] Terraform 코드 실행 후 할 일
  • [ AWS-Terraform ] 테라폼을 이용한 아키텍처 구축
  • [ AWS ] 배포 파이프라인 만들기
L_Chae
L_Chae
🎮😻🤓🖥✨
  • L_Chae
    Cherish
    L_Chae
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 🌥️Cloud Study🌥️
        • AWS
        • NHN
        • KT
        • Microsoft (Azure 900)
        • TroubleShooting
        • IaC
        • Etc
      • STUDY
        • English
        • Japanese
        • 개인정보보호 관련
      • 초등학교 AI 교육 진행
        • 2024학년도 1학년-2학년
        • 2025학년도 4학년
      • 2024 KDT 📕
        • Lecture
        • Study 📗
        • Study-JAVA
        • Project
        • etc
      • INTERLUDE ✦
        • 2022 SYSTEM STUDY
        • 2022 Winter Study
        • 2023 AutoMobility STUDY
        • 2023 Summer Study (CPPG)
        • 2023 Reversing STUDY
        • etc
      • Private🔒
        • MacBook 💻
        • Screenshot 🩷
        • Photo 🖼️
        • FFXIV - Backup 🎮
        • Tistory
      • PBL 📗
        • 논문 스터디
        • Backup
      • Project 🖤
        • 2022-2) 개인정보보호 소학회
        • 2022-2) winter GURU2 - iOS
        • 2023-1) PBL 3
        • 2023-2) PBL 4
        • 2024-1) SW-AI교육 실습 일지
      • CTF 🚩
        • 2022
        • 2023
      • News scrap 📰
        • 2022
        • 2023
      • 2022 공부 로그 🐯
      • 2023 공부 로그🐰
  • 블로그 메뉴

    • 글쓰기
    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    content collection
    deprecation warning
    N1
    JLPT
    멱등성
    ansible galaxy
    jlpt n1 도전기
    ansible
    JLPT N1
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
L_Chae
[ AWS ] 파이프라인을 이용한 무중단 배포
상단으로

티스토리툴바