DevOps/Jenkins

[Jenkins] 파이프라인(Pipeline) 활용한 빌드 배포 환경 구축

cob 2023. 12. 13. 15:45

 

 

 

1. Jenkins

Jenkins는 CI/CD 파이프라인을 구축하고 관리하는 데 사용되는 도구 중 하나로 소프트웨어 개발 과정에서 자동화된 빌드, 테스트, 배포를 관리하는 오픈 소스 자동화 도구다. 이를 통해 개발자들은 코드 변경 사항을 통합하고, 자동으로 테스트하며, 배포하는 과정을 효율적으로 관리할 수 있다.
  • CI : 여러 개발자들이 작업한 코드를 정기적으로 하나로 통합하고, 그 과정에서 자동화된 테스트를 거쳐 소프트웨어가 기존 코드와 함께 잘 동작하는지 확인하는 것
  • CD : CI의 일부로, 소프트웨어가 테스트를 통과하고 배포 가능한 상태가 되면, 이를 자동화하여 프로덕션 환경에 자동으로 배포하는 프로세스를 가리킨다. 

 

 

2. 구조도

로컬에서 해당 SVN 리포지토리에 push 하고, Jenkins를 사용해 원하는 서버로 배포

 

Jenkins 구조도

 

3. SVN 구축

2023.12.04 - [Linux] - [Linux] Ubuntu SVN 설치 및 자동 실행 방법

 

 

 


4. Jenkins 설치

4-1) JDK install

Java 11버전 이상을 사용할 경우 상관없지만, 이하를 사용한다면 추가로 11 버전 이상을 다운로드하아야 한다. Jenkis는 11버전 이상이 필요하다.
sudo apt update  # 패키지 리스트 업데이트
sudo apt install openjdk-11-jdk  # OpenJDK 11 설치

 

 

4-2) Jenkins install

해당 URL을 통해 OS에 맞는 Jenkins를 설치

https://www.jenkins.io/download/

 

Jenkins download and deployment

Jenkins – an open source automation server which enables developers around the world to reliably build, test, and deploy their software

www.jenkins.io

 

 

 

4-1) Jenkins 포트 변경

Jenkins를 설치하게 되면 기본 포트 8080으로 설정된다. 
// 우분투 기준
sudo vi /etc/default/jenkins

// centos 7이상 기준
sudo vi /usr/lib/systemd/system/jenkins.service

// 적용
systemctl daemon-reload

// 재시작
systemctl restart jenkins

포트 변경

 

  • 8080 => 7070 변경
// Jenkins 재실행 둘중에 하나로 실행
/etc/init.d/jenkins start
systemctl start jenkins

// 반화벽 활성화
sodu ufw enable
// 방화벽 포트 열기
sodu ufw allow 7070
// 방화벽 확인
sodu ufw status

 

 

4-2) 접속

http://[서버 IP]:[PORT] 주소로 접속

key 입력 화면

  • 빨간 글씨 안내 주소 경로를 찾아 password를 입력
cat /var/lib/jenkins/secrets/initialAdminPassword

 

 

 

4-3) 플러그인 설치

일일이 플러그인을 설치하는 경우가 아니라면, 제안해 주는 Instal suggested plugins를 선택하여 설치한다.

플러그인 설치

 

 

 


5. Credentials 등록

민감한 정보를 노출하지 않고, 누구나 액세스할 수 없도록 관리해 준다.
  • [Dashboard] => [Jenkins 관리] => [Credentials]

Credentials

 

5-1) Credentials  등록(SVN)

  • [ global ] 선택

global

 

 

5-2) [ Add Credentials] 클릭  

Add  Credentials

 

 

5-3) SVN 리포지토리 접속 ID, PW를 입력 Credentials의 ID는 Credentials를 사용할 때 적용된다.

  • [ Username with password ] 선택

SVN  Credentials

 

 

 

 

6. SSH Agent key 등록

remote server(원격 서버)에 접속하기 위해 SSH key를 등록해 주어야 한다.

 

 

6-1) 플러그인 설치

  • [ Jenkins 관리 ] => [ Plugins ]

 

 

 

6-2) SSH agent 설치

Jenkins를 셋팅할 때 Install suggested plugins로 했을 경우 기본적인 건 있지만 추가로 SSH Agent 추가 설치가 필요하다.
  • [ Available plugins ] => [ SSH Agent Plugins ] 설치

