favicon Jay Lee DevLog

📚 목차

저번에 했던 GitAction을 통한 CI/CD 에서

GitAction을 Jenkins로만 변경하는 과정을 정리해보려 한다.

 

Jenkins에 대한 설치 과정은

https://dlwjdwls90.tistory.com/176

 

SVN과 Jenkins를 이용한 CI/CD 구축하기 - 1. Jenkins 설치 및 svn 저장소 등록

현재 내가 재직 중인 회사는아직 svn을 이용한 형상관리를 하고 있다. 이에 맞춰서 CI/CD를 구축하여연구소 내에 제안해보려고 한다. 뭐 굉장한건 아니고, 그냥 svn commit 되면jenkins를 통해 이를 업

dlwjdwls90.tistory.com

위 글을 통해 설치하자.

 


1. Jenkinsfile 추가하기.

jenkins와 git을 연결하기 위해서는 다른 docker나 kubenetes처럼 특정 파일이 필요하다.

그게 바로 Jenkinsfile이다.

이전 글들을 봤으면 알 수 있듯이,

해당 파일안에 빌드부터, 내가 컨트롤하고픈 사항들을 넣어주면

git push 발생 후 해당 파일의 내용을 순서대로 실행하는 구조이다.

먼저 나는 Jenkinsfile을 이렇게 만들었다.

git의 루트 경로에 만들어주면 된다.(dockerfile과 같은 위치.)

해당 내용은 진행을 하면서 점점 변할 예정이다.

pipeline {
  agent any

  environment {
    IMAGE_NAME = "react-app"
    CONTAINER_NAME = "react-app-container"
    PORT = "3000"
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Install') {
      steps {
        sh 'npm install'
      }
    }

    stage('Build') {
      steps {
        sh 'npm run build'
      }
    }

    stage('Docker Build') {
      steps {
        sh "docker build -t ${IMAGE_NAME}:${BRANCH_NAME} ."
      }
    }

    stage('Deploy (Only main)') {
      when {
        branch 'main'
      }
      steps {
        sh """
          docker rm -f ${CONTAINER_NAME} || true
          docker run -d --name ${CONTAINER_NAME} -p ${PORT}:80 ${IMAGE_NAME}:main
        """
      }
    }

    stage('Skip Deploy (Non-main)') {
      when {
        not {
          branch 'main'
        }
      }
      steps {
        echo "Not deploying since branch is not 'main'."
      }
    }
  }
}

 

대강 설명하자면,

stage는 빌드의 단계라고 보면 된다.

처음엔 checkout을 실행, 이후 install -> build -> docker build 이런식으로 진행한다는 거다.

안에 내용들은 내가 해당 stage에서 실행하고픈 내용을 넣어주면 된다.

나의 경우 push 발생 -> checkout -> docker image 빌드 -> local에 docker image 배포

일단은 이렇게만 작업해보려 한다.

 


2. Jenkins 연동

먼저 Jenkins 쪽을 작업해주자.

초기 버전은 별다른 방법을 사용하진 않을 거고,

그냥 특정 주기마다 branch 체크해서 업데이트하는 방식으로 진행해보자.

 

  • Git plugin
  • Docker plugin
  • Docker Pipeline

위에 플로그인을 먼저 설치해주자.

이후 Multibranch Pipeline을 선택하고 만들어주자.

간단하게 하려면 Freestyle로 만들면되는데,

최대한 실전과 비슷하게 만들어보려 한다.

자주 사용하는 항목에 대한 비교는 아래 표를 참고하자.

항목 FreeStyle Pipeline Multibranch Pipeline
유지보수 어려움 용이 매우 용이
복잡한 논리 처리 불가 가능 가능
브랜치 별 실행 불가 수동 구성 자동 구성
실무 사용 X O O (특히 GitHub Flow 사용하는 회사에서 자주 사용)

 

이제 New Item을 눌러 job을 생성해주자.

 

 

이후 설정을 진행해주면 된다.

먼저 git 주소 설정.

 

 

체크 주기 설정.(나중엔 webhook을 통한 업데이트로 변경할거다.)

 

이후 생성하면

첫 빌드를 시작할건데,

나의 경우 아래와 같은 에러가 발생했다.

npm 명령을 찾을 수 없다는 에러였다.

그래서 jenkins 계정으로 로그인하여

npm -v

명령을 실행해보니, 아래와 같이 나왔다.

기존 내 계정으로 실행하면

 

이렇게 잘 나오는 모습을 보니,

