[Mere] CI/CD 파이프라인 구축하고 성능 개선해보기

BangDori·2023년 12월 22일
0

Mere

목록 보기
4/5

Mere에서 Github actions를 활용하여 CI/CD 환경을 구축하고 있는데, CI, CD 시간이 너무 오래 걸린다는 문제가 발생하여서 이를 개선한 경험에 대해 공유해보겠습니다.

📕 목차
0. 들어가며
1. 문제 분석하기
2. 패키지 의존성 캐시를 활용하여 성능 개선하기
3. Artifact를 활용하여 성능 개선하기

0. 들어가며

저희 팀은 프론트엔드 팀원이 2명밖에 없습니다. 그런데도 굳이 CI/CD 파이프라인을 구축한 이유는 디자이너님과 더 원활한 소통을 하기 위해서였습니다.

저희 팀은 현재 학생들로 이루어져있어 각자 작업 시간이 다르기에, 매번 디자이너님과 디스코드의 화면공유 기능을 통해 소통하기에는 힘들었고 노션/깃허브 PR에 이미지를 추가하더라도 버튼 활성화/비활성화와 같은 인터랙션을 확인하기도 힘들었습니다.

그래서 디자이너님과 더 원활히 소통하기 위해 CD 파이프라인을 구축을 예정하고 있었는데, 이왕 하는김에 테스트 코드를 추가하진 못하더라도 lint를 이용해서 CI 까지 한번 해보자! 라는 생각으로 CI/CD 파이프라인을 구축하게 되었습니다!

1. 문제 분석하기

name: CI

on:
  push:
    branches: ['develop']
  pull_request:
    branches: ['develop']

jobs:
  lint:
  	  # ... lint script

  build:
      # ... build script 

기존에는 lint, build 라는 2개의 jobs을 구성한 상태로 workflow는 아래와 같이 구성되어 있었습니다.

시간이 오래 걸리는 문제점을 확인해보니, 각 job들이 독립된 환경에서 node_modules들을 각각 다운받고 있었습니다.

초기 CI

실제로, 초기에는 프로덕션 코드의 양이 많지 않음에도 CI를 진행하는데 1분 36초라는 시간이 소요되고 있었습니다. develop 브런치에 PR을 하고 1분 36초를 기다려야 한다는 말인데, 만약 workflow에 CD가 추가되게 된다면 2배 이상인 최소 3분이 걸리게 될 것이라고 생각하였고 개선이 필요하다고 생각하였습니다.

2. 패키지 의존성 캐시를 활용하여 성능 개선하기

각각의 job에서 node_modules들을 모두 새로 다운 받을필요 없이, 이전 버전과 동일하다면 최신 버전의 node_modules을 가져와 시간을 단축하는 캐싱 전략을 추가하였습니다.

name: CI

on:
  pull_request:
    branches: ['develop']

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
			# ... 버전 설정
			
			# Cache 의존성 검사
      - name: Cache dependencies
        id: cache
        uses: actions/cache@v3
        with:
          path: '**/node_modules'
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install Dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: npm install

      # ... 린트 실행

  build:
    runs-on: ubuntu-latest

    steps:
	  # ... 버전 설정


      - name: Cache dependencies
        id: cache
        uses: actions/cache@v3
        with:
          path: '**/node_modules'
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install Dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: npm install

      # ... 빌드 실행

패키지 의존성 캐시를 workflow에 추가한 후에, 실제로 ci에 걸리는 시간이 1분 36초에서 36초로 무려 1분이나 줄어든 것을 확인할 수 있었습니다.

개선된 CI

eslint 이미지build 이미지

내부에서 어떻게 동작하는지 확인해보니 dependencies들을 캐시해오면서 시간을 단축한 것을 확인할 수 있습니다.

잠깐 workflow script가 무엇인지 정확히 짚고 넘어가봅시다. 함께 스크립트를 따라가면서 이해해봅시다!