SSH Agent Plugins

 

 

 

6-3) .ssh 디렉터리 생성 [Jenkins 서버]

소유자 홈디렉토리 경로(ssh 기본 경로). ssh를 생성해 준다. 있으면 만들 필요 없음

.ssh

$ mkdir .ssh

 

 

 

6-4) ssh key 생성  [Jenkins 서버]

key 생성 시. ssh 디렉터리 안에 개인키(id_rsa), 공유키(id_rsa.pub)가 생성된다.
공유키를 접속하고 싶은 서버에 등록해야 접속 가능하다.
$ ssh-keygen -t rsa

 

 

  • 공개키를 원격서버에 등록한다.
$ssh-copy-id -i ~/.ssh/id_rsa.pub username@원격서버 IP

 

 

 

6-5) 공유키 확인 [원격 서버]

~/.ssh/authorize_keys 해당 경로와 파일이 없다면 생성해야 공개키가 등록된다.

.ssh 원격 서버

$ mkdir .ssh
$ chmod 700 .ssh

$ cd .ssh
$ vi authorized_keys
$ chmod 600 authorized_keys

 

*주의 사항: .ssh파일, authorize_keys 파일을 만들 때 너무 많은 권한(chmod 777)을 주게 되면 보안상 이유로 접속할 수 없다.

 

 

 


7. Credentials 등록(SSH)

[ 5. Credentials 등록 ]과 동일한 과정

  • Kind : SSH Username with private key 선택
  • ID : Credentials 호출 ID 설정
  • Username : 빈값 설정 시 자동적으로 jenkins 세팅됨
  • Enter directly : 개인키 등록

 

 

7-1) 개인키(id_rsa)

[ 6. SSH Agent key 등록 ] 때 발급받은 SSH 개인키(id_rsa)를 복사하여 등록한다.

Jenkins 서버 개인키

 

  • vi 편집기로 들어가 내용 복사
$ vi id_rsa

 

 

 


8. Item 생성

  • [Dashbord] > [새로운 Item] 클릭

새로운 Item

 

 

8-1) Pipeline

Jenkins의 Pipeline는 전체 CI/CD 프로세스를 시각적으로 파악하기 쉽다.
  • [Pipeline] 선택 > [OK] 클릭

 

 

8-2) Pipeline 스크립트 작성

  • 스크립트 작성 > [Apply] > [저장]

Pipeline

Build 순서 : Checkout(소스 체크아웃) > Deploy(원격 서버 배포) > Run (원격 서버에서 실행) > Post(임시 파일 정리) 
pipeline {
    agent any
    
    stages {
        stage('Checkout') {
            steps {
                script {
                    checkout([$class: 'SubversionSCM',
                        additionalCredentials: [],
                        locations: [[
                            cancelProcessOnExternalsFail: true,                     // 프로세스 실패할 경우 Checkout  중단
                            credentialsId: 'e87b092c-9dfe-4d51-adc2-70573b4b115a',  // 자격증명
                            depthOption: 'infinity',                                // 프로젝트의 모든 버전 히스토리를 포함 체크아웃
                            ignoreExternalsOption: true,                            // 외부(externals) 속성을 무시
                            local: '.',                                             // 워크스페이스 경로 지정
                            remote: 'svn://192.168.66.128/TEST2'
                        ]],
                        quietOperation: true,                                       // CheckOut시 발생하는 메세지 최소화
                        workspaceUpdater: [$class: 'UpdateUpdater']                 // SVN에서는 작업 공간을 최신 상태로 유지하고, 변경된 내용을 가져와서 업데이트하는 방식
                    ])
                }
            }
        }
        stage('Deploy') {
            steps {
                script {
                    def projectName = env.WORKSPACE.tokenize('/')[-1] 
                    
                    // SSH로 원격 서버에 전체 디렉토리를 동기화하여 전송
                    sshagent(credentials: ['test1_ssh']) {
                        sh "rsync -avz -e 'ssh -o StrictHostKeyChecking=no' --exclude='.svn' ${env.WORKSPACE}/ operation@192.168.66.129:/home/operation/jex/workspace/${projectName}/"
                        
                    }
                }
            }
        }
        stage('Run Remote Build Script') {
            steps {
                script {
                    sshagent(credentials: ['test1_ssh']) {
                        def projectName = env.WORKSPACE.tokenize('/')[-1] 
                        // 원격 서버에서 Maven 패키징 실행
                        sh "ssh -o StrictHostKeyChecking=no operation@192.168.66.129 'cd /home/operation/jex/workspace/${projectName}/ && chmod +x ./jenkins_script_prod.sh && ./jenkins_script_prod.sh'"
                    }
                }
            }
        }
    }
    post { // 파이프라인 종료된 후 작업
        cleanup {
            dir("${env.WORKSPACE}@tmp") {
              deleteDir()
            }
        }
    }
}

 

 

 

