배포 자동화 프로젝트(3) - CI/CD, Github Action

신혜린·2024년 6월 5일
0

💡 참고자료
https://velog.io/@sangwoong/CICD-GitHub-Action으로-CICD-구축하기
https://rachel0115.tistory.com/entry/Github-Actions로-CICD-구축하기-EC2-S3-CodeDeploy

어떤 툴을 이용해 배포를 할 것인지 정했고 (S3, CloudFront), 그럼 이제 배포 자동화 구축을 위해선 어떤 CI/CD 툴을 이용할 건지, 사용법은 어떤지 간략하게 알아보자.



📂 CI/CD 란?


CI 지속적인 통합

  • 코드 변경 사항이 발생 (push) 할 때마다 자동으로 빌드 및 테스트를 실행한다.
  • 자주 코드를 통합해야 코드가 충돌되는 현상을 미리 발견할 수 있다.
  • 프로덕트 품질 관리 및 버그 발견이 빨라진다.

CD 지속적인 배포

  • 코드 변경 사항이 테스트 및 승인을 거쳐 자동으로 프로덕션 환경에 배포(merge to main)된다.
  • 새로운 기능과 버그 수정 사항이 실제 사용자에게 빠르게 제공된다.
  • 사용자 피드백을 수집하고 제품을 개선하는 속도를 향상시킬 수 있다.

📂 CI/CD가 필요한 이유


테스트 서버의 존재 유무

  • 운영 서버로 배포하기 전 단계인 테스트 서버가 존재하고 있기 때문에 테스트 서버로의 자동화된 테스트를 통해 버그를 빠르게 식별하고 개선할 수 있을 것이다.

불필요한 비용 절감

  • 자동화된 프로세스를 통해 수동 작업을 줄여 불필요한 시간 낭비를 줄일 수 있을 것이다.

직관적인 업무 프로세스 공유

  • CI/CD는 개발자가 서로 어떤 작업을 했고, 블로커가 무엇인지 직관적으로 확인할 수 있는 도구이다.
    • CI/CD를 활용하면 커뮤니케이션 스텝을 한 단계 줄여 각 팀원의 작업을 더 쉽게 확인할 수 있는 협업 도구가 될 것이다.

개발자가 개발에만 집중할 수 있는 환경이 되도록

  • 불필요한 비용을 절감하고, 서로의 업무 프로세스를 좀 더 직관적으로 공유할 수 있게 된다면 개발에 더욱 집중할 수 있는 환경이 마련될 것이다.
    • 프로덕트의 품질 향상을 위해 반드시 필요한 환경 마련

📂 Github Action


  • CI/CD 에는 여러 종류의 툴이 존재한다
    • Github Action, Jenkins, Travis CI, CircleCI 등
    • AWS Lamda와 같은 서버리스 플랫폼 이용
    • Docker 또는 Kubernetes를 사용한 컨테이너 기반 애플리케이션 배포

➡️ 이 중 현업에서 많이 사용하면서 무료로 사용할 수 있고 빌드용 서버가 따로 필요 없는 Github Action을 사용하기로 함.

  • Github Action을 사용하면 코드의 통합과 배포 프로세스를 자동화하여 개발 생산성을 향상시킬 수 있다.

yaml 파일 생성 위치


root
.github
	ㄴ workflows
		 ㄴ cicd-file-name.yml

-> .github/workflows/cicd.yml



구성요소 (Workflow, Event, Jobs, Actions, env)


Workflow

  • 한개 이상의 Job (CI/CD)를 실행할 수 있는 일종의 프로세스
  • YAML 파일과 문법으로 구성되며 내부 스크립트에 정의된 event로 워크 플로우가 실행된다.

Event

  • workflow가 실행되기 위한 특정한 행동을 가리킨다.
  • ex) push, pull request, merge

Jobs

  • 특정 이벤트에 따라 처리하는 프로세스를 구분하고 정의한다.
  • 각각의 step으로 나뉘고, 이 step은 shell에서 동작하는 CLI와 동일하게 실행된다.
  • 정의한 step 순서대로 실행되며, step 별로 동일한 환경변수를 지정할 수 있어 데이터를 공유할 수 있다.
    • 하나의 job이 실행되기 전에 다른 job이 무조건 실행되어야 하는 것처럼 의존 관계 형성 가능
    • 즉, 병렬적 실행이 가능하다.

Actions

  • 반복되는 코드를 모듈이나 함수로 관리하는 것처럼, 복잡하고 자주 사용되는 작업을 정의한다.
  • 워크플로우 내 자주 반복되는 스크립트를 미리 정의하여 좀 더 효율적으로 관리할 수 있게 한다.
    • Github 마켓 플레이스에 퍼블릭하게 배포해 놓은 action들이 많이 있으니 참고할 것.

env

  • repository 별로 환경변수를 독립적으로 설정할 수 있다.
    • Github repository → settings → secrets and variables (actions) → New repository secret
- name: Environment Setup
	run: ${{ secrets.등록한_환경변수_이름 }}


📂 FrontEnd 배포 프로세스 (S3, CloudFront)


🔗 FE CI/CD pipeline
1. 특정 브랜치에 push나 merge 동작 수행
2. Github Actions 에서 push 혹은 merge 를 감지하고 정해진 action 을 수행
3. 업데이트된 코드를 바탕으로 Build 수행
4. 프론트엔드 빌드 및 S3 업로드
5. CloudFront 캐시 무효화