jenkins 계정에 대한 링크가 안걸려 있는 것 같다.

Jenkins 관리 -> System -> Global properties에서 환경변수에 아래의 내용을 넣어주자.

이름 : PATH
값 : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

이후 다시 빌드해보면,

똑같다..흠..그럼 다른 방법을 사용하자.

Jenkinsfile에 npm이 설치되어 있는 Docker 이미지를 통한 npm install을 추가하자.

pipeline {
  agent {
    docker {
      image 'node:20'
    }
  }

  environment {
    IMAGE_NAME = 'react-app'
    CONTAINER_NAME = 'react-app-container'
    PORT = '3000'
    NPM_CONFIG_CACHE = "${WORKSPACE}/.npm"
  }

  stages {
    stage('Checkout') {
      steps {
        checkout scm
      }
    }

    stage('Prepare NPM Cache') {
      steps {
        sh 'mkdir -p $NPM_CONFIG_CACHE'
      }
    }

    stage('Install') {
      steps {
        sh 'npm install'
      }
    }

    stage('Build') {
      steps {
        // eslint 오류 무시
        sh 'CI=false npm run build'
      }
    }

    // === 이 단계부터는 Docker 명령어가 필요하므로, Jenkins 호스트에서 실행 ===
    stage('Docker Build & Deploy') {
      agent any // 기본 Jenkins 노드에서 실행
      steps {
        sh """
          docker build -t ${IMAGE_NAME}:latest .
          docker rm -f ${CONTAINER_NAME} || true
          docker run -d -p ${PORT}:80 --name ${CONTAINER_NAME} ${IMAGE_NAME}:latest
        """
      }
    }
  }
}

이렇게 하고 실행해보면,

process apparently never started in /var/lib/jenkins/workspace/github-project_develop@2@tmp/durable-c76d0347
 
(running Jenkins temporarily with -Dorg.jenkinsci.plugins.durabletask.BourneShellScript.LAUNCH_DIAGNOSTICS=true might make the problem clearer)
script returned exit code -2

이러한 에러가 발생하는데,

원인을 확인해보니

WORKSPACE를 정확하게 잡지 못하는 것과,

agent를 명시해주지 않아서 docker에서 cli를 계속 실행하려는 원인이었다.

해결 방법은 WORKSPACE를 명확히 해주고 agent를 상황에 맞게 맞춰주는 방법이다.

pipeline {
  agent none  // 파이프라인 전체에는 agent 설정하지 않음

  options {
    disableConcurrentBuilds()
  }

  environment {
    IMAGE_NAME = 'react-app'
    CONTAINER_NAME = 'react-app-container'
    PORT = '3000'
  }

  stages {
    stage('Install & Build') {
      agent {
        docker {
          image 'node:20'
          args '-v /var/run/docker.sock:/var/run/docker.sock'
          customWorkspace '/var/lib/jenkins/workspace/github-project-fixed'
        }
      }
      environment {
        NPM_CONFIG_CACHE = "${WORKSPACE}/.npm"
      }
      steps {
        sh 'mkdir -p $NPM_CONFIG_CACHE'
        sh 'npm install'
        sh 'npm run build || true'
      }
    }

    stage('Docker Build & Deploy') {
      agent any  // 여기서는 Jenkins 호스트에서 실행
      steps {
        dir('/var/lib/jenkins/workspace/github-project-fixed') {
          script {
            // 디버그
            sh 'echo "PWD: $PWD"'
            sh 'echo "PATH: $PATH"'
                    
            // Docker 작업
            sh "docker build -t ${IMAGE_NAME}:latest ."
            sh "docker rm -f ${CONTAINER_NAME} || true"
            sh """
              docker run -d \\
                -p ${PORT}:80 \\
                -v ${env.WORKSPACE}:${env.WORKSPACE} \\
                -w ${env.WORKSPACE} \\
                --name ${CONTAINER_NAME} \\
                ${IMAGE_NAME}:latest
            """
          }
        }
      }
    }
  }
}

이런식으로 바꿔줬다.

이후 빌드를 해주면,

 

이렇게 성공했다.

실제 배포까지 됐는지 확인해보면 아래와 같이 잘 배포되어 있는 모습이다.

 

이후 http://localhost:3000 으로 접속해보면

APP이 실행되는 모습을 확인할 수 있다.

 

+ Recent posts

/ /

Contact

📧 dlwjdwls60@naver.com


블로그에 내용이 있으면 해당 글을 보여주며, 없으면 내용이 복사된 채로 ChatGPT로 연결됩니다.