SUIN

[TIL] Github Action Deploy to AWS 처리 내가 마주한 이슈 본문

TIL

[TIL] Github Action Deploy to AWS 처리 내가 마주한 이슈

choi suin 2023. 3. 28. 01:43
728x90

v2 시작되는 프로젝트에서 기존 v1에서 자동 배포된 내용을 참고해서 그대로 반영할 것인가 
조금 더 도전해 볼 것인가 고민하던 난 이왕 다시 하는 거 새로 만들어보자! 하며 바로 도전해 보았다
기존 내가 알고 사용하던 워크플로우 

name: CI-CD
on:
  pull_request:
    branches:
      - main
jobs:
  deploy:
    name: 'Build & Deploy'
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: ${{ secrets.NODE_VERSION }}
          
      - name: Cache node modules     
        uses: actions/cache@v2
        id: cache
        with:
          path: node_modules
          key: npm-packages-${{ hashFiles('**/package-lock.json') }}

      - name: Install Dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: yarn install
        
      - name: setting .env
        run: |
         echo "REACT_APP_SERVER_URL=$REACT_APP_SERVER_URL" >> .env.production
         echo "REACT_APP_BUCKET_NAME=$REACT_APP_BUCKET_NAME" >> .env.production
         echo "REACT_APP_REGION=$REACT_APP_REGION" >> .env.production
         echo "REACT_APP_ACCESS=$REACT_APP_ACCESS" >> .env.production
         echo "REACT_APP_SECRET=$REACT_APP_SECRET" >> .env.production
         echo "REACT_APP_BUCKET_URL=$REACT_APP_BUCKET_URL" >> .env.production
        env:
         REACT_APP_SERVER_URL: ${{ secrets.REACT_APP_SERVER_URL }}
         REACT_APP_BUCKET_NAME: ${{ secrets.REACT_APP_BUCKET_NAME }}
         REACT_APP_REGION: ${{ secrets.REACT_APP_REGION }}
         REACT_APP_ACCESS: ${{ secrets.REACT_APP_ACCESS }}
         REACT_APP_SECRET: ${{ secrets.REACT_APP_SECRET }}
         REACT_APP_BUCKET_URL: ${{ secrets.REACT_APP_BUCKET_URL }}
 
      - name: Build
        run: unset CI && yarn build

      # aws user 연결
      - name: Configure AWS credentials 
        uses: aws-actions/configure-aws-credentials@v1 
        with: 
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 
          aws-region: ap-northeast-2

      #  /build를 s3로 업로드
      - name: Upload to S3
        env:
          BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME}}
        run: |
          aws s3 sync \
            ./build s3://$BUCKET_NAME
            
      # 업로드한 s3 파일을 각 CDN 캐시 무효화하여 리프레시 하기
      - name: CloudFront Invalidation
        env:
          CLOUD_FRONT_ID: ${{ secrets.DISTRIBUTION_ID}}
        run: |
          aws cloudfront create-invalidation \
            --distribution-id $CLOUD_FRONT_ID --paths "/*"

 
새롭게 도전한 워크플로우 

name: Deploy to AWS

on:
  push:
    branches:
    - main
  workflow_dispatch:

jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@master

      - uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: Cache node modules
        id: npm-cache
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      - if: steps.npm-cache.outputs.cache-hit == 'true'
        run: echo 'npm cache hit!'
      - if: steps.npm-cache.outputs.cache-hit != 'true'
        run: echo 'npm cache missed!'
      - run: npm ci

      - name: Generate Environment Variables File for Production
        run: |
          echo "REACT_APP_SERVER_URL=$REACT_APP_SERVER_URL" >> .env.production
          echo "REACT_APP_BUCKET_NAME=$REACT_APP_BUCKET_NAME" >> .env.production
          echo "REACT_APP_REGION=$REACT_APP_REGION" >> .env.production
          echo "REACT_APP_ACCESS=$REACT_APP_ACCESS" >> .env.production
          echo "REACT_APP_SECRET=$REACT_APP_SECRET" >> .env.production
          echo "REACT_APP_BUCKET_URL=$REACT_APP_BUCKET_URL" >> .env.production
        env:
          REACT_APP_SERVER_URL: ${{ secrets.REACT_APP_SERVER_URL }}
          REACT_APP_BUCKET_NAME: ${{ secrets.REACT_APP_BUCKET_NAME }}
          REACT_APP_REGION: ${{ secrets.REACT_APP_REGION }}
          REACT_APP_ACCESS: ${{ secrets.REACT_APP_ACCESS }}
          REACT_APP_SECRET: ${{ secrets.REACT_APP_SECRET }}
          REACT_APP_BUCKET_URL: ${{ secrets.REACT_APP_BUCKET_URL }}

      - name: Build
        run: unset CI && npm run build

      - name: deploy to s3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --delete
        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME}}
          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: 'build'

      - name: Invalidate CloudFront
        uses: chetan/invalidate-cloudfront-action@v2
        env:
          DISTRIBUTION: ${{ secrets.DISTRIBUTION_ID }}
          PATHS: "/index.html"
          AWS_REGION: "ap-northeast-2"
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

 

