[AWS/EC2] AWS EC2 Jenkins CI/CD

SihoonCho·2024년 3월 14일
0

[AWS/EC2] AWS EC2

목록 보기
5/6
post-thumbnail
post-custom-banner

※ 읽기에 앞서


본 시리즈는 작성자의 이해와 경험을 바탕으로 실습 위주의 설명을 기반으로 작성되었습니다.
실습 위주의 이해를 목표로 하기 때문에 다소 과장이 많고 생략된 부분이 많을 수 있습니다.
따라서, 이론적으로 미흡한 부분이 있을 수 있는 점에 대해 유의하시기 바랍니다.

또한, 본 시리즈는 ChatGPT의 도움을 받아 작성되었습니다.
수 차례의 질문을 통해 도출된 여러가지 다양한 방식의 코드를 종합하여
작성자의 이해와 경험을 바탕으로 가장 정석으로 생각되는 코드를 재정립하였습니다.

📌 Jenkins Plugin 설치

  • NodeJS, GitLab 플러그인 설치


📌 Jenkins GitLab 설정


📖 GitLab AccessToken 발급


📖 Jenkins Credential 설정


📖 Jenkins System 설정


📖 Jenkins Tools NodeJS 설정


📌 React Dockerfile 작성

  • /frontendDockerfile 작성
# Node.js 공식 이미지 사용. 버전 20.11.1, 경량화된 Alpine Linux 기반
FROM node:20.11.1-alpine

# 작업 디렉토리 설정. 컨테이너 내 앱의 기본 경로
WORKDIR /home/app

# 현재 디렉토리의 package.json과 package-lock.json 파일이 존재한다면
# 컨테이너의 작업 디렉토리로 package.json과 package-lock.json 복사
COPY package*.json ./

# package.json에 명시된 애플리케이션 의존성 설치
# package-lock.json이 있을 경우 더 빠르게 설치 가능
RUN npm install

# 현재 디렉토리의 모든 파일을 컨테이너의 작업 디렉토리로 복사
COPY . .

# React 애플리케이션 빌드
RUN npm run build

📌 SpringBoot Dockerfile 작성

  • /backendDockerfile 작성
# OpenJDK 17을 포함하는 경량화된 Alpine Linux 베이스 이미지 사용
FROM openjdk:17-jdk-alpine

# 컨테이너 내부의 작업 디렉토리를 /home/app로 설정
WORKDIR /home/app

