GitHub Actions를 활용한 S3 + Cloudfront 자동 배포

Jongco·2023년 12월 28일
4

Intro

GitHub Actions는 소프트웨어 개발 워크플로우를 자동화하는 강력하고 유연한 도구를 제공합니다.
진행한 사이드 프로젝트에서는 테스트와 운영을 위해 두 개의 환경(staging, production)에 대한 셋팅이 필요했습니다.
GitHub Actions 워크플로우를 통해 AWS 서비스를 활용하여 자동으로 배포하는 방법을 살펴보겠습니다.

워크플로우

워크플로우 진행 과정은 다음과 같습니다.

name: STAGING-AWS-DEPLOY

#트리거
on:
  push:
    branches:
      - dev

jobs:
  build-and-upload:
    runs-on: ubuntu-latest
	
    #각 단계
    steps:
      - uses: actions/checkout@v3

		# 환경변수 등록
      - name: Generate Environment Variables File for Production
        run: |
          echo "REACT_APP_KAKAO_CLIENT_ID=$REACT_APP_KAKAO_CLIENT_ID" >> .env.staging
          echo "REACT_APP_SERVICE_URL=$REACT_APP_SERVICE_URL" >> .env.staging
          echo "REACT_APP_SERVER_URL=$REACT_APP_SERVER_URL" >> .env.staging
        env:
          REACT_APP_KAKAO_CLIENT_ID: ${{ secrets.DEV_REACT_APP_KAKAO_CLIENT_ID }}
          REACT_APP_SERVICE_URL: ${{ secrets.DEV_REACT_APP_SERVICE_URL }}
          REACT_APP_SERVER_URL: ${{ secrets.DEV_REACT_APP_SERVER_URL }}

		# 의존성 install
      - name: Install Dependencies
        run: yarn install

		# 빌드
      - name: Build
        run: yarn run build:staging

		#빌드한 파일 S3 업로드
      - name: Upload S3 bucket
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --acl public-read --follow-symlinks --delete
        env:
          AWS_S3_BUCKET: ${{ secrets.DEV_AWS_S3_BUCKET }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'ap-northeast-2'
          SOURCE_DIR: 'dist'
          DEST_DIR: 'dist'

		# CloudFront 캐시 무효화
      - name: Invalidate CloudFront
        uses: chetan/invalidate-cloudfront-action@master
        env:
          PATHS: '/*'
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'ap-northeast-2'
          DISTRIBUTION: ${{ secrets.DEV_AWS_DISTRIBUTION_ID }}
  1. 트리거: dev 브랜치에 대한 모든 푸시에서 트리거됩니다.
  2. 환경 변수 파일 생성: GitHub에 저장된 Secret 정보를 기반으로 환경 변수 파일을 생성합니다.
  3. 의존성 설치: Yarn을 사용하여 프로젝트 의존성을 설치합니다.
  4. 빌드: 지정된 package.json의 명령어를 사용하여 프로젝트를 빌드합니다.
  5. S3 버킷에 업로드: 빌드된 파일을 S3 버킷에 업로드합니다.
  6. CloudFront 무효화: 지정된 CloudFront 배포를 무효화하여 변경 사항을 반영합니다.

트리거

github actions의 발동 시점을 설정합니다. 트리거에는 다양한 옵션이 있습니다.
저희는 dev 브랜치에 코드가 push되었을 때 자동적으로 배포되도록 설정해 두었습니다.

#트리거
on:
  push:
    branches:
      - dev

환경변수 등록

		# 환경변수 등록
      - name: Generate Environment Variables File for Production
        run: |
          echo "REACT_APP_KAKAO_CLIENT_ID=$REACT_APP_KAKAO_CLIENT_ID" >> .env.staging
          echo "REACT_APP_SERVICE_URL=$REACT_APP_SERVICE_URL" >> .env.staging
          echo "REACT_APP_SERVER_URL=$REACT_APP_SERVER_URL" >> .env.staging
        env:
          REACT_APP_KAKAO_CLIENT_ID: ${{ secrets.DEV_REACT_APP_KAKAO_CLIENT_ID }}
          REACT_APP_SERVICE_URL: ${{ secrets.DEV_REACT_APP_SERVICE_URL }}
          REACT_APP_SERVER_URL: ${{ secrets.DEV_REACT_APP_SERVER_URL }}

환경변수 등록 과정은 다음과 같습니다.

  • 프로젝트 repository에 Settings - Security - Secrets and variables - Actions로 들어가서 New repository secret 클릭
  • Secret의 NameSecret을 설정합니다.
    • 여기에서 Name은 꼭 프로젝트에서 사용하는 환경변수 이름과 동일할 필요는 없습니다.

그 후 yml에서
echo "REACT_APP_KAKAO_CLIENT_ID=$REACT_APP_KAKAO_CLIENT_ID" >> .env.staging
명령어로 환경변수를 등록한 후,
아래와 같이 프로젝트에서 사용하는 환경변수: Secret에 등록한 환경변수 형태로 연결해줍니다.

저희는 웹팩에서 빌드할 때 env 파일을 지정할 수 있도록 package.json을 아래와 같이 설정했습니다.
"build:staging": "cross-env NODE_ENV=staging webpack"
만약 파일 명이 .env.test라면
echo "REACT_APP_KAKAO_CLIENT_ID=$REACT_APP_KAKAO_CLIENT_ID" >> .env.test
로 설정하면 됩니다.
(따로 설정하지 않으면 빌드 후 환경변수가 undefined를 반환합니다..😢)

env:
    REACT_APP_KAKAO_CLIENT_ID: ${{ secrets.DEV_REACT_APP_KAKAO_CLIENT_ID }}
    REACT_APP_SERVICE_URL: ${{ secrets.DEV_REACT_APP_SERVICE_URL }}
    REACT_APP_SERVER_URL: ${{ secrets.DEV_REACT_APP_SERVER_URL }}

의존성 설치 및 빌드

- name: Install Dependencies
  run: yarn install

- name: Build
  run: yarn run build:staging

의존성을 설치하고, build를 진행합니다.
package.json에 설정한 build 명령어를 사용하면 빌드를 진행할 수 있습니다.

"build:staging": "cross-env NODE_ENV=staging webpack"

S3 + Cloudfront

		#빌드한 파일 S3 업로드
      - name: Upload S3 bucket
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --acl public-read --follow-symlinks --delete
        env:
          AWS_S3_BUCKET: ${{ secrets.DEV_AWS_S3_BUCKET }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'ap-northeast-2'
          SOURCE_DIR: 'dist'
          DEST_DIR: 'dist'

		# CloudFront 캐시 무효화
      - name: Invalidate CloudFront
        uses: chetan/invalidate-cloudfront-action@master
        env:
          PATHS: '/*'
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_REGION: 'ap-northeast-2'
          DISTRIBUTION: ${{ secrets.DEV_AWS_DISTRIBUTION_ID }}

빌드한 파일을 S3에 업로드하고, 배포되어 있는 CloudFront의 캐시를 무효화합니다.
CloudFront의 캐시를 무효화 하지 않으면, 각각 CDN에 적용되어 있는 캐시가 만료되기 전까지는 새롭게 적용된 컨텐츠를 확인할 수 없기에 꼭 필요한 작업입니다.

s3 업로드에 사용한 오픈소스

AWS_S3_BUCKET: ${{ secrets.DEV_AWS_S3_BUCKET }} #버킷 이름
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} #IAM Access key
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # Secret key
AWS_REGION: 'ap-northeast-2' # <- 서울 리전
SOURCE_DIR: 'dist' # <- 빌드한 결과물 중 S3에 올릴 파일
DEST_DIR: 'dist' # <- S3 어디에 올릴 건지 !

CloudFront distribution ID to operate on, e.g., ''

이제 마지막으로 CloudFront 무효화를 진행합니다.
들어가는 요소는 위와 거의 동일하지만 CloudFront에는 Distribution Id를 넣어줘야 합니다.
(EDFDVBD6EXAMPLE << 이렇게 생긴 아이디!)

- name: Invalidate CloudFront
  uses: chetan/invalidate-cloudfront-action@master
    env:
    	PATHS: '/*' # <- 초기화 시킬 경로
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # 위와 동일
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # 위와 동일
        AWS_REGION: 'ap-northeast-2' # 위와 동일
        DISTRIBUTION: ${{ secrets.DEV_AWS_DISTRIBUTION_ID }} # <- CloudFront distribution Id

결과물

배포가 성공적으로 된 것을 확인할 수 있습니다.
각 단계에서 정한 name을 통해 중간중간 오류가 발생한다면 어디에서 발생했는지,
어떤 부분을 고쳐야 하는지 알 수 있습니다 😊

1개의 댓글

comment-user-thumbnail
2024년 3월 24일

퍼가요~♡

답글 달기