Jenkins를 통한 CI/CD 구축(3)

박세건·2024년 9월 10일
0

기술 실습

목록 보기
5/18
post-thumbnail

CD 구축

Jenkins를 통한 CI구축을 완료했으니 CD까지 연결되어서 실제 배포시키는 과정을 진행해보자.

Jenkins에서 CD 구축

Publish over SSH 설정

  • SSH로 EC2에 jar 배포 파일을 전달해주기 위해 Publish over SSH Plugin 설치
    Dashboard > Jenkins 관리 > 시스템 설정
  • 발급받은 key.pem 파일을 메모장으로 열어서 전체 내용을 복사 후 key에 넣기
  • 추가 클릭
  • 정보 작성
    • name : 임의 작성
    • Hostname : 서버 도메인 주소
    • Username : ubuntu
    • Remote Directory : 젠킨스에서 build하여 만든 jar 파일을 ec2 서버로 옮길 주소로 설정
      ex) /home/ubuntu/jenkins_build
  • Test Configuration 으로 테스트

트러블 슈팅

jenkins.plugins.publish_over.BapPublisherException: Failed to connect and initialize SSH connection. Message: [Failed to change to remote directory [/home/ubuntu/jenkins_build]]
에러 발생

...jenkins_build 디렉토리가 없어서 발생한 문제 디렉토리 생성으로 해결


  • Success 문장을 확인한 후 저장

CD Pipeline 코드 작성

Pipeline Syntax에서 Snippet Generator 기능을 사용

  • sshPublisher 선택
  • name은 이전에 생성한 ssh Server name으로 선택
  • 옮길 배포 파일 설정 : Transfer Set
    • 우리는 jar파일을 ec2서버로 보낼것이기에 *.jar로 설정

CD 파이프라인 과정

  1. jar파일을 관리하는 디렉토리인 jenkins_build로 이동
  2. 현재 실행되고 있는 애플리케이션 종료(프로세스 종료)
  3. 새로운 버전의 애플리케이션을 실행
    이전에 실행되고있는 애플리케이션(프로세스)를 기억하기 위해 save_pid 파일로 관리

위 순서에 맞게 Exec command 코드를 작성

# jar파일을 저장시킬 디렉토리로 이동
cd /home/ubuntu/jenkins_build
# 실행되고 있던 프로세스를 종료(save_pid.txt 파일 이용)
kill -9 `cat save_pid.txt`
# 파일 삭제
rm save_pid.txt
# jar 파일 실행
nohup java -jar demo-0.0.1-SNAPSHOT.jar > logs/demo.log 2>&1 &
# 마지막으로 실행된 백그라운드 프로세스의 프로세스 ID(PID) 저장
echo $! > save_pid.txt

Genereate Pipeline Script를 통해 코드 생성한 후 Pipeline Script에 추가

최종 Script

pipeline {
    agent any



    stages {
        stage('Clone') {
            steps {
                sh 'pwd'
                git branch: 'dev', credentialsId: 'qkrtprjs', url: 'https://lab.ssafy.com/qkrtprjs456/test.git'
            }
            post {
                failure {
                  echo 'Repository clone failure !'
                }
                success {
                  echo 'Repository clone success !'
                }
            }
        }
        stage('Build'){
            steps{
                sh 'pwd'
                sh 'chmod +x gradlew' // 실행 권한 추가
                sh './gradlew bootJar'
            }
            post {
                failure {
                  echo 'Repository build failure !'
                }
                success {
                  echo 'Repository build success !'
                }
            }
        }
        stage('Deploy'){
            steps {
                dir('build/libs') {
                    sshPublisher(
                    publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', 
                    execCommand:
                        '''
                        cd /home/ubuntu/jenkins_build
                        kill -9 `cat save_pid.txt`
                        rm save_pid.txt
                        nohup java -jar demo-0.0.1-SNAPSHOT.jar > logs/demo.log 2>&1 &
                        echo $! > save_pid.txt
                        ''',
                    execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '*.jar')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]
                    )
                }
            }
            post {
                failure {
                  echo 'Repository Deploy failure !'
                }
                success {
                  echo 'Repository Deploy success !'
                }
            }
        }
    }
}
  • 정상적으로 빌드를 성공했고 지정한 home/ubuntu/jenkins_build 디렉토리에 jar 파일이 정상적으로 이동됨을 확인했고, save_pid.txt 파일도 정상적으로 생성됨을 확인했다.
  • 그렇다면 해당 서버에 직접 접근을 할 수 있는지 확인해보자.
    • Jenkins가 8080을 사용하기 때문에 스프링프로젝트의 포트를 5050으로 설정해주고 push와 merge를 진행

