728x90
반응형

CI / CD

  • CI (Continuous Integration): 지속적 통합
    • 코드에 대한 통합
    • 다수의 개발자들의 코드 베이스를 계속해서 통합한다는 의미
    • 즉 여러 개발자들의 코드를 빠르게 배포함을 의미
  • CD (Continuous Deployment): 지속적 배포
    • 코드 베이스를 사용자가 사용하는 환경에 코드 베이스 배포를 자동화하는 것
  • 즉 CI/CD는 여러명의 개발자들이 개발 환경을 통합하여 사용자가 사용하는 환경에 전달하는 모든 일련의 과정들을 자동화하는 것
    • 여기에는 코드 빌드, 테스트, 배포가 포함됨

젠킨스?

  • Java Runtime Environment에서 동작
    • 플러그인 사용하여 자동화 작업 파이프라인을 설계
    • 파이프라인 마저도 플러그인의 일부
  • Credentials 플러그인을 사용해서 민감정보를 보관

젠킨스 파이프라인

  • Section - 가장 큰 개념
    • Agent Section
      • 오케스트레이션 처럼 slave node에 일 처리 지정
    • Post Section
      • 스테이지가 끝난 이후의 결과에 따른 후속 조치
      • 즉 작업 결과에 따른 행동 조치
    • Stage Section - 카테고리
      • 어떠한 일을 처리할 것인지 stage를 정의
      • Dockerfile의 스테이지를 정의하는 것과 같음
    • Step Section - 카테고리 내부의 동작들
      • 한 스테이지 안에서의 단계
      • 여러 작업들 실행 가능

Declaratives

  • Environment, Stage, Options, Parameters, Triggers, When
  • 각 Stage 안에서 어떠한 일을 할 것인지 정의
  • Environment
    • 환경변수들 정의
  • Parameter
    • 파이프라인 실행시 파라미터를 받음
  • Triggers
    • 파이프라인이 실행되는 트리거 정의
    • 예시: 특정 브랜치에 머지가 감지되었을 때
  • When
    • 수행되는 조건문
    • 예시: 환경변수 BUILD_BRANCH가 dev일 때

예시

  • 깃허브 특정 레포 dev(개발)/main(운영) 브랜치에 머지 인식
    • 미리 Credentials에 등록해 둔 각각 환경에 따라 다른 환경 변수 로드
    • 각각 환경에 따른 이미지 명으로 도커 이미지 빌드
    • 개인 도커 레지스트리에 이미지 푸시
    • 각각 개발/운영 서버에 배포할 서비스 경로 생성
      • docker-compose.yml 파일 복사
      • 빌드하여 푸시한 이미지 가져와서 컨테이너 구동
// 브랜치별 배포 위치

def getDeployTargets(envName) {

targets = [:]



// dev 브랜치

targets['prod'] = [[

    COMPOSE_ENV: 'dev',

    SSH_MODE: 'KEYONLY',

    SSH_IP: '<Server IP>',

    SSH_KEY_ID: 'Key Id From Jenkins Credentials',

    COPY_DIR: '도커 컴포즈 파일 배포할 서버 상의 경로'

]]

    return targets[envName]
}



// 브랜치별 환경 정보

def getBuildBranch(branchName) {
    branches = [
        'origin/main': 'prod',
    ]

    return branches[branchName]
}



