AWS React code 배포 부터 CI/CD

이규황·2023년 5월 13일
post-thumbnail

전체적인 진행 순서

배경 & 목적

회사에서 기존에 개발된 웹페이지를 대규모로 업데이트 하기로했었다. 기존에 플라스크 프레임워크 Jinja를 기반으로 back-end 와 front-end 가 함께 개발되어 있었고, 백과 프론트를 분리개발을 진행하기로 했고, 프론트는 react.js로 개발을 진행하기로 했다.
프론트 코드를 배포하기 위해서 AWS를 통해 배포하는 방법을 how to적으로 작성해보려고한다.
또한 코드 원격저장소로 github를 사용하기에 github에 코드를 배포함과 동시에 자동으로 설명된 aws 설정에 의해 배포고 웹페이지가 업데이트 될 수 있게 한다.

AWS 세팅

각 설정에 항목마다의 설명은 생략합니다.

1). S3 버킷 생성

  • *S3란? Amazon Simple Storage Service(Amazon S3)는 업계 최고의 확장성, 데이터 가용성, 보안 및 성능을 제공하는 객체 스토리지 서비스입니다.

  • S3 버킷을 통해 원하는 양의 데이터를 저장합니다.

  • React 코드를 저장해서 보여주기 위해서 AWS S3 서비스를 이용해 S3 버킷에 Build합니다.

1 S3에 접속하고 버킷 만들기를 클릭한다.

2. 일반 구성에 버킷 이름, AWS 리전을 서울로 선택한다.

3. 객체 소유권을 ACL 활성화한다. 객체 소유권은 객체 라이터를 클릭한다.

4. 버킷의 퍼블릭 액세스 차단 설정과 버전 관리를 다음과 같이 설정한다.

5. 기타 설정을 다음과 같이 한다.

6. 만들어진 버킷을 클릭하고 속성에 들어가 맨 아래 정적 웹 사이트 호스팅을 편집한다.

7. 다음과 같이 입력하고 변경 사항 저장을 클릭한다.

8. 권한에 들어가 버킷 정책을 입력한다.

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity 클라우드 프론트 설정 후 입력"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::s3 name/*"
        }
    ]
}

9. 버킷 생성을 완료

총 9단계를 거치면 S3 버킷을 만들수 있습니다.
설정을 하는 방법은 구글링을 통해서 충분히 가능하고, 아마 권한에 따른 버킷 정책을 설정하는 부분이 생각보다 까다롭스니다. 클라우드프론트로 서빙해서 배포를 진행할 예정이라 클라우드 프론트에서 s3접근할 수 있는 권한을 설정해줘야 합니다.

2). CloudFront

  • *Amazon CloudFront는 .html, .css, .js 및 이미지 파일과 같은 정적 및 동적 웹 콘텐츠를 사용자에게 더 빨리 배포하도록 지원하는 웹 서비스입니다. CloudFront는 엣지 로케이션이라고 하는 데이터 센터의 전 세계 네트워크를 통해 콘텐츠를 제공합니다. CloudFront를 통해 서비스하는 콘텐츠를 사용자가 요청하면 지연 시간이 가장 낮은 엣지 로케이션으로 요청이 라우팅되므로 가능한 최고의 성능으로 콘텐츠가 제공됩니다.

*역할 : 사용자의 위치에 따라 최단 경로로 웹페이지에 접속할 수 있게 도와줍니다.

1. AWS Cloud Front에 접속합니다.

2. 오른쪽 상단에 배포 생성 버튼을 통해 배포생성을 해줍니다.

3. 원본 도메인에서 S3 버킷에서 생성한 도메인 값을 입력해 줍니다.

4. OAI(Origin Access Identity)를 설정해서 모든 버킷 접근은 CloudFront를 통해서 접근할 수 있게 합니다.

-*OAI 설정하는 것이 지금은 다른 방법으로도 할 수 있는 것으로 압니다. 참고

  • OAI(Origin Access Identity)를 생성해서 S3 버킷에 접근할 수 있게 해줍니다. [해당 Identity Access값은 S3 버킷 정책에 추가해줘야 합니다.]

5. 기본 캐시 동작을 설정해 줍니다.

  • Http 와 Https 프로토콜을 모두 허용합니다.

  • 캐시 키 및 원본 요청은 기본 AWS 추천 정책을 따릅니다.

  • 캐시 정책을 사용해서 TTL(Time To Live)를 설정해줄 수 있습니다.

  • 이 부분은 얼마나 오랫동안 콘텐츠들이 캐시에 저장되는지를 나타내는 부분입니다.

6. 설정 편집

*Route 53을 통해 설정한 도메인 주소값을 입력합니다.(대체도메인 CNAME)