트러블 슈팅

지금까지 내가 작성한 파이프라인 Script는 dev 브랜치의 코드를 clone 하고 clone한 프로젝트를 기준으로 빌드를 진행해서 jar 파일을 생성한다. 그 후에 만들어진 jar 파일을 기준으로 -jar 명령어를 통해 애플리케이션을 구동시킨다.

CI/CD란 수정된 코드에 대해서 빌드와 테스트를 진행하고 문제가 발생하지 않았을때에 배포과정까지 수행되도록 자동화시키는 것.
하지만, 나의 CI/CD는 이미 merge된 코드를 클론해서 CI/CD를 진행시키는 파이프라인이다.

때문에 CI/CD 파이프라인을 수정하려 한다.
1. 개발하는 브랜치(팀원들 각각의 개인 브랜치)와 운영을 담당하는 브랜치(dev) 사이에 중간 브랜치를 생성(backend, frontend)
2. 팀원들이 각자 개발을 진행한 후에 중간브랜치로 PR을 진행 후 팀원들과 코드리뷰 후 merge
3. merge를 진행했을때에 클론따서 CI 파이프라인을 진행해서 문제가 없는지 확인
4. 문제가 없음을 확인하고 dev 브랜치로 PR 날리고 merge 진행
5. merge된 dev 브랜치를 기준으로 CI/CD 과정을 진행하여 서비스 배포 진행

추가적으로 Jenkins에서 merge가 승인되었을때에 파이프라인이 진행되도록 설정을 하면 merge후에 코드가 합쳐지는 과정에서 push도 인식하기때문에 CI/CD가 두번 반복되는 상황이 발생한다. PR에 Webhook을 걸어서 해당 브랜치가 merge되기 전에 임시로 합쳐보는 과정을 진행하는 것이 아니라면 push Webhook을 통해 진행하는 것을 추천한다.


CD/CD로 배포를 진행한 프로젝트 확인

배포시킨 프로젝트의 코드중 {퍼블릭ipv4}:{포트번호}/test 로 접속하면 HelloWorld를 출력하도록 코드를 작성


트러블 슈팅

해당 과정에서 응답하는 데 시간이 너무 오래 걸립니다. 라는 문구 확인

  • 시도
    • 인바운드 규칙에서 5050포트를 허용해준다는 설정을 하지 않음
  • 결과
    • 새로운 포트를 사용할때 항상 보안그룹을 수정해줘야한다는 것을 기억
      • 하지만 여전히 해결되지 않고 너무 오래 걸린다는 응답
  • 시도
    • 5050포트에서 8081 포트로 변경
  • 결과
    • 문제 해결 : 5050 포트로 여러 CI/CD를 진행했고 추가적으로 다른 충돌이 발생했을 것으로 생각


정상적으로 화면이 뜨는 것을 확인

생각해 볼 것

CI/CD로도 충분히 편리한 배포를 진행할 수 있는데 Docker를 왜 사용했는지

  • 환경 일관성: Docker를 사용하면 개발, 테스트, 운영 환경이 동일하게 유지되므로 환경 간의 불일치 문제를 줄일 수 있습니다.
  • 환경변수 설정을 application.yml 파일로 진행하면 코드를 github와 gitlab을 통해 관리하게되면 이 설정파일이 노출되게 된다. application.yml 대신에 환경변수에 대한 설정을 Docker Container를 띄우는 과정에서 설정해줌으로 안전하게 배포 가능
  • 시간 효율성: CI/CD를 통한 서버 배포 방식에서는 수동으로 서버를 설정하는 데 많은 시간이 소요되지만, Docker Compose를 사용하면 명령어 한 줄로 컨테이너를 추가할 수 있어 훨씬 빠릅니다.
  • 확장성: 컨테이너화된 서비스는 필요에 따라 쉽게 확장할 수 있으며, 클러스터링과 오케스트레이션 도구(예: Kubernetes)를 통해 더욱 효율적인 관리가 가능합니다.
profile
멋있는 사람 - 일단 하자

0개의 댓글