pipeline {
    agent any

    //환경변수
    environment {

        //브랜치 선택

        BUILD_BRANCH = getBuildBranch(env.GIT_BRANCH)

        // 도커 설정
        DOCKER_IMAGE = ''
        DOCKER_IMAGE_NAME = '빌드 할 도커 이미지 명'

        // Git, Docker private 레지스트리 로그인 정보 설정    
        GIT_KEY_ID = '깃허브 인증 SSH 키 ID - Credentials에서 관리;'
        REGISTRY_LOGIN_INFO_ID = 'donghquinn_registry'
    }



                        stages {

                            stage('체크아웃') {
                                steps {
                                    echo "작업 브랜치: ${env.GIT_BRANCH}"

                                    git branch: BUILD_BRANCH, credentialsId: GIT_KEY_ID, url: 'git@github.com:donghquinn/<레포 명>.git'

                            }

                        }



                        stage('도커 이미지 빌드') {
                            steps {
                                script {

                                    // 브랜치에 따라 이미지 이름 변경
                                    DOCKER_IMAGE = docker.build(DOCKER_IMAGE_NAME + '-' + BUILD_BRANCH)

                                }

                                echo "Built: ${DOCKER_IMAGE_NAME}-${BUILD_BRANCH}"

                            }
                        }



                        stage('도커 이미지 Push') {
                            steps {
                                script {
                                    // 개발서버 내부 Docker private 레지스트리에 업로드
                                    docker.withRegistry('https://registry.donghyuns.com', REGISTRY_LOGIN_INFO_ID) {

                                    DOCKER_IMAGE.push(env.BUILD_NUMBER)
                                    DOCKER_IMAGE.push('latest')
                                }

                            }
                            echo "Pushed: ${DOCKER_IMAGE_NAME}-${BUILD_BRANCH}:${env.BUILD_NUMBER}"

                            }

                        }



                        stage('도커 컨테이너 배포') {

                        steps {

                            script {

                                def deployTargets = getDeployTargets(BUILD_BRANCH)

                                def deployments = [:]



                                // 배포 타깃별로 병렬 배포

                                for (item in deployTargets) {

                                def target = item



                                deployments["TARGET-${BUILD_BRANCH}"] = {

                                def remote = [:]
                                remote.name = target.SSH_IP
                                remote.host = target.SSH_IP
                                remote.allowAnyHosts = true

                                if (target.SSH_MODE == 'KEYONLY') {

                                    withCredentials([

                                    // DOTENV 파일과 SSH KEY를 가져옴
                                    sshUserPrivateKey(credentialsId: target.SSH_KEY_ID, keyFileVariable: 'SSH_PRIVATE_KEY', usernameVariable: 'USERNAME')
                                    ]) {

                                    // 가져온 키로 ssh 정보 설정
                                    remote.user = USERNAME

                                    remote.identityFile = SSH_PRIVATE_KEY

                                    sshCommand remote: remote, command: """
                                        mkdir -p ${target.COPY_DIR}-${BUILD_BRANCH}/
                                    """

                                    // docker compose 파일 전송
                                    sshPut remote: remote, from: "docker-compose.${BUILD_BRANCH}.yml", into: "${target.COPY_DIR}-${BUILD_BRANCH}/docker-compose.yml", failOnError: 'true'

                                    // 각 상황에 맞는 .env.* 파일 전송

                                   // 각 상황에 맞는 .env.* 파일 전송
                                  sshPut remote: remote, from: DOTENV, into: "${target.COPY_DIR}/.env", failOnError: 'true'

                                    // 도커 이미지 Pull 및 재시작
                                    sshCommand remote: remote, command: """
                                        cd ${target.COPY_DIR}-${BUILD_BRANCH}/
                                        BUILD_BRANCH=${BUILD_BRANCH} docker compose pull
                                        BUILD_BRANCH=${BUILD_BRANCH} docker compose up -d
                                    """
                                }
                            }
                        }

                        parallel deployments

                    }

                }

            }

        }

    }

}
728x90
반응형

'데브옵스 devOps > Jenkins' 카테고리의 다른 글

[Jenkins] 실제 CI/CD 배포 세팅  (2) 2024.10.29
728x90
반응형

무중단 배포 전략이란 무엇일까? 각 전략에 대해 개인적 명칭을 붙여봤다.

무중단 배포란?

무중단 배포란 서비스의 중단 없이 새로운 버전을 배포하는 것을 의미한다. 이용자의 서비스 이용에 지장을 주지 않는 것과 동시에 새로운 버전을 배포하는 것이 핵심인 것이다.

이를 위해선 로드 밸런서와 두 대 이상의 서버가 필요하다.

Blue/Green 배포 - 단순 배포 전략?

이전 버전 서버와 새로운 버전 서버를 두고,
공통 로드 밸런서를 통해 새로운 버전을 배포하는 전략이다.

  • Blue 서버: 이전 환경 서버
  • Green 서버: 새로운 배포 환경

Blue/Green 전략

장점

트래픽을 새로운 버전쪽으로 옮기기 때문에 호환성 문제가 발생하지 않는다.

