Jenkins로 CI/CD 구축하기

2024. 12. 13. 15:47·Devops

배경

 

현재 참여 중인 프로젝트는 Github action을 통해 CI/CD를 구축했습니다. 하지만 Github action을 사용하는 것에 대해 2가지 단점이 있었습니다.

 

첫 번째 단점은 빌드 시간이 오래 걸린다는 점입니다. 로컬에서 React 프로젝트 빌드 시간이 약 30초였는데, action에서는 약 3분이 걸리며 빌드 시간이 약 6배 증가했습니다. 깃허브 액션이 실행되는 서버가 매번 변경되어 workflow를 실행할 때마다 React 프로젝트 내 필요한 라이브러리를 설치하는데 시간이 소요됩니다.

 

두 번째 단점은 workflow가 실행되는 서버가 동적이라는 점입니다. 배포 서버는 인바운드 규칙이 있고 action이 실행되는 서버 IP는 매번 바뀝니다. 이에 배포 서버에서 ssh 접근을 허용하는 데 어려움이 있었습니다.

 

위 2가지 단점을 해결하고자 고정 서버에서 배포를 진행하는 Jenkins를 이용하여 서비스 배포를 시도했습니다.

 

 

Jenkins 도입

Jenkins를 설치하는 방법은 [MAC] 젠킨스 설치하기 글을 참고해 주세요😀

Macbook local에 Jenkins를 설치하여 진행했습니다.

사내 jenkins 가 설치된 서버에서 실행해 봤지만,, 낮은 스펙으로 인해 뻗어 버렸습니다. 

AWS EC2 t2.small

 

 

1. 젠킨스 접속 후 아이템 생성

 

 

2. 아이템 이름을 작성하고 파이프라인 선택

 

 

3. 세부사항 설정

(저는 별도로 필요한 설정이 없어서 스크립트 파일만 작성했습니다.)

 

 

3-2. 스크립트 파일 작성

 

- 빌드에 필요한 세부사항 설정

  • agent : 파이프라인 또는 스테이지를 실행할 위치를 설정
  • tools : Jenkins에서 관리하는 특정 도구를 파이프라인에서 사용하도록 설정
    • Jenkins 관리 > Tools에서 등록할 수 있고 등록 시 작성한 이름과 동일하게 사용 가능합니다.

Jenkins 관리 > Tools

  • options : 파이프라인이나 특정 스테이지의 실행 동작을 제어하기 위한 다양한 옵션을 설정
    • timeout : 파이프라인의 최대 실행 시간을 설정합니다.
  • environment : 파이프라인 실행 시 사용할 환경 변수를 설정
pipeline {
    agent any
    tools {
        nodejs "NodeJS-22"
        jdk 'java-11'
    }
    options {
        timeout(time: 10, unit: 'MINUTES')
    }
    
    environment { 
        GIT_CREDENTIALS = 'Github-user'
        FRONT_END_BUILD_SHELL_SCRIPT = 'build-front-view.sh'
        DOCKER_IMAGE_NAME = 'project-docker-image'
        REPO_URL = '../kea-rems-web.git'
        BRANCH = 'develop'
        DOCKERHUB_CREDENTIALS = credentials('DOCKER_HUB_ADMIN')
        SSH_NAME = 'hz_sudo'
    }
}

 

 

- Git repository에서 프로젝트 코드 클론

  • Jenkins 관리 > Cridentials 페이지에서 Git 인증 정보 등록 필요

