DevOps/DevOps

[DevOps] 멀티모듈 프로젝트 CI/CD 적용 - 설계 구상 편

sebinChu 2024. 7. 22. 20:02

개요


실제 운영 환경 인프라를 구축하기 위해 설계를 팀에 공유해보기로 하였다. 사실 3달 정도 전에 Github Actions + Elastic Beanstalk으로 실습을 해보았으나, 실패했던 경험이 있기에 다시 돌아와서 설계 과정을 세세히 기록해본다.
 
또한, 이전에는 사용해보고 싶었던 기술(Jenkins)과 추천 받은 기술로 구성하였다면, 이번엔 온전히 내 논리와 지식을 활용하여, 배포를 하자마자 사용자가 생길 우리 서버..🤗의 아키텍처를 구상해보려고 한다.
 
 
 

1) 이전 시도 복기와 현재 상황


나에게 가장 까다롭게 다가오는 것은 멀티 모듈 설계와 AWS 사용이다. 이 까다로운 문제를 하나씩 풀어 나가는 과정을 작성해보자..!
 

1-1) 모듈 구조 파악하기

우리 프로젝트는 현재 core가 라이브러리 형식으로 하위 프로젝트 2개에 사용되고 있다.

 

  • core(공통 라이브러리)
  • admin(관리자 모듈)
  • user(사용자 모듈

 

1-2) 인턴십에 수행했던 PoC를 떠올려 보자.

나는 이미 멀티 모듈 구조의 CI/CD를 설계 및 구축, 운영한 경험이 있다.
멀티 모듈을 자동으로 빌드 및 배포하기 위해서는 다음과 같은 동작 순서를 따른다.
 

  1. 공통 라이브러리 모듈을 빌드 및 저장소에 push
  2. 하위 모듈들이 저장소에 있는 공통 라이브러리를 의존하여 빌드 및 실행

 
GCP에서는 이 과정을 Cloud Build와 Artifact Registry로 수행이 가능하다. 정확히 말하자면 70% 정도만 가능하다. 그 이유는 Instance Group으로 생성되는 인스턴스들이 Registry의 파일을 가져와 자동으로 배포하려면, Terraform이나 엔서블 등과 같은 도구를 통해 컨트롤 해야한다. 당시 나에게 주어진 시간이 많이 없었기에 빌드만 자동화하였다.
 

  • 해당 레포에서 필자가 수행한 PoC를 확인할 수 있다. MVN, NPM, Docker에 대한 자료가 있으니, 참고하길 바란다.
 

GitHub - cobinding/gcp-docker-ci-cd: [DevOps] Docker와 GCP로 CI/CD 구축

[DevOps] Docker와 GCP로 CI/CD 구축. Contribute to cobinding/gcp-docker-ci-cd development by creating an account on GitHub.

github.com

 
 

1-3) 현재 프로젝트는 AWS를 사용

나의 경험을 살려서 멀티 모듈 CI/CD 구조를 어림잡아 생각해보면 ① 빌드 자동화 도구 ② 공통 라이브러리 저장소 ③ 배포 자동화 도구 이 세 가지가 필요하다. 이에 상응하는 AWS 기술은 codebuild, s3, code deploy 등이 있다.
 
현재 진행하는 CS 스터디에서 스터디원이 위 세 가지 조합 비교하여 github actions + elastic beanstalk 구조에 대해 발표한 적이 있다. 이를 참고하여 해당 구조를 적용해보자! 팀원의 블로그 🤗
 
 

2) 멀티모듈 CI/CD를 컨트롤할 github actions 기술 이해


2-1) 기존에 사용했을 때 실패했던 이유

개요에서 밝혔듯, 이 기술을 이미 써본 적이 있지만, 당시에 제대로 구축을 못 했었다. 실패했던 이유는 회사에서 PoC를 진행했을 땐 이미 서버, 인프라 구조가 이미 설계 및 구축되어 있었기에 이에 맞춰서 CI/CD에만 집중할 수 있었다. 그런데 지금 프로젝트에서는 인프라 자체를 스스로 설계 및 구상해야 해서 혼동한 점이 많았다. (멀티 모듈 인프라는 직접 설계 및 구축 해보는 것이 이번 프로젝트에서 처음이었다.. 😂)  그래서 공통 라이브러리를 의존하는 부분이나 개별 인스턴스에 github actions와 EB를 세팅해야 하는 부분 등을 놓쳤었다.
 
결론적으로 기술 이해도 부족/약간의 조급함ㅎ이 문제였고 백엔드 팀장님과 날밤까며 계속 시도를 했지만 원인 파악도 제대로 하지 못하고 실패를 했었다.. 😂 다시 돌아간다면 나의 경험을 떠올리면서 이 프로젝트에 적용을 어떻게 할지 침착하게 구상 및 설계했을 것같다..!
 

 
 