# 호스트 시스템의 SpringBoot 애플리케이션 JAR 파일을 컨테이너 내부 작업 디렉토리로 복사
COPY build/libs/*.jar app.jar

# 컨테이너가 시작될 때 실행될 명령어 정의, 작업 디렉토리 /home/app/app.jar 파일 실행
ENTRYPOINT ["java","-jar","./app.jar"]

# 컨테이너의 8080 포트를 외부로 노출
EXPOSE 8080

📌 Jenkins Pipeline 설정


📖 Jenkins Credential 설정


📖 Pipeline 생성

초록색 박스로 표시된 GitLab webhook URL을 잘 기록해둔다.
초록색 박스로 표시된 고급 탭에서 GenerateSecret token을 잘 기록해둔다.

git branch: 'master', credentialsId: 'YOUR_CREDENTIAL', url: 'https://<Your Git Repository URL>'
pipeline {
    agent any
    
    tools {
        nodejs 'nodejs-20.11.1'
    }
    
    // 필요한 변수 설정
    environment {
        PROJECT_DIR = 'your_project_directory_name'
        DOCKER_REGISTRY = 'your_docker_registry_url'
        
        BACKEND_IMAGE_NAME = 'server/backend'
        FRONTEND_IMAGE_NAME = 'server/frontend'
        
        BACKEND_CONTAINER_NAME = 'server-backend'
        FRONTEND_CONTAINER_NAME = 'server-frontend'
    }


    stages {
        stage('Checkout') {
            steps {
                echo 'Starting Repository Checkout'
                
                git url: 'https://<Your Git Repository URL>',
                credentialsId: 'YOUR_CREDENTIAL', branch: 'master'
                
                echo 'Repository Checkout Completed'
            }
        }
        
        stage('Build Frontend') {
            steps {
                echo 'Starting Frontend Build Process'
                
                dir('frontend') {
                    sh 'docker ps -aqf "name=${FRONTEND_CONTAINER_NAME}" && docker stop ${FRONTEND_CONTAINER_NAME} && docker rm ${FRONTEND_CONTAINER_NAME}'
                    sh 'docker images -q ${FRONTEND_IMAGE_NAME} && docker rmi ${FRONTEND_IMAGE_NAME}'
                    sh 'docker build -t ${FRONTEND_IMAGE_NAME} .'
                }
                
                echo 'Frontend Build Completed Successfully'
            }
            post {
                success {
                    script {
                        echo 'Build Frontend Success'
                    }
                }
                failure {
                    script {
                        echo 'Build Frontend Failed'
                    }
                }
            }
        }
        
        stage('Test Frontend') {
            steps {
                echo 'Starting Frontend Tests'
                
                dir('frontend') {
                    sh 'npm test'
                }
                
                echo 'Frontend Tests Completed'
            }
            post {
                success {
                    script {
                        echo 'Test Frontend Success'
                    }
                }
                failure {
                    script {
                        echo 'Test Frontend Failed'
                    }
                }
            }
        }
        
        stage('Deploy Frontend') {
            steps {
                echo 'Deploying Frontend'
                
                sh "docker run -d -p 3000:3000 --name ${FRONTEND_CONTAINER_NAME} ${FRONTEND_IMAGE_NAME}"
                
                echo 'Frontend Deployed Successfully'
            }
            post {
                success {
                    script {
                        echo 'Deploy Frontend Success'
                    }
                }
                failure {
                    script {
                        echo 'Deploy Frontend Failed'
                    }
                }
            }
        }
        
        stage('Build Backend') {
            steps {
                echo 'Starting Backend Build Process'
                
                dir('backend') {
                    sh 'chmod +x ./gradlew'
                    sh './gradlew clean build'
                    
                    sh 'docker ps -aqf "name=${BACKEND_CONTAINER_NAME}" && docker stop ${BACKEND_CONTAINER_NAME} && docker rm ${BACKEND_CONTAINER_NAME}'
                    sh 'docker images -q ${BACKEND_IMAGE_NAME} && docker rmi ${BACKEND_IMAGE_NAME}'
                    sh 'docker build -t ${BACKEND_IMAGE_NAME} .'
                }
                
                echo 'Backend Build Completed Successfully'
            }
            post {
                success {
                    script {
                        echo 'Build Backend Success'
                    }
                }
                failure {
                    script {
                        echo 'Build Backend Failed'
                    }
                }
            }
        }
        
        stage('Test Backend') {
            steps {
                echo 'Starting Backend Tests'
                
                dir('backend') {
                    sh './gradlew test'
                }
                
                echo 'Backend Tests Completed'
            }
            post {
                success {
                    script {
                        echo 'Test Backend Success'
                    }
                }
                failure {
                    script {
                        echo 'Test Backend Failed'
                    }
                }
            }
        }
        
        stage('Deploy Backend') {
            steps {
                echo 'Deploying Backend'
                
                sh "docker run -d -p 8080:8080 --name ${BACKEND_CONTAINER_NAME} ${BACKEND_IMAGE_NAME}"
                
                echo 'Backend Deployed Successfully'
            }
            post {
                success {
                    script {
                        echo 'Deploy Backend Success'
                    }
                }
                failure {
                    script {
                        echo 'Deploy Backend Failed'
                    }
                }
            }
        }
    }
    
    post {
        always {
            echo 'Pipeline Execution Complete.'
        }
        success {
            echo 'Pipeline Execution Success.'
            script {
                echo '빌드/배포 Success'
            }
        }
        failure {
            echo 'Pipeline Execution Failed.'
            script {
                echo '빌드/배포 Failed'
            }
        }
    }
}

📖 GitLab Webhook 추가

  • URL: 위에서 기록해둔 GitLab webhook URL
  • Secret token: 위에서 기록해둔 Secret token


📌 Mattermost Webhook 설정


📖 Mattermost Webhook 추가


📖 Jenkins Mattermost Plugin 설치


📖 Jenkins System 설정

  • Endpoint: Incoming Webhook URL
  • Channel: Incoming Webhook을 추가할 때 선택했던 채널 이름 (초록색 박스 안의 주소)
  • Build Server URL: Jenkins URL (자동으로 입력되어 있을 것이다.)

📖 공통 함수 정의

// 공통 함수 정의
def sendMattermostNotification(String stage, String status) {
    script {
        def AUTHOR_ID = sh(script: "git show -s --pretty=%an", returnStdout: true).trim()
        def AUTHOR_NAME = sh(script: "git show -s --pretty=%ae", returnStdout: true).trim()
        
        def color = (status == 'Success') ? 'good' : 'danger'
        def message = "${stage} ${status}: ${env.JOB_NAME} #${env.BUILD_NUMBER} by ${AUTHOR_ID}(${AUTHOR_NAME})\n(<${env.BUILD_URL}|Details>)"
        def endpoint = 'https://your_notification_service_endpoint'
        def channel = 'your_notification_service_channel'
        
        mattermostSend (
            color: color,
            message: message,
            endpoint: endpoint,
            channel: channel,
        )
    }
}

pipeline {
    agent any
    
    ...
}

📖 Pipeline 적용

// 공통 함수 정의
def sendMattermostNotification(String stage, String status) {
    script {
        def AUTHOR_ID = sh(script: "git show -s --pretty=%an", returnStdout: true).trim()
        def AUTHOR_NAME = sh(script: "git show -s --pretty=%ae", returnStdout: true).trim()
        
        def color = (status == 'Success') ? 'good' : 'danger'
        def message = "${stage} ${status}: ${env.JOB_NAME} #${env.BUILD_NUMBER} by ${AUTHOR_ID}(${AUTHOR_NAME})\n(<${env.BUILD_URL}|Details>)"
        def endpoint = 'https://your_notification_service_endpoint'
        def channel = 'your_notification_service_channel'
        
        mattermostSend (
            color: color,
            message: message,
            endpoint: endpoint,
            channel: channel,
        )
    }
}

pipeline {
    agent any
    
    tools {
        nodejs 'nodejs-20.11.1'
    }
    
    // 필요한 변수 설정
    environment {
        PROJECT_DIR = 'your_project_directory_name'
        DOCKER_REGISTRY = 'your_docker_registry_url'
        
        BACKEND_IMAGE_NAME = 'server/backend'
        FRONTEND_IMAGE_NAME = 'server/frontend'
        
        BACKEND_CONTAINER_NAME = 'server-backend'
        FRONTEND_CONTAINER_NAME = 'server-frontend'
    }


    stages {
        stage('Checkout') {
            steps {
                echo 'Starting Repository Checkout'
                
                git url: 'https://<Your Git Repository URL>',
                credentialsId: 'YOUR_CREDENTIAL', branch: 'master'
                
                echo 'Repository Checkout Completed'
            }
        }
        
        stage('Build Frontend') {
            steps {
                echo 'Starting Frontend Build Process'
                
                dir('frontend') {
                    sh 'docker ps -aqf "name=${FRONTEND_CONTAINER_NAME}" && docker stop ${FRONTEND_CONTAINER_NAME} && docker rm ${FRONTEND_CONTAINER_NAME}'
                    sh 'docker images -q ${FRONTEND_IMAGE_NAME} && docker rmi ${FRONTEND_IMAGE_NAME}'
                    sh 'docker build -t ${FRONTEND_IMAGE_NAME} .'
                }
                
                echo 'Frontend Build Completed Successfully'
            }
            post {
                success {
                    script {
                        sendMattermostNotification('Build Frontend', 'Success')
                    }
                }
                failure {
                    script {
                        sendMattermostNotification('Build Frontend', 'Failed')
                    }
                }
            }
        }
        
        stage('Test Frontend') {
            steps {
                echo 'Starting Frontend Tests'
                
                dir('frontend') {
                    sh 'npm test'
                }
                
                echo 'Frontend Tests Completed'
            }
            post {
                success {
                    script {
                        sendMattermostNotification('Test Frontend', 'Success')
                    }
                }
                failure {
                    script {
                        sendMattermostNotification('Test Frontend', 'Failed')
                    }
                }
            }
        }
        
        stage('Deploy Frontend') {
            steps {
                echo 'Deploying Frontend'
                
                sh "docker run -d -p 3000:3000 --name ${FRONTEND_CONTAINER_NAME} ${FRONTEND_IMAGE_NAME}"
                
                echo 'Frontend Deployed Successfully'
            }
            post {
                success {
                    script {
                        sendMattermostNotification('Deploy Frontend', 'Success')
                    }
                }
                failure {
                    script {
                        sendMattermostNotification('Deploy Frontend', 'Failed')
                    }
                }
            }
        }
        
        stage('Build Backend') {
            steps {
                echo 'Starting Backend Build Process'
                
                dir('backend') {
                    sh 'chmod +x ./gradlew'
                    sh './gradlew clean build'
                    
                    sh 'docker ps -aqf "name=${BACKEND_CONTAINER_NAME}" && docker stop ${BACKEND_CONTAINER_NAME} && docker rm ${BACKEND_CONTAINER_NAME}'
                    sh 'docker images -q ${BACKEND_IMAGE_NAME} && docker rmi ${BACKEND_IMAGE_NAME}'
                    sh 'docker build -t ${BACKEND_IMAGE_NAME} .'
                }
                
                echo 'Backend Build Completed Successfully'
            }
            post {
                success {
                    script {
                        sendMattermostNotification('Build Backend', 'Success')
                    }
                }
                failure {
                    script {
                        sendMattermostNotification('Build Backend', 'Failed')
                    }
                }
            }
        }
        
        stage('Test Backend') {
            steps {
                echo 'Starting Backend Tests'
                
                dir('backend') {
                    sh './gradlew test'
                }
                
                echo 'Backend Tests Completed'
            }
            post {
                success {
                    script {
                        sendMattermostNotification('Test Backend', 'Success')
                    }
                }
                failure {
                    script {
                        sendMattermostNotification('Test Backend', 'Failed')
                    }
                }
            }
        }
        
        stage('Deploy Backend') {
            steps {
                echo 'Deploying Backend'
                
                sh "docker run -d -p 8080:8080 --name ${BACKEND_CONTAINER_NAME} ${BACKEND_IMAGE_NAME}"
                
                echo 'Backend Deployed Successfully'
            }
            post {
                success {
                    script {
                        sendMattermostNotification('Deploy Backend', 'Success')
                    }
                }
                failure {
                    script {
                        sendMattermostNotification('Deploy Backend', 'Failed')
                    }
                }
            }
        }
    }
    
    post {
        always {
            echo 'Pipeline Execution Complete.'
        }
        success {
            echo 'Pipeline Execution Success.'
            script {
                sendMattermostNotification('빌드/배포', 'Success')
            }
        }
        failure {
            echo 'Pipeline Execution Failed.'
            script {
                sendMattermostNotification('빌드/배포', 'Failed')
            }
        }
    }
}
profile
개발을 즐길 줄 아는 백엔드 개발자
post-custom-banner

0개의 댓글