내가 알게 된 내용. 정확하게 알았던 내용  

 

1. 동작 이벤트

name :내가 만든 동작은 CI-CD가 아닌 CD 이므로 name 수정 
Event :  기존 main으로 pull_request  동작시 액션이 수행되어야 했다면  push로 이벤트 변경 
- v1에서는 develop 브랜치가 별도로 존재했지만 지금은 featuer 브랜치에서 main으로 바로 올리기 때문에 혹시 모를 동작을 대비해 push 이벤트로 수정이 되었다. (pr에서 merge시  혹시 모를 버그 예방... )

name: Deploy to AWS

on:
  push:
    branches:
    - main

 

2. 노드 모듈 버전 설정 

GitHub Actions에서는 기본적으로 CI 서버에 Node.js가 이미 설치가 되어 있어서 굳이 별도로 설치를 해 줄 필요는 없지만
GitHub Actions의 CI 서버는 주기적으로 이러한 소프트웨어가 업데이트되기 때문에 프로젝트에서 사용하는 정확한 버전의 Node.js를 설치하는 편이 더 안전하다고 한다.

   - uses: actions/setup-node@v3
        with:
          node-version: 16

https://www.daleseo.com/github-actions-setup-node/

 

3. 노드 모듈 캐싱처리

path :인자로는 우분투 운영체제에서 npm이 패키지 저장소에서 내려받은 패키지를 CI 서버에 저장해 두는 경로를 지정
 key :  인자로 GitHub의 캐시에서 데이터를 읽거나 쓸 때 사용되는 식별자를 명시하며  키값은 {소유자}/{저장소명}@{참조자}의 형태로 명시된다
npm ci를 적용한 이유 
 install , ci 두 가지 모두 결과적으로는 두 명령어 모두 의존성 목록을 설치하지만 
npm install의 경우  package.json을 읽어 의존성 목록을 만들고 package-lock.json을 통해 설치할 의존성의 버전을 알려주는 것이기 때문에 npm install 시 package.json 에 의존성 목록에 추가될 것이고 package-lock.json 도 업데이트 된다.
ci의 경우  쓰기 권한이 없으며 package-lock.json 파일을 기반으로 의존성을 설치하고, package.json 은 버전 매칭 밸리데이션 용도로 사용된다. 다만 package-lock.json 이 무조건 존재해야만 하고, 만약 없으면 에러를 낸다.
만약 하나의 프로젝트에 여러 명의 개발자가 협업을 할 경우 각자의 로컬 환경에서 npm, node 버전 등이 서로 다를 수 있다. 이러한 상황에서 각자 npm install 실행한다면, 서로 다른 버전을 가지는 모듈을 가지는 경우가 생길 수 있으며 로컬 환경뿐만 아니라 CI/CD 등 서로 다른 환경에 의해 발생할 수 있다고 한다. 
기존 프로젝트에 package-lock.json 파일 이 존재했기 때문에 ci로 처리하여 기존 node_modules 삭제 후 package-lock.json을 읽고 의존성 목록을 설치하는 방법을 선택했다.

 - name: Cache node modules
        id: npm-cache
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      - if: steps.npm-cache.outputs.cache-hit == 'true'
        run: echo 'npm cache hit!'
      - if: steps.npm-cache.outputs.cache-hit != 'true'
        run: echo 'npm cache missed!'
      - run: npm ci

https://www.daleseo.com/github-actions-cache/
https://github.com/actions/setup-node
https://mygumi.tistory.com/409
 

4. env 선언 

프로젝트 내 이미지 파일을 s3에 별도로 프런트가 관리되고 있었기 때문에  api server url과 함께 명시해 주었다.  

- name: Generate Environment Variables File for Production
        run: |
          echo "REACT_APP_SERVER_URL=$REACT_APP_SERVER_URL" >> .env.production
          echo "REACT_APP_BUCKET_NAME=$REACT_APP_BUCKET_NAME" >> .env.production
          echo "REACT_APP_REGION=$REACT_APP_REGION" >> .env.production
          echo "REACT_APP_ACCESS=$REACT_APP_ACCESS" >> .env.production
          echo "REACT_APP_SECRET=$REACT_APP_SECRET" >> .env.production
          echo "REACT_APP_BUCKET_URL=$REACT_APP_BUCKET_URL" >> .env.production
        env:
          REACT_APP_SERVER_URL: ${{ secrets.REACT_APP_SERVER_URL }}
          REACT_APP_BUCKET_NAME: ${{ secrets.REACT_APP_BUCKET_NAME }}
          REACT_APP_REGION: ${{ secrets.REACT_APP_REGION }}
          REACT_APP_ACCESS: ${{ secrets.REACT_APP_ACCESS }}
          REACT_APP_SECRET: ${{ secrets.REACT_APP_SECRET }}
          REACT_APP_BUCKET_URL: ${{ secrets.REACT_APP_BUCKET_URL }}


5. s3 업로드

기존에는 Configure AWS credentials  선언 후  s3 버킷을 업데이트해 줬다면  이번엔 Configure AWS credentials  설정을 S3 업로드 시 함께 선언하는 방식으로 변경했다.
두 가지의 차이점은 이후  무효화에서 나타났다.

// 기존 s3 연결 
 - name: Configure AWS credentials 
        uses: aws-actions/configure-aws-credentials@v1 
        with: 
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 
          aws-region: ap-northeast-2


      - name: Upload to S3
        env:
          BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME}}
        run: |
          aws s3 sync \
            ./build s3://$BUCKET_NAME
           
// 변경된 s3연결
- name: deploy to s3
        uses: jakejarvis/s3-sync-action@master
        with:
          args: --delete
        env:
          AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME}}
          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: 'build'


6. CloudFront 무효화 

      - name: CloudFront Invalidation
        env:
          CLOUD_FRONT_ID: ${{ secrets.DISTRIBUTION_ID}}
        run: aws cloudfront create-invalidation --distribution-id $CLOUD_FRONT_ID --paths "/*"

만약 내가 Configure AWS credentials 선언을 deploy to s3 함께 적용 후  아래와 같이 선언한다면 에러를 보게 될 것이다... 
왜냐면.. 그게 지금 나였으니.... ㅜㅜㅜㅜ 🥹

Git Action CloudFront Invalidate 무효화 CD 이슈해결

지난번 CD 연결이 성공한 줄 알고 방치시켜놨던 action에서 알고 보니 CloudFront 무효화 처리가 안되고 있었던 것이었다.. 기존 코드 - name: Deploy env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACC

suinchoi.tistory.com

 
저번에 포스팅한 에러와 동일한 에러가 발생하게 되었는데 팀원에게 문의한 결과 이번엔 IAM 정책의 문제가 아니었다.. 
4시간의 검색 끝에 별도로 무효화 처리를 위해 추가 aws-access 처리를 한번 더 해주어야 한다는 내용을 알게 되었고 적용해 본 결과 정상적으로 동작하는 것을 알 수 있었다.

      - name: Invalidate CloudFront
        uses: chetan/invalidate-cloudfront-action@v2
        env:
          DISTRIBUTION: ${{ secrets.DISTRIBUTION_ID }}
          PATHS: "/index.html"
          AWS_REGION: "ap-northeast-2"
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

https://github.com/chetan/invalidate-cloudfront-action
 

 
하나씩 확인하며 구현해 보니  동작원리를 알아가는 것이 재미있었고 마지막 성공의 희열은 정말,,, 말로 설명할 수 없을 만큼 행복하다. 
 
 
오늘도 무한 4시간의 삽질을 통해 자동배포 완성!

마지막으로 

출발....🚀