프론트엔드 CI/CD 파이프라인 구축

이태곤·2024년 2월 27일
0

CS

목록 보기
23/23

Github Action + S3 + CloudFront

  • Github Action: 빌드, 테스트 및 파이프라인을 자동화 할 수 있는 CI/CD 플랫폼으로, repository에서 이벤트가 발생할 때 workflow를 실행

  • S3: 정적 웹 호스팅 제공

  • CloudFront (CDN 서비스): S3 Bucket 오리진 서버로부터 콘텐츠를 효율적으로 캐싱하고, 사용자에게 가장 가까운 엣지 로케이션에서 콘텐츠를 제공

  1. GitHub Action 실행: GitHub 리포지토리에서 푸시가 발생하면, 설정된 GitHub Action이 실행

  2. 빌드 및 테스트: GitHub Action은 정적 리소스를 빌드하고 실행 (정적 파일을 생성)

  3. S3 업로드: 빌드된 정적 파일은 Amazon S3 버킷으로 업로드

  4. CloudFront 캐싱 갱신: 업로드된 새로운 정적 파일은 CloudFront 캐시의 오리진으로 설정된 S3 버킷에서 가져와 엣지 로케이션에 배포


1. S3 Bucket

  • S3 버킷 생성: 정적 파일을 저장하기 위해 새로운 S3 버킷을 생성

  • 퍼블릭 엑세스 차단 설정: 생성한 S3 버킷의 설정에서 '모든 퍼블릭 엑세스 차단' 옵션 선택

    • 사용자는 S3 버킷에 직접 액세스하지 않고 CloudFront를 통해 캐싱된 컨텐츠에 접근
  • 객체 소유권 설정에서 'ACL(액세스 제어 목록) 비활성화'를 선택

  • CloudFront


2. 정적 웹 사이트 호스팅

  • 생성한 S3 버킷의 속성에서 "정적 웹 사이트 호스팅"을 활성화하고, 인덱스 문서와 오류 문서를 지정