stages {
        stage('GitHub Repository Clone') { 
            steps {
                checkout([
                    $class: 'GitSCM',
                    branches: [[name: "${BRANCH}"]],
                    userRemoteConfigs: [[
                        credentialsId: "${GIT_CREDENTIALS}",
                        url: "${REPO_URL}"
                    ]]
                ])
                script {
                    env.GIT_COMMIT = sh(
                        script: "git rev-parse HEAD", 
                        returnStdout: true
                    ).trim()
                    
                    // Git 커밋 해시가 제대로 설정되었는지 확인
                    if (!env.GIT_COMMIT || env.GIT_COMMIT.isEmpty()) {
                        error "Failed to retrieve GIT_COMMIT. Exiting build."
                    } else {
                        echo "Successfully retrieved GIT_COMMIT: ${env.GIT_COMMIT}"
                    }
                }
            }
        }

 

 

- REACT 빌드 스크립트 실행

stage('Frontend Build') { 
            options {
                retry(1)
            }
            steps {
                sh 'chmod +x ${WORKSPACE}/${FRONT_END_BUILD_SHELL_SCRIPT}'
                sh "${WORKSPACE}/${FRONT_END_BUILD_SHELL_SCRIPT}"
            }
        }

 

 

- Spring 프로젝트 빌드 실행

stage('Backend Build') { 
            options {
                retry(2)
            }
            steps {
                sh 'java --version'
                sh 'chmod +x gradlew'
                sh './gradlew clean build -x test'
            }
        }

 

 

- Docker Hub 로그인 및 도커 이미지 생성

  • Github와 동일하게 Docker hub 인증 정보도 Jenkins 관리 > Cridentials에서 등록 필요
stage('Docker Hub Login'){
            steps{
                sh "echo ${DOCKERHUB_CREDENTIALS_PSW} | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin"
            }
        }
        
        stage('Docker Image Build and Push') {
            options {
                retry(1)
            }
            steps{
                sh "docker build --platform linux/amd64 -t ${DOCKERHUB_CREDENTIALS_USR}/test-web:dev-${env.GIT_COMMIT} ." // docker build
                sh "docker push ${DOCKERHUB_CREDENTIALS_USR}/test-web:dev-${env.GIT_COMMIT}"//docker push
            }
        }

 

 

- 도커 이미지 배포

  • 배포 서버에 SSH로 접근하고 도커 허브에 올린 이미지를 pull 받아서 컨테이너 실행
stage('Docker Image Pull and Deploy') {
            options {
                retry(1)
            }
            steps {
                script {
                    def DOCKER_IMAGE_TAG = "${DOCKERHUB_CREDENTIALS_USR}/test-web:dev-${env.GIT_COMMIT}"

                    def EXEC_COMMAND = """
                        OLD_IMAGE_ID=\$(sudo docker inspect --format='{{.Image}}' test-web); 
                        sudo docker stop kea-rems-web;
                        
                        sudo docker rm test-web;
                        sudo docker rmi \${OLD_IMAGE_ID};
                        
                        sudo docker pull ${DOCKER_IMAGE_TAG};
                        sudo docker run -d -p 9091:8080 --name test-web --net=test-network ${DOCKER_IMAGE_TAG}
                    """
        
                    sshPublisher(
                        failOnError: true,
                        publishers: [
                            sshPublisherDesc(
                                configName: "${SSH_NAME}",
                                verbose: true,
                                transfers: [
                                    sshTransfer(
                                        execCommand: "${EXEC_COMMAND}"
                                    )
                                ]
                            )
                        ]
                    )
                }
            }
        }

 

 

결과

React 빌드 시간은 180s(3분) >  30s로 약 83% 감소했지만, Docker 이미지 생성 단계에서 약 3분 정도 소요되어 Github action과 비교하여 오랜 시간이 걸렸습니다. 그 결과, CI/CD 전체 실행 시간을 비교하면 약 30초 정도 단축했습니다.

 

  React (FE) 빌드 도커 이미지 생성 전체
Github action 소요시간 3분 7초 38초 5분 57초
Jenkins 소요시간 20초 4분 10초 5분 25초

 

 

 

도커 이미지를 생성하는 데 오래 걸린 이유 (예측)

 

 

1. 이미지 플랫폼 문제

: Github action은 우분투에서 --platform linux/amd64 타입의 이미지를 만들고 Jenkins는 macbook local 환경에서 실행했기 때문에 arm머신에서 x86 이미지를 빌드한다. 그 결과 이미지를 생성하는 데 오래 걸릴 수 있다.

 

실제로 도커 이미지를 실행할 플랫폼을 별도로 지정하지 않고 실행했을 때 약 1분가량 시간을 단축할 수 있었다.

 

 

2. 리소스 부족

: Github action은 워크플로우를 실행하는 전용 서버로 돌리지만, Jenkins는 로컬 환경에서 시스템 리소스를 공유해서 사용하기 때문에 속도가 느리지 않았을까 생각했습니다.

 

 

 

* github action의 self-hosted runners라는 기능을 발견했습니다. 간단하게 자기 소유의 서버로 Github action을 실행하는 것입니다. 다음에 CI/CD를 설계하면 사용해 보겠습니다.

 

'Devops' 카테고리의 다른 글

[MAC] 젠킨스 설치하기  (1) 2024.10.16
'Devops' 카테고리의 다른 글
  • [MAC] 젠킨스 설치하기
HBean_
HBean_
백엔드 개발자의 개발 로그 💻
  • HBean_
    개발_log
    HBean_
  • 전체
    오늘
    어제
    • 전체 (103)
      • WEB (49)
        • Spring (14)
        • AWS EC2 (6)
        • DB (3)
        • 2020_webCamp (25)
        • JPA (1)
      • Devops (2)
      • 보안 (4)
      • Git (6)
      • JAVA (13)
      • 자료구조 (2)
      • 알고리즘 (11)
      • 네트워크 (2)
      • SStudy (2)
      • 실전프로젝트2 (4)
      • 개발 일기 (1)
      • 개발툴 (4)
      • Intellij (2)
      • 이슈 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GITHUB
  • 공지사항

  • 인기 글

  • 태그

    플러그인
    인텔리제이
    tomcat
    톰캣
    IntelliJ
    웹
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
HBean_
Jenkins로 CI/CD 구축하기
상단으로

티스토리툴바