2-2) github actions 조건문 분기로 변동사항이 있는 모듈만 빌드하기!

아무튼, 위 사고의 과정을 따라 프로젝트에 적용해야할 사항들을 작성해보자.
멀티 모듈 CI/CD를 위해 요구되는 사항과 해결 방법은 다음과 같다.
 
1. 한 모듈이 수정되면 해당 모듈만 빌드 및 배포되어야 한다. 

  • 한 모듈이 수정될 때 모든 모듈이 같이 빌드 및 배포한다면 아무리 자동화가 되어있다하더라도 비효율적이다. 변동 사항 없이 잘 돌아가고 있는 모듈까지 재빌드 및 재실행하기보다, 변동사항이 생긴 모듈만 빌드 및 배포되도록 설정하자.
  • github actions는 yaml 파일처럼 동작 과정을 작성할 수 있다.

 
예시 코드

name: CI/CD Pipeline

on:
  push:
    branches:
      - main
    paths:
      - 'common/**'  # common 모듈에 대한 변경
      - 'admin/**'   # admin 모듈에 대한 변경
      - 'user/**'    # user 모듈에 대한 변경

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up JDK
        uses: actions/setup-java@v2
        with:
          java-version: '11'  # 필요한 Java 버전으로 변경

      - name: Build and test
        run: |
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^common/ ]]; then
            cd common
            ./gradlew build
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^admin/ ]]; then
            cd admin
            ./gradlew build
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^user/ ]]; then
            cd user
            ./gradlew build
            cd ..
          fi

      - name: Deploy to Elastic Beanstalk
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'your-aws-region'
        run: |
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^common/ ]]; then
            cd common
            ./gradlew assemble
            zip -r application.zip build/libs/*.jar
            eb init your-app-name --region $AWS_REGION
            eb deploy
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^admin/ ]]; then
            cd admin
            ./gradlew assemble
            zip -r application.zip build/libs/*.jar
            eb init your-app-name --region $AWS_REGION
            eb deploy
            cd ..
          fi
          if [[ $(git diff --name-only HEAD^ HEAD) =~ ^user/ ]]; then
            cd user
            ./gradlew assemble
            zip -r application.zip build/libs/*.jar
            eb init your-app-name --region $AWS_REGION
            eb deploy
            cd ..
          fi

 
 
2. 하위 모듈들은 각각의 EB를 가지면서 이 공통 라이브러리를 항상 의존해야한다.

  • 이는 EB의 .ebextensions 파일로 컨트롤 한다.

 
 

3) 결론 및 정리


3-1) Github Actions

하나의 github actions가 여러 EB를 포인팅하고 있는 구조를 갖는다. 이 구조에서 하위 모듈들이 독립적으로 빌드될 수 있도록 조건분기를 실행파일에 설정한다. 
* 여기서는 빌드 과정만 신경 쓴다!
 

  1. 이벤트 기반 트리거
    • 특정 이벤트(나의 경우 merge)에 반응하여 자동으로 워크플로우를 실행한다.
    • on 키워드를 사용하여 어떤 이벤트가 발생할 때 워크플로우가 실행될지를 정의한다. ➡️ 설계에서 정의
  2. 워크플로우 정의:
    • YAML 형식으로 워크플로우를 정의합니다. 각 워크플로우는 여러 개의 작업(Job)으로 구성될 수 있다.
    • 각 작업은 특정 환경에서 실행되며, 다양한 명령어와 스크립트를 실행할 수 있다.
  3. 작업 및 단계:
    • 각 작업은 여러 단계를 포함 (예를 들어 코드 체크아웃, 의존성 설치, 테스트 실행, 빌드 생성 등)을 포함할 수 있다.
      • 하위 모듈들은 저장소에 저장된 공통 라이브러리의 의존성을 설치하고 빌드를 생성한다.
      • 하위 모듈들은 이 과정을 통해 각각의 모듈을 자신의 변경사항이 바뀌었을 때만 빌드하는 것에 집중한다.
      • 하위 모듈이 공통 라이브러리를 의존하는 것은 EB의 .ebextensions에서 진행한다.

 

3-2) Elastic Beanstalk

 

  1. 배포 패키지
    • Elastic Beanstalk에 배포할 애플리케이션은 ZIP 파일 형식의 배포 패키지로 준비되어야 한다. 
    • EB 배포를 커스텀하기 위해 .ebextensions/ 에 옵션을 작성한다. 
      • ebextenstions: EB를 실행할 때 최초로 실행되는 파일
      • 하위 모듈은 저장소에 저장된 공통 라이브러리 의존
      • 공통 모듈은 빌드된 파일을 저장소에 push

 
 
다음 편에서는 직접 설계한 것과 설정 파일들을 다뤄보겠다. 쉽지 않은 길이 예상된다... 그래도 아자아자 🤗