path: '**/node_modules'

  • 캐시할 경로를 지정합니다.
  • 현재는 node_modules 폴더로 지정한다는 의미입니다.

key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

  • 캐시의 고유 키를 생성합니다. 이 키는 현재 실행중이 OS (runner.os), 문자열 “-node-” 그리고 package-lock.json의 해시 값을 조합하여 생성합니다.
  • package-lock.json 파일의 내용이 변경되면 해시 값도 변경되기 때문에, 의존성이 변경될 때마다 새로운 해시 키가 생성됩니다.

restore-keys: | ${{ runner.os }}-node-

  • 위에 키를 생성할 때, 이전에 생성된 키 중 완벽하게 일치하는 캐시 키가 없을 때 가장 가까운 캐시 키를 복원합니다.
  • 이때, 가장 가까운 캐시 키를 복원한다는 의미는, 가장 가까운 캐시 키의 패키지 부분을 가져와서 사용한다는 의미입니다.
    • 캐시 키의 패키지 부분을 가져와서 사용하기 때문에 npm install 과정의 시간을 줄여줄 수 있습니다.

if: steps.cache.outputs.cache-hit != 'true' run: npm install

  • 앞서 실행한 cache step에서 캐시 히트(cache-hit)가 발생하지 않았을 경우(즉, 캐시된 node_modules가 없을 경우) package들을 다운받습니다.

이제 firebase에서 제공해주는 기능 중 하나인 preview channels와 deploy를 추가해보겠습니다. lint와 build의 경우에는 실제로 아래와 같이 Dependencies가 캐싱이 잘 되어 오고 있었지만,

CD 추가

firebase에서의 호스팅 시간이 20~30초가 걸리는 것을 감안하고 확인을 해도, firebase preview hosting 이전 시간이 22초나 걸려서 총 53초가 소요되는 것을 확인하였습니다.

3. Artifact를 활용하여 성능 개선하기

현재 문제점을 확인해보니 캐시 파일을 가져올 때 너무 오랜 시간이 걸린다는 점과 이전에 build job에서 build를 했음에도 불구하고 새로운 build를 진행하는 것이 문제였습니다.

현재 build와 build_and_preview가 하나의 job에서 진행되는 것이 아닌 각자 독립된 환경에서 실행되고 있기 때문에 Artifact를 이용하여 빌드 결과물을 저장하고 외부 패키지를 다운 받도록 하였습니다.

Artifact

  • 각 Job들은 독립된 환경을 가지고 독립적으로 실행됩니다. 하지만 독립된 환경에서 외부의 패 키지를 받을 수 있도록 하기 위해, GitHub Actions Artifacts를 이용할 수 있습니다.
  • GitHub Actions Artifacts를 이용하면, CI/CD 워크플로우를 실행할 때 다른 단계나 job에 전달할 수 있는 기능을 제공해 이전에 빌드된 파일을 다른 Job에서 가져와서 사용할 수 있습니다.

Artifact는 크게 두 가지의 기능을 가집니다.

  1. actions/upload-artifact
  2. actions/download-artifact

upload-artifact는 build된 파일들을 artifact에 업로드해주는 역할을 하는 github action이며, download-artifact는 업로드된 artifact에서 build된 파일을 불러올 수 있는 github action입니다.

build-artifact

build 파일을 artifact에 저장하고, build_and_preview에서 이를 다운로드 받아서 사용하는 방식을 활용하였더니 32초로 줄어든 것을 확인할 수 있었습니다.

build artifact log

cache 기능을 활용한 것이 아니기에 cache dependencies 로그는 없어진 것을 확인할 수 있고, build파일을 artifact에서 가져오는 방식을 이용해서 firebase preview 호스팅 이전 시간이 22초에서 7초나 줄어들었습니다!. 총 시간은 53초에서 32초로 줄어들게 되었네요!

이제 개발에만 집중할 수 있겠군요! 히히 😆

happy

참고

profile
Happy Day 😊❣️

0개의 댓글