3. 정책 생성

  • Action: CloudFront가 S3 버킷의 객체를 읽을 수 있도록 s3:GetObject 작업이 허용

  • Resource: arn:aws:s3:::reservation.genesis-airport.com/*으로 지정하여 S3 버킷의 모든 객체에 대한 액세스를 허용합니다.

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::reservation.genesis-airport.com/*"
        }
    ]
}

4. Github Action CI

  • frontend-user-deploy.yml
name: Deploy Frontend-user to S3

on:
  push:
    branches: [ "main", "develop" ]
    paths:
      - 'client/**' 

env:
  AWS_REGION: ap-northeast-2
  S3_BUCKET_NAME: reservation.genesis-airport.com

permissions:
  contents: read

jobs:
  deploy-frontend:
    name: Deploy Frontend
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install
        working-directory: ./client

      - name: .env setting
        run: |
          echo "REACT_APP_REDIRECT_URI_CLIENT=${{ secrets.REACT_APP_REDIRECT_URI_CLIENT }}" >> .env
          echo "REACT_APP_CLIENT_ID=${{ secrets.REACT_APP_CLIENT_ID }}" >> .env
          echo "REACT_APP_CLIENT_SECRET=${{ secrets.REACT_APP_CLIENT_SECRET }}" >> .env
          echo "REACT_APP_SERVER_URL=${{ secrets.REACT_APP_SERVER_URL }}" >> .env
        working-directory: ./client

      - name: Build React app
        run: npm run build
        working-directory: ./client

      - 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: ${{ env.AWS_REGION }}

      - name: Upload to AWS S3
        run: aws s3 sync ./client/build/ s3://$S3_BUCKET_NAME/ --delete     
  • frontend-admin-deploy.yml
name: Deploy Frontend-admin to S3

on:
  push:
    branches: [ "main", "develop" ] # main 또는 develop 브랜치에 푸시될 때만 이벤트가 발생
    paths:
      - 'admin/**' # admin 폴더 내용이 변경될 때만 이벤트가 발생

env:
  AWS_REGION: ap-northeast-2
  S3_BUCKET_NAME: admin.genesis-airport.com

permissions:
  contents: read # Git 리포지토리의 컨텐츠를 읽을 권한 부여

jobs:
  deploy-frontend:
    name: Deploy Frontend
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4 # 리포지토리 체크아웃을 수행

      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '20' # Node.js 버전 20을 설치

      - name: Install dependencies
        run: npm install
        working-directory: ./admin

      - name: .env setting
        run: |
          echo "REACT_APP_REDIRECT_URI_ADMIN=${{ secrets.REACT_APP_REDIRECT_URI_ADMIN }}" >> .env
          echo "REACT_APP_CLIENT_ID=${{ secrets.REACT_APP_CLIENT_ID }}" >> .env
          echo "REACT_APP_CLIENT_SECRET=${{ secrets.REACT_APP_CLIENT_SECRET }}" >> .env
          echo "REACT_APP_SERVER_URL=${{ secrets.REACT_APP_SERVER_URL }}" >> .env
        working-directory: ./admin 

      - name: Build React app
        run: npm run build
        working-directory: ./admin

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS 액세스 키 ID를 설정
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS 시크릿 액세스 키를 설정
          aws-region: ${{ env.AWS_REGION }} # AWS 지역을 설정

      - name: Upload to AWS S3
        run: aws s3 sync ./admin/build/ s3://$S3_BUCKET_NAME/ --delete # admin 폴더의 빌드된 파일을 S3 버킷으로 업로드

4. CloudFront

  1. Origin Domain Name (원본 도메인): 클라우드프론트 배포에서 원본으로 사용될 서버의 도메인 주소

  2. Origin Path (원본 경로): 원본 도메인 뒤에 붙는 경로로, 원본 서버에서 클라우드프론트로 전송할 파일이나 데이터의 위치를 지정

  3. Origin Access ID (원본 액세스 ID, OAI): 클라우드프론트가 S3 버킷 또는 사용자 정의 원본에 접근할 때 사용하는 IAM 역할의 ID

  4. Alternate Domain Name (CNAME, 대체 도메인): 클라우드프론트에 연결할 대체 도메인 이름(CNAME)

    • 우리의 경우 가비아를 통해 도메인을 구매해서 클라우드프론트에 해당 도메인의 CNAME 레코드를 매핑해야 한다.
  5. SSL Certificate (사용자 정의 SSL 인증서): 클라우드프론트에 연결할 사용자 정의 SSL/TLS 인증서의 정보


🚀 Trouble Shooting

  1. OAI 설정: OAI 설정을 통해 S3 버킷에 직접 액세스하는 것이 아니라 CloudFront를 통해서만 콘텐츠에 액세스할 수 있도록 할 수 있다.
    그러나, CloudFront에 대한 원본 액세스 제어(OAI)가 올바르게 구성되지 않아서 CloudFront는 S3 버킷의 콘텐츠에 접근할 수 없었다.

    • 권한 설정: CloudFront 배포에 OAI를 올바르게 연결하고, S3 버킷의 권한이 OAI를 통한 접근을 허용
  2. CloudFront Caching: 클라우드프론트 캐싱이 예상한 대로 작동하지 않았다.

    • 캐시 무효화: CloudFront 캐시 무효화는 배포된 콘텐츠의 모든 사본을 CloudFront 에지 서버에서 삭제하여, 다음 요청이 올 때 원본에서 최신 버전의 콘텐츠를 가져오도록 설정했다.
    • 캐시 정책 검토: CloudFront 캐시 정책을 생성하여, TTL을 설정할 수 있다.

2개의 댓글

comment-user-thumbnail
2024년 2월 27일

본인의 서비스가 전세계를 대상으로 하는것이 아니라면 CloudFront 보다는 region replica 로 보다 낮은 지연시간을 보장받을수 있답니다. 근데 왜 자꾸 제가 공부하는거 따라하는거죠?

1개의 답글