*HTTP를 HTTPS로 전환시켜주기 위한 SSL인증서를 설정합니다.

  • 인증서요청하기 버튼을 통해서 AWS에서 인증서를 HTTPS인증서를 발급받을 수 있습니다.
    HTTPS 인증서를 발급할때 지역 설정에 유의해야하고, 발급받기까지 24시간 정도 걸리는 것으로 기억합니다.

7. Error Pages 설정

  • *SPA(Single Page Application)을 기반으로 작성되어있기 때문에 빌드된 파일 주소를 제외한 주소에서 기본 페이지가 보일 수 있도록 해야 합니다. (React)
  1. HTTP error code - 403 선택
  • S3에서는 파일이 없는 경우 403을 전달해 주기 때문입니다.
  1. Customize error response - Yes 선택

  2. Response page path - /index.html 선택 (이전 Default root object 와 같은 파일 선택)

  3. HTTP Response code - 200: OK 선택

8. 배포 완료

  1. 상태탭에 활성화 상태를 확인합니다.

3). Route 53을 통해 경로 설정

  • AWS에서 제공하는 DNS(Domain Name Service)

-*DNS란? example.com을 IP 주소로 변환하는 역할

  • 레코드

도메인 또는 하위 도메인의 트래픽을 라우팅할 방법을 정의하는 데 사용되는 호스팅 영역 내 객체를 의미합니다.

원하는 DNS주소값을 이용하기 위한 설정을 해줍니다. 저 같은 경우는 회사 주소값을 이용해서 설정합니다.

1.Route 53 대시보드 접속

  • *호스팅 영역을 클릭한다.

2.원하는 호스팅 영역을 클릭

3. 레코드 생성 클릭

4. 다음과 같이 값을 채우고 레코드 생성을 클릭한다.

CI/CD 설정

  • *CI/CD란?

github Actions을 활용하는 대표적인 예시 중 하나이다.

로컬 레포에서 원격 레포로 푸쉬하고 난 후, Actions에서는 이벤트 발생에 따라 자동으로
빌드 및 배포하는 스크립트를 실행시켜준다.

1) .github yml 파일 생성

1. .github/workflows 경로안에 .yml 파일 생성

*스크립트 설정하는 자세한 방법과 실행원리는 구글링을 통해서 자세하게 알 수 있다. 내가 설정했던 것을 기반의로 스크립트를 노출 가능한 부분만 공유한다.

*아래 스크립트에 주석을 통해 설명을 추가했고, E2E 테스트와 jest test를 위한 설정도 포함

name: 리액트 빌드 / 배포 Actions # workflow 이름

on: # workflow 실행 조건
  push:
    branches: [main, dev] # 메인, 데브 브랜치에 푸쉬된 경우
  pull_request:
    branches: [main, dev] # 메인, 데브 브랜치에 pr 생성한 경우