8-3) Deploy Script

기존 원격 서버에 있는 프로젝트와 동기화하여 변경사항이 있는 파일만 배포한다. 
// SSH로 원격 서버에 전체 디렉토리를 동기화하여 전송
sshagent(credentials: ['test1_ssh']) {
    sh "rsync -avz -e 'ssh -o StrictHostKeyChecking=no' --exclude='.svn' ${env.WORKSPACE}/ operation@192.168.66.129:/home/operation/jex/workspace/${projectName}/"
}
  • credentials : [ 7. Credentials 등록(SSH)] 에서 생성한 credentials의 ID를 입력
  • rsync : 파일의 변경 여부를 확인하고 변경된 파일만을 전송(a : 아카이브모드 전송, v : 출력, z : 압축)
  • StrictHostKeyChecking=no : SSH 연결 시 호스트 키 체크를 비활성화한다. 일반적으로 SSH가 처음 해당 호스트에 연결할 때 호스트 키를 확인하게 되는데 호스트 키가 클라이언트에 저장되어 있지 않거나 변경된 경우에는 연결을 중단되기 때문에 체크를 하지 않는다.

 

 

 

8-4) Run Remote Build Script

원격 서버에 접속하여 원격서버의 쉘을 실행한다.
sshagent(credentials: ['test1_ssh']) {
    def projectName = env.WORKSPACE.tokenize('/')[-1] 
    // 원격 서버에서 Maven 패키징 실행
    sh "ssh -o StrictHostKeyChecking=no operation@192.168.66.129 'cd /home/operation/jex/workspace/${projectName}/ && chmod +x ./jenkins_script_prod.sh && ./jenkins_script_prod.sh'"
}
  •  chmod +x ./jenkins_script_prod.sh  : jenkins_script_prod.sh 파일에 실행권한이 없기 때문에 전송 시 부여한다.
  •  ./jenkins_script_prod.sh  : 원격 서버에서 해당 쉘을 실행한다.

 

 

 

8-5)  Post

빌드가 종료되었을 때 취할 조치를 작성할 수 있다.
  • 원격 서버에 파일을 배포할 경우 프로젝트 임시파일을 생성하게 되는데 해당 파일을 삭제한다.
cleanup {
    dir("${env.WORKSPACE}@tmp") {
      deleteDir()
    }
}

 

 

 

 


9. 원격서버 쉘 스크립트

파이프라인 [Run Remote Build Script] 스텝에서 실행되는 원격서버의 스크립트 파일(jenkins_script_prod.sh)
# jenkins_script_prod.sh
echo '> Maven 패키징 ...'
mvn clean package

echo '> 실행중인 TEST2 찾는 중 ...'
CURRENT_PID=$(pgrep -f TEST2)

echo "PID : $CURRENT_PID"
if [ -z "$CURRENT_PID" ]; then
        echo '> TEST2는 실행 중이 아닙니다.'
else
        echo "> kill -9 $CURRENT_PID"
        kill -9 "$CURRENT_PID"
        sleep 3
fi

echo '> TEST 실행 중 ...'

JAR_NAME=$(ls ./target/*.jar | xargs basename)
echo "> JAR Name: $JAR_NAME"

nohup java -jar -Dspring.profiles.active=prod ./target/$JAR_NAME >> ~/jex/logs/$(date '+%Y-%m-%d').log 2>&1 &
sleep 3
  • nohup : 백그라운드에서 실행
  • ~/jex/logs/$(date '+%Y-%m-%d').log : 로그 파일을 쌓기 위해 추가
  • 2>&1 : 출력 결과와 에러 내용을 저장하기 위해 추가

 

 

 


10. 완료 후 테스트

[Apply] > [저장] 후 빌드 테스트

완료 후 테스트

  • Step별로 확인 가능하다.

 

 

 

반응형