[Project] [1] CI/CD Jenkins Pipeline를 이용한 배포 설계

Hayoon·2024년 3월 21일
0

토이 프로젝트를 진행하면서 발생했던 문제에 대한 본인의 생각과 고민을 기록한 글입니다.
기술한 내용이 공식 문서 내용과 상이할 수 있음을 밝힙니다.

마지막 블로그글 이후로 3주만에 복귀했다. 3주간 CI/CD에 대해서 공부하고 시행착오를 겪으며 어떻게 결국 배포까지 했는지에 대해 글을 남긴다.

CI/CD가 뭐지?

개발자 및 팀에 의해서 개발된 결과물에 대해 지속적인 통합과 배포를 하는 프로세스이다. Continuous Integration, Continuous Delivery라고 하는 CI/CD는 개발된 어플리케이션을 통합하고 빌드, 테스트, 배포에 이르기까지 전 과정에 대한 자동화 처리를 담당한다.

CI(지속적 통합): 작업된 코드의 컴파일, 테스트, 패키징 작업이 포함되어 있다. Jenkins를 사용하여 SpringBoot의 WAR파일을 패키징할 것이다.
CD(지속적 배포): CI에 의해서 패키징된 결과물을 다시 개발 또는 운영 서버(Tomcat)로 배포하는 작업이다.

CI/CD를 해야하는 이유?

내가 생각하기에는 팀의 협업, 그리고 고객의 두가지 관점으로 봐야 할 것 같다.

  1. CI/CD를 통해 코드 변경 사항이 자동으로 빌드 및 테스트되고, 배포되므로 개발자는 코드를 더 자주 커밋하고 통합할 수 있게 된다. 이는 개발 사이클을 단축시키고, 개발 속도를 높인다.
  2. 지속적인 개선과 빠른 피드백 루프를 통해 고객의 요구 사항을 신속하게 반영할 수 있다.
    무중단 배포 전략의 이유로, 사용자가 서비스를 이용하는 도중에도 배포가 이루어져도 서비스가 중단되지 않도록 하여 사용자 경험을 향상시킨다.

과정

CI/CD 구성 아키텍처이다. (AWS, NCP, Docker 등 클라우드 서버 및 VM은 해당 설계도에서 생략)

1. 개발자의 코드 변경 사항 Push 및 Commit
개발자는 로컬 개발 환경에서 코드를 수정하고 이를 Git 저장소에 commit 후 push한다. 이는 새로운 기능 추가, 버그 수정, 리팩토링 등 다양한 변경 사항을 포함한다.

2. Git Webhook 트리거
Git 저장소에 변경 사항이 push되면, 설정된 Git Webhook이 트리거되어 Jenkins 서버에 알린다. Webhook은 사전에 Jenkins와 Git 저장소 사이에 연동되어야 한다.(이후 게시 예정)

3. Jenkins에서의 자동 Build-Test-Deploy 과정
Build 단계: Jenkins는 소스 코드를 빌드하기 위해 build 작업을 수행한다. 이 단계에서는 소스 코드 컴파일, 필요한 의존성 다운로드, 패키징 등이 수행된다.
예를 들어, Java 프로젝트의 경우 ./gradlew clean compileQuerydsl build 명령으로 빌드를 수행한다.
Test 단계: 빌드가 성공적으로 완료되면, 단위 테스트를 자동으로 실행한다. ./gradlew test 명령을 사용하여 수행한다. 테스트가 실패하면, CI/CD Pipeline은 중단되며, 개발자는 피드백을 받아 문제를 수정해야 한다.
Deploy 단계: 모든 테스트가 성공적으로 통과하면, Jenkins는 deploy 작업을 수행하여 애플리케이션을 배포한다. Tomcat 서버에 WAR 파일을 배포하는 경우 deploy stage를 사용하여 Tomcat 서버에 애플리케이션을 배포한다.

4. Tomcat 서버에서의 WAR 파일 실행
배포 과정에서 Tomcat 서버는 새로운 WAR 파일을 인식하고, 이를 자동으로 풀어서 애플리케이션을 실행한다. Tomcat은 WAR 파일을 애플리케이션으로 변환하는 과정에서 필요한 환경 설정을 로드하고, 애플리케이션을 실행할 준비를 한다.

5. 서비스 확인 및 모니터링
배포가 완료되면, 개발 팀은 애플리케이션의 상태를 확인하고, 정상적으로 서비스가 제공되는지 모니터링한. 문제가 발견되면 수정 후 다시 CI/CD Pipeline을 통해 변경 사항을 배포한다.

Jenkins, WAR로 배포하는 이유?

Jenkins 사용 이유
1. 유연성과 확장성: Jenkins는 플러그인을 통해 대부분 종류의 개발, 테스트, 배포 작업을 자동화할 수 있다. GitHub, GitLab과 같은 다양한 소스 코드 관리 도구와 통합할 수 있으며, Slack, 이메일 등을 통한 알림 설정도 가능하다.
2. 강력한 커뮤니티와 지원: Jenkins는 오랜 기간 동안 널리 사용되어 온 오픈 소스 프로젝트로, 문제 해결, 새로운 기능 개발, 플러그인 업데이트 등에서 강력한 지원을 한다.
3. 커스터마이징이 용이한 Pipeline: Jenkins의 파이프라인은 Groovy 기반의 스크립트로 정의될 수 있으며, 빌드, 테스트, 배포 등의 작업을 세밀하게 제어할 수 있다.