name: Deploy React App

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

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

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14'

      - name: Install dependencies
        run: |
          npm install

      - name: Build project
        run: |
          npm run build

      - name: Deploy to S3
        run: |
          aws s3 sync build/ s3://your-frontend-bucket --delete
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Invalidate CloudFront cache
        run: |
          aws cloudfront create-invalidation --distribution-id your-distribution-id --paths "/*"
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}


📂 BackEnd 배포 프로세스 (EC2, CodeDeploy)


  • 배포 툴은 다양하게 많이 있으나, AWS의 CodeDeploy를 사용할 때를 예시로 들었다.

🔗 BE CI/CD pipeline
1. 특정 브랜치에 push나 merge 동작 수행
2. Github Actions 에서 push 혹은 merge 를 감지하고 정해진 action 을 수행
3. 업데이트된 코드를 바탕으로 Build 와 Test 수행
4. 코드를 압축하여 Zip 파일 생성
5. S3에 zip 파일 업로드
6. CodeDeploy 에 배포 요청
7. S3로부터 zip 파일 다운로드
8. 지정한 EC2 인스턴스에 애플리케이션 파일 전달
9. EC2 인스턴스에서 쉘 스크립트를 실행함으로써 배포 완료

# Deploy.yml

name: Deploy Backend with Gradle

on:
  push:
    branches:
      - main

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

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

      - name: Set up JDK
        uses: actions/setup-java@v2
        with:
          distribution: 'adopt'
          java-version: '11'

      - name: Build with Gradle
        run: ./gradlew build

      - name: Run tests with Gradle
        run: ./gradlew test

      - name: Zip build files
        run: zip -r backend.zip build/libs/

      - name: Upload to S3
        run: aws s3 cp backend.zip s3://your-backend-bucket/backend.zip
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Deploy to EC2 using CodeDeploy
        run: |
          aws deploy create-deployment \
            --application-name your-codedeploy-app \
            --deployment-group-name your-codedeploy-deployment-group \
            --s3-location bucket=your-backend-bucket,key=backend.zip,bundleType=zip
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# appspec.yml
# CodeDeploy가 파일을 어디에 배치하고 어떤 스크립트를 실행할지 정의한다

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/app
hooks:
  AfterInstall:
    - location: deploy.sh
      timeout: 300
      runas: ec2-user
# deploy.sh 스크립트
#!/bin/bash

# 애플리케이션 디렉토리로 이동
cd /home/ec2-user/app

# Gradle을 사용하여 애플리케이션 실행
./gradlew bootRun


📂 기타 참고사항


참고1) Cache 설정

  • Github Action은 cache를 제공한다.
    • package-lock.json 의 경우, 새로운 작업 소스 코드와 비교했을 때 변동 사항이 없을 시 굳이 디펜던시를 다시 설치할 필요 없음.
    • 해시값으로 바꾸어 해당 파일을 캐싱할 수 있다.
- name: Cache Dependencies
		id: dependecy-cache
		uses: actions/ache@v3
		with:
			path: "**/node_modules" 
			# 캐시 대상이 되는 파일
			# node_module 디렉토리의 하위 내용을 모두 캐시하겠다는 의미
			
			key: npm-package-${{ hashFiles('**/package-lock.json') }}
			# 캐시를 식별할 때 사용하는 옵션
			# 해싱이 되어 저장된 package-lock.json 파일과 
			# 새로운 commit 코드의 package-lock.json 파일이 일치하는지 확인
			
			restore-keys: |
				npm-package-${{ hashFiles('**/package-lock.json') }}
				npm-package-
			# 캐싱이 되어있는 해시 파일을 찾는다
			# 해시 파일을 찾는 기준은 restore-keys에 등록된 순으로 찾는다
			# Caches에 npm-package-1, npm-package-2 라는 파일이 있다고 가정할 때,
			# package-lock.json의 해싱 파일이 npm-package-1 로 되어 있다면 npm-package-1을 기준 캐시 데이터로 저장한다/
			# 지정된 캐시 파일은 commit의 package-lock.json 파일과 비교하는 작업을 수행한다

참고2) Slack Message

Slack API의 수신웹후크를 사용하여 CI/CD의 결과를 Slack 메세지로 보내는 방법

  • 앱 추가 절차

    1. 앱 관리 > Incoming Webhook 검색 > 원하는 채널에 추가
    2. 구성 편집의 웹훅 URL 정보 확인 (Github Repository의 환경변수로 지정해준다.)
  • 슬랙 메시지 전송 job 구성하기

    서버에 코드를 배포하는 job이 종료되면 CI/CD의 결과를 슬랙 메시지로 전송하기 위한 job

    jobs:
    	slack_notification:
    		if: ${{ always() }}
    		needs: [ local, test, main ]
    		runs-on: ubuntu-latest
  • 슬랙 메시지 커스터마이즈 서버 배포의 결과에 따라 상태메시지(STATUS)와 슬랙에서 사용될 이모지를 커스터마이징 할 수 있다.
      - name: CUSTOM_STATUS SET UP
        run: |
          if [ "${{ github.ref }}" = "refs/heads/develop" ];
          then
            BUILD_NAME="Staging Deploy"
          elif [ "${{ github.ref }}" = "refs/heads/main" ];
          then
            BUILD_NAME="Production Deploy"
          else
            BUILD_NAME="Local Build & Test"
          fi
    • if문을 통해 조건을 생성한다.
    • github.ref 라는 변수에 접근하여 Github Action이 실행될 때 저장되는 branch의 이름을 받아온다.
      • 슬랙 메시지에 보여질 BUILD_NAME 변수에 값을 동적으로 지정해줄 수 있다.
    • fi 를 작성하여 구문을 종료한다.
profile
개 발자국 🐾

0개의 댓글