단점

실제 운영에 필요한 리소스보다 2배의 리소스를 확보해야 한다.(서버가 두 대이고, 일질적인 트래픽이 통하는 서버는 이전 환경이기 때문)

Rolling 전략 - 순차 배포 전략?

운영중인 복수개의 인스턴스들을 하나씩 돌아가며(Rolling) 로드 밸런서로부터 트래픽을 끊고 새로운 버전 배포를 하는 전략이다.
즉, 새로운 버전 배포가 완료가 되면 다시 로드 밸런서에 연결을 하고, 그 다음 인스턴스를 로드 밸런서로부터 끊고 새로운 버전을 배포하는 것을 반복하는 것이다.

장점

  • 많은 서버 자원을 배포하지 않아도 무중단 배포 가능
  • 인스턴스마다 차례로 돌아가며 배포를 진행하므로, 배포로 인한 위험이 줄어든다.

단점

  • 새 버전을 배포할 때마다 서비스 중인 인스턴스의 개수가 줄어, 서버 부담이 증가한다.
  • 배포가 진행되는 동안 새로운 버전과 이전 버전의 호환성 문제가 발생할 수 있다.

Canary 배포 - 위험 감지 전략?


Canary 전략

이전 버전과 새로운 버전이 동시에 가동되는 방식으로, 새 버전 인스턴스는 일부 사용자에게만 서비스 하는 방식이다.
새 버전이 정상적으로 작동함을 확인하면, 전체 트래픽을 새 버전으로 전환한다.
오류가 발생한다면, 일부 사용자에게만 발생하므로 오류 방지에 효과적이다.

참고 (개인적 의견)

Canary 전략이라는 이름이 붙은 이유는 탄광에서 위험 감지용으로 데려가던 것에서 유래한 것 같다.

장점

  • 사용자 테스트와 무중단 배포가 동시에 가능함
  • 새로운 버전으로 인한 위험을 감소시킬 수 있다.

단점

  • Rolling 전략과 마찬가지로 이전과 새로운 버전이 동시에 존재하므로 호환성 문제가 발생 가능하다.

참고 링크

728x90
반응형

'데브옵스 devOps > Server' 카테고리의 다른 글

[파일전송] Rsync  (0) 2024.12.11
728x90
반응형

_Async/Await_

 

비동기란 무엇일까?

비동기

비동기는 하나의 동작이 완료되는 것을 기다리지 않고, 동시에 다른 작업을 처리하는 방식이다.

 

자바스크립트는 싱글 스레드 언어이기 때문에 필수 불가결적으로 비동기 함수를 선택하게 되었다.

하나의 스레드를 가지고 다른 작업이 마칠때까지 대기해야 한다면, 작업이 너무나도 느려지기 때문이다.

비동기 처리를 지원하는 방식으로는 Promise와 async/await이 있다.

 

예시

비동기 함수는 에어프라이어에 음식 넣고 돌린 후, 다른 재료를 손질하면서 틈틈이 에어프라이어를 체크하는 것과 유사하다.

에어프라이어가 음식을 조리하는 것이 완료될 때 까지 기다리는 것이 아닌, 동시에 다른 작업을 진행하는 것이다.

 

동기

동기는 하나의 동작이 완료된 후에 다음 동작으로 실행하는 순차적 방식이다.

 

개인적인 헷갈림 정리

처음에 비동기와 동기의 개념을 반대로 생각하고 있었다.

오히려 동기를 "병렬 처리"와 개념적으로 혼동하고 있었던 것 같다.

 

동기는 "순차적", 비동기는 "동시에"라고 외워야 겠다.

 

참고 링크

- 주니어 웹 개발자가 알아야 할 "비동기 통신" (https://yozm.wishket.com/magazine/detail/1982/)

728x90
반응형

'백엔드 Backend' 카테고리의 다른 글

[결합도/응집도] 결합도와 응집도란  (0) 2024.10.24
악성코드 종류  (0) 2024.10.16
SOLID 원칙  (0) 2024.10.08
[백엔드] REST API 란?  (0) 2024.09.27
[백엔드] gRPC란  (3) 2024.09.26

+ Recent posts