WAR 배포 이유 및 외장 Tomcat 사용 장점
1. 분리된 환경: WAR 파일을 외장 Tomcat에 배포하여 애플리케이션과 서버 환경을 분리할 수 있다.
2. 보안: 외장 Tomcat 서버를 사용하면, 서버의 보안 설정을 애플리케이션과 독립적으로 관리할 수 있다. 접근 제어 설정, SSH/SSL 설정 등을 애플리케이션의 변경 없이 수행할 수 있다.
3. 다중 애플리케이션 배포: 여러 Tomcat 인스턴스를 분산 배치함으로써, 애플리케이션의 확장성을 크게 향상시킬 수 있다.

AWS EC2 서버 및 Jenkins 구축

AWS EC2 서버를 생성하여 Jenkins, Tomcat을 실행하였다.(해당 글은 Jenkins에 포커싱)
인바운드 규칙은 아래와 같다.

PORT사용처
ICMPVPC와 인터넷 또는 다른 VPC 간의 ICMP 트래픽을 허용하게 된다. 즉, VPC로 묶인 EC2 인스턴스 간에 ICMP를 통한 통신(예: ping)이 허용
22SSH 서비스의 기본 포트로, 서버 관리자가 서버에 원격으로 로그인하거나 파일을 전송할 때 사용
8080웹 애플리케이션을 실행하기 위한 대체 HTTP 포트
1024NCP(네이버 클라우드 플랫폼) 접속을 위해 사용한 특정 서비스나 관리 콘솔 접근 포트

Jenkins 설치 시 주의해야할 점은 내 프로젝트는 Java 17버전이어서 JDK 17을 설치했지만, Jenkins는 Java 11이하 지원이라 Java 11로 설정해야 설치가 정상적으로 된다.(설치 후 JAVA17로 변경해도 됨)
인바운드 규칙 ICMP 추가 후 Jenkins → Tomcat으로 Ping을 통해 AWS VPC 내에서 EC2 인스턴스 간 통신 확인

Terminus SSH 접속

ID: ec2-user, PWD: .pem key로 인증, IP Address: Jenkins AWS Public IP 기입


cat /var/lib/jenkins/secrets/initialAdminPassword을 통해 Jenkins 초기 암호 를 확인하고 Jenkins로 접속한다.

Jenkins Item Pipeline

아래는 프로젝트를 빌드하기 위한 설정 값들이다.

Git Webhook

Settings > Developer settings > Personal access tokens에서 Access Token 발급받아야 Jenkins에서 git repository webhook 계정 연동 가능

Jenkinsfile.groovy

Jenkins는 pipeline으로 CI/CD 프로세스를 구성한 이유는 다음과 같다.

  1. 코드로서의 인프라(IaC): 파이프라인을 Jenkinsfile에 코드로 정의함으로써, 빌드, 테스트, 배포 과정을 코드로 관리할 수 있다.
  2. 복잡한 워크플로우 지원: 파이프라인을 사용하면 여러 단계(Stages)로 이루어진 복잡한 워크플로우를 구성할 수 있다. 각 단계에서 빌드, 테스트, 배포 등의 작업을 정의하고, 조건부 로직을 사용해 특정 조건에서만 단계를 실행하도록 설정할 수 있다.
pipeline {
    agent any

    stages {
        stage('build') {
            steps {
                sh '''
                    chmod 755 gradlew
                    ./gradlew clean compileQuerydsl build -PskipAsciidoctor
                    '''
            }
        }
        stage('test') {
            steps {
                sh './gradlew test'
            }
        }
        stage('deploy') {
            steps {
                deploy adapters: [tomcat9(credentialsId: 'deployer_user', path: '', url: 'http://{tomcatIP}:8080/')], contextPath: null, war: '**/*.war'
            }
        }
    }
    post {
        success {
            echo "This will run when the run finished successfully"
        }
        failure {
            echo "This will run if failed"
        }
        changed {
            echo "This will run when the state of the pipeline has changed"
        }
    }
}

chmod 755 gradlew: gradlew 파일에 대해 소유자에게는 읽기, 쓰기, 실행 권한을 모두 부여하고, 그룹과 다른 사용자에게는 읽기와 실행 권한만 부여하는 것을 의미한다. 이는 gradlew 파일이 Gradle 빌드 스크립트 실행 파일이므로, 사용자가 이 파일을 실행할 수 있도록 설정

./gradlew clean compileQuerydsl build -PskipAsciidoctor:
clean: 이전에 생성된 빌드 파일을 삭제하여 초기 상태에서 시작한다. 빌드 과정에서 발생할 수 있는 문제(환경설정, 의존성, 컴파일 오류)를 방지하기 위해 사용한다.
compileQuerydsl: Querydsl 관련 소스 파일을 컴파일한다.
build: 프로젝트를 빌드 단계에서는 애플리케이션의 컴파일, 테스트, 패키징 등이 수행한다.
-PskipAsciidoctor: skipAsciidoctor라는 프로젝트 속성을 true로 설정하여, Asciidoctor를 사용한 문서 생성을 스킵한다.

배운점

코드를 작성하는 것이 개발의 시작일 뿐, 프로덕트가 실제로 사용자의 손에 닿는 순간까지를 아우르는 전 과정이 진정한 개발이라고 생각한다. 개발, 배포, 운영, 그리고 피드백에 이르기까지 모든 단계에서의 지속적인 협력과 통합을 강조하는 DevOps를 통해 더 나은 소프트웨어를 더 빠르게, 더 안정적으로 제공할 수 있는 길을 모색할 수 있어야한다.

다음 글은 Tomcat 서버 연동 및 배포로 찾아뵙겠다.

profile
Junior Developer

0개의 댓글

관련 채용 정보