jobs: # job 설정
  test-dev-pr_push:
    runs-on: ubuntu-20.04
    if: github.base_ref == 'dev' || github.ref == 'refs/heads/dev'

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

      # Cypress tests
      - name: Cypress run
        uses: cypress-io/github-action@v4
        with:
          start: yarn start-dev
          wait-on: 'http://localhost:3000' #yarn start 완료까지 기다림
          browser: chrome # test browser 환경
          config: baseUrl=http://localhost:3000
          env: CYPRESS_URL=http://localhost:3000

  test-prod-push:
    runs-on: ubuntu-20.04
    if: github.ref == 'refs/heads/main'

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

      # Cypress tests
      - name: Cypress run
        uses: cypress-io/github-action@v4
        with:
          start: yarn start-prod
          wait-on: 'http://localhost:3000' #yarn start 완료까지 기다림
          browser: chrome # test browser 환경
          config: baseUrl=http://localhost:3000
          env: CYPRESS_URL=http://localhost:3000

  deploy-dev: # job id
    name: 리액트 Develop build & deploy
    runs-on: ubuntu-20.04
    if: github.ref == 'refs/heads/dev'
    needs: test-dev-pr_push
    environment: Develop

    env:
      # Treating warnings as errors because process.env.CI = true. 에러 나올경우 고려
      CI: false

    steps: #react build
      - uses: actions/checkout@v3

      - name: Get yarn cache
        id: yarn-cache-dir
        run: echo "::set-output name=dir::$(yarn cache dir)"
      - uses: actions/cache@v3
        id: yarn-cache
        with:
          path: ${{ steps.yarn-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      # yarn 패키지 설치
      - name: yarn
        run: yarn

      - name: React Dev build
        run: yarn build-dev

      # 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

      # react 빌드한 /build를 s3로 업로드
      - name: Upload to S3
        env:
          BUCKET_NAME_DEV: ${{ secrets.AWS_S3_BUCKET_NAME_DEV}}
        run: |
          aws s3 sync \
          ./build s3://$BUCKET_NAME_DEV

      # 업로드한 s3 파일을 각 CDN 캐시 무효화하여 리프레시 하기
      - name: CloudFront Invalidation
        env:
          CLOUD_FRONT_ID_DEV: ${{ secrets.AWS_CLOUDFRONT_ID_DEV}}
        run: |
          aws cloudfront create-invalidation --distribution-id $CLOUD_FRONT_ID_DEV --paths / /index.html /error.html /service-worker.js /manifest.json /favicon.ico

  deploy-prod: # job id
    name: 리액트 Production build & deploy
    runs-on: ubuntu-20.04
    if: github.ref == 'refs/heads/main'
    needs: test-prod-push
    environment: Production

    env:
      # Treating warnings as errors because process.env.CI = true. 에러 나올경우 고려
      CI: false

    steps: #react build
      - uses: actions/checkout@v3

      - name: Get yarn cache
        id: yarn-cache-dir
        run: echo "::set-output name=dir::$(yarn cache dir)"
      - uses: actions/cache@v3
        id: yarn-cache
        with:
          path: ${{ steps.yarn-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      # yarn 패키지 설치
      - name: yarn
        run: yarn

      - name: React Prod build
        run: yarn build-prod

      # 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

      # react 빌드한 /build를 s3로 업로드
      - name: Upload to S3
        env:
          BUCKET_NAME_PROD: ${{ secrets.AWS_S3_BUCKET_NAME_PROD}}
        run: |
          aws s3 sync \
          ./build s3://$BUCKET_NAME_PROD

      # 업로드한 s3 파일을 각 CDN 캐시 무효화하여 리프레시 하기
      - name: CloudFront Invalidation
        env:
          CLOUD_FRONT_ID_PROD: ${{ secrets.AWS_CLOUDFRONT_ID_PROD}}
        run: |
          aws cloudfront create-invalidation --distribution-id $CLOUD_FRONT_ID_PROD --paths / /index.html /error.html /service-worker.js /manifest.json /favicon.ico

  test-dev:
    runs-on: ubuntu-20.04
    if: github.ref == 'refs/heads/dev'
    needs: deploy-dev

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

      # Cypress tests
      - name: Cypress run
        uses: cypress-io/github-action@v4
        with:
          browser: chrome # test browser 환경
          config: baseUrl=https://dev-axcloudv4.axchange.co
          env: CYPRESS_URL=https://dev-internal-api.axchange.co/

  test-prod:
    runs-on: ubuntu-20.04
    if: github.ref == 'refs/heads/main'
    needs: deploy-prod

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

      # Cypress tests
      - name: Cypress run
        uses: cypress-io/github-action@v4
        with:
          browser: chrome # test browser 환경
          config: baseUrl=https://ax-cloud.com
          env: CYPRESS_URL=https://internal-api.axchange.co/

2) Github repo에 시크릿키 등록

git secrets를 설정하기 위해서는 관리자 권한이 필요하다. 회사에서는 권한이 분리되어 있어서 아무나 접근할수 없으니 참고!
yml스크립트에를 실행할때 필요한 secrets key와 value를 설정하느 것 쉽게 ENV 역할을 한다고 생각하된다.

1. github settings에 접속하여 secrets에 Actions 접속

2. secrets key 생성

  1. AWS_ACCESS_KEY_ID

모든 레포에서 사용 가능하도록 설정되어 있음

  1. AWS_SECRET_ACCESS_KEY

    모든 레포에서 사용 가능하도록 설정되어 있음

  2. AWS_S3_BUCKET_NAME_DEV

dev 서버 S3 버킷으로 설정한 이름 확인

  1. AWS_S3_BUCKET_NAME_PROD

prod 서버 S3 버킷으로 설정한 이름 확인

  1. AWS_CLOUDFRONT_ID_DEV

dev 서버 에 접속하면 확인가능

  1. AWS_CLOUDFRONT_ID_PROD

prod 서버에 접속하면 확인가능

3. PR을 생성 merge 이후 github action이 잘 동작하는지 확인한다.

이렇게 모든 설정을 완료했다.

모든 설정이 그렇듯 처음에는 많이 낯설어서 시간도 오래걸리고 이해도 잘안가지만, 설정을 한번 경험해보면.... 무척 간단하다. 처음에 이것을 설정하는... 무척 고생을 많이했다. 따로 설명을 해주는 사람이 없어서 스스로 설정을해서 잘 작동하는지 알아야했기때문에이다. 하지만 정말 많은 공부가 되서 지금은 나에게 아주 특별한 경험이다.

profile
도전중

0개의 댓글