이 글은 AWS의 S3, CloudFront를 사용해 리액트 프로젝트를 배포한다. 이 과정을 Github Actions를 활용해 자동 배포를 소개하고자 한다.
잘못된 내용이 있다면 편하게 말씀해주세요 🙏🏻
들어가기에 앞서
배포 방법은 다음과 같다. React 프로젝트의 빌드 결과는 정적 리소스 파일(.html, .js, .css ...)이다. 이를 S3에 올린다. 정적 리소스 파일이기에 S3의 baseURL에 index.html를 연결시켜주면 클라이언트는 baseURL에 들어왔을 때 index.html를 받게 된다. 이때 보통 CSR(클라이언트 사이드 렌더링)로 작동하기에 클라이언트의 브라우저는 S3에 필요한 리소스(. js,. css...)를 요청한다. 마치 nginx가 정적 리소스 전달하는 것처럼 보인다. (직접 전송하지만) S3에 CloudFront의 기능을 추가하여 보다 멋지게 만들 수 있다. S3에 커스텀 도메인과 https를 지원한다. 요청마다 별도의 상태 코드 지정할 수 있고, 전 세계 AWS CDN에 정적 리소스를 보내 클라이언트들은 가까운 CDN으로 부터 더 빠르게 페이지를 받을 수 있다.
배포 방법은 다음과 같다. (예제 소스코드, 프로젝트에 적용한 소스코드)
0. Github Actions 기초 설명
1. S3의 버킷 생성과 정적 웹사이트 호스팅 설정
2. IAM을 활용해 Github Actions이 CLI로 AWS 접속할 사용자 생성
3. react 프로젝트가 업데이트될 때마다 빌드하고 빌드한(/build) 정적 리소스를 Github Actions를 활용해 자동으로 S3 버킷에 올린다.
4. CloudFront 생성과 설정, 연결 확인
5. CloudFront Invalidation 갱신과 Github Actions 정리
6. 아키텍처
7. 주의
0. Github Actions 기초 설명
예제 : github actions 단순 작동. yaml
name: Test Front build workflow # workflow 이름
on: # workflow 실행 조건
push:
branches:
- main # 메인 브랜치가 푸쉬된 경우
jobs: # job 설정
test: # job id
name: Hello test job! # job 이름
runs-on: ubuntu-20.04 # job 가상환경
steps:
- name: check ls
run: ls -al # checkout 하기전 확인
- name: checkout Github Action # step 이름
uses: actions/checkout@v3 # github actions 가상 환경에 해당 레포 소스 가져오기
- name: after actions/checkout ls
run: ls -al # checkout 후 확인
- name: job - test echo
run: echo "Hello test job!"
# 생략
github actions는 저장소의. github/workflows에. yaml 파일을 인식하여 작동한다. 위 파일은. github/workflows/front-build.yaml이다. 위 github actions는 github이 CI/CD 하는 서버를 제공해준다고 생각하면 좋다. 해당. yml 파일을 github가 제공해주는 서버에서 작동한다.
job id가 test 인 경우를 알아보자.
test job은 ubuntu-20.04의 서버에서 실행된다.
서버에서 실행할 명령어들은 steps의 -name으로 구분되어 실행된다.
steps의 name: check ls step 명령은 ls -al 명령어이다. github가 제공해준 CI/CD 서버의 현재 파일을 확인한다.
그다음 steps는 checkout gGithub Action으로 actions/checkout@v3이라는 이미 만들어진 플러그인(?, 스크립트라고 생각해도 좋다)을 사용한다. 이 기능은 서버에 CI/CD를 사용하고 있는 저장소를 CI/CD 서버에 클론 한다.
그다음 after actions/checkout ls 명령의 결과는 아래와 같다.
이처럼 Github Actions를 활용해 제공되는 CI/CD 서버에서 원하는 명령, 스크립트, 다른 사람들이 올려놓은 기능들을 실행하며 다양하게 활용할 수 있다.(테스트, 빌드, 배포 등)
1. S3의 버킷 생성과 정적 웹사이트 호스팅 설정한다.
S3 버킷 생성
버킷 정책 편집
버킷에 대한 접속을 누구나 할 수 있도록 정책을 변경해줘야 한다.
# 기존
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Principal": {},
"Effect": "Allow",
"Action": [],
"Resource": []
}
]
}
# 변경
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Principal": "*", # 특정 사용자에 대해 권한을 제어하고 싶다면 입력한다. (전체 : * / 특정 사용자 : arn:aws:iam:AWS-account-ID:user/IAMID) "Effect": "Allow",
"Action": "s3:GetObject", # Actions : 버킷에 대해 어떤 작업을 허용(또는 거부)할 것인지 선택하는 옵션
"Resource": "arn:aws:s3:::team-35-issues-tracker/*" # 접근 권한을 주고자하는 버킷을 입력한다. (입력 포맷 : arn:aws:s3:::버킷명/*)
}
]
}
2. IAM을 활용해 Github Actions이 CLI로 AWS 접속할 사용자 생성한다.
IAM을 활용해 S3 권한을 가진 사용자 생성하기(CI/CD에서 CLI로 접속할 유저 id라고 생각하면 좋다.)
Github repo에 시크릿키 등록
사용자의 액세스 키, 시크릿 키를 등록하자.
3. react 빌드한 /build 정적 리소스를 Github Actions를 활용해 자동으로 S3 버킷에 올린다.
build와 s3에 업로드하는 Github Actions
react 파일을 빌드한다. 빌드된 정적 리소스 파일을 위에서 만든 s3에 올리면 된다.
기본적으로 CI/CD 서버에는 aws CLI가 설치되어있어 aws 명령어를 사용할 수 있다.
1. aws-actions/configure-aws-credentials@v1을 사용해 aws에 접속할 사용자를 등록한다.
2. aws s3 명령어를 통해 react의 빌드 결과 build 폴더를 버킷에 올린다
# GitHub Actions CLI 환경에서 S3 사용자 권한의 aws 접속
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_S3_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY_ID }}
aws-region: ap-northeast-2
# react 빌드한 /build를 s3로 업로드
- name: Upload /build to S3
env:
BUCKET_NAME: ${{ secrets.AWS_S3_BUCKET_NAME}}
run: |
aws s3 cp --recursive --region ap-northeast-2 build s3://$BUCKET_NAME
build 폴더를 s3://$BUCKET_NAME에 업로드할 수 있다. (--recursive 옵션 : 하위 폴더 파일까지 모두)
버킷의 정적 웹사이트 호스팅 활성화하여 index.html 보내기
하단에 활성화되고, S3으로 정적 웹 사이트 호스팅 된 URL이 나온다.
4. CloudFront 생성과 설정(사용자 권한 업데이트, Invalidation 갱신)
CloudFront 생성
+a) OAI란?
CloudFront에서 S3 사용할 때 보통 ON을 한다. S3 버킷에 직접 접근하지 못하고 오직 CloudFront를 통해서만 파일 접근하도록 할 수 있다. 덧(22.06.15) : OAI는 웹 호스팅 때는 적용이 안된다고 한다.
CloudFront 완료!
+a) CloudFront 추가 설정
위와 같이 설정했을 때 URL에 접속 안 되는 경우가 있다.
CloudFront가 배포가 아직 안되었는데 들어갔거나 에러 발생 시 응답 페이지 경로가 없는 경우일 수 있다.
5. CloudFront Invalidation 갱신과 Github Actions 정리
캐시 Invalidation(무효화) 갱신하기
CloudFront로 배포된 S3 정적 리소스들은 AWS 엣지 로케이션에 전송되어 클라이언트의 요청에 빠르게 응답할 수 있다. 엣지 로케이션의 캐시는 24시간으로 설정되어있다. 24시간이 지나면 오리진 서버(S3)의 정적 리소스를 다시 받아 캐시로 사용한다.
문제
react 프로젝트가 업데이트되어 새롭게 배포되었다. 오리진 S3에 올라간 정적 리소스는 최신이다. 하지만 전 세계 엣지 로케이션의 정적 리소스는 최신 리소스가 아닌 이전에 배포된 리소스이다.
해결
방법 1: 이를 위해 엣지로케이션의 캐시를 무효화 갱신 API를 사용하여 초기화
방법 2(참고) : CloudFront는 엣지로케이션 파일의 캐시가 없을 때 오리진에서 파일 새로 가져오는 방법을 사용해 파일 내용 바뀔 때 디렉토리 경로나 파일명 바꾸거나, Cache-Control의 캐시 유지 시간을 짧게 설정하여 운영할 수 있다. 단, 모든 파일 캐시 유지 시간을 짧게 하면 오리진에서 접속하여 새 파일 가져오는 주기가 짧아 전체 성능이 떨어질 수 있다
방법 1을 사용하자.
CloudFront의 캐시 Invalidation API는 아래 명령어를 사용하면 된다.
aws cloudfront create-invalidation --profile=[사용자 아이디] --distribution-id [CloudFront ID] --paths "/*"
+a 덧(22.06.24) : 전체 경로를 지정(와일드 카드 사용)에는 반드시 /* 이 아닌 따옴표 ("/*") 를 사용해야 한다. (참고)
profile(AWS User(사용자) ID) : aws 실행할 환경에 profile 설정되어있다면 입력, (Github Actions에서 AWS 접속 명령 있다면 생략 가느으)
CloudFront ID : CloudFront → 배포 → ID 확인 가능
기존 S3에 접속하는 사용자에 CloudFront 권한 추가
Invalidation API와 Github Actions에 추가
aws cloudfront create-invalidation --distribution-id $CLOUD_FRONT_ID --paths "/*"
명령을 사용해 캐시를 무효화할 수 있다. 이때 즉각 엣지로케이션이 반응하는 것은 아니며 짧게는 10초, 프로젝트 단위에 따라 10분 넘게 걸릴 수 있다고 한다.
Github Actions 정리
기능만 하는 GIthub Actions .yaml 코드는 이곳 을 참고하면 된다.
github repo secrets에서 아래와 같이 지정하면 된다.
AWS_CLOUDFRONT_ID = cloudfront id
AWS_S3_ACCESS_KEY_ID = S3 권한이 있는 유저 엑세스 키
AWS_S3_SECRET_ACCESS_KEY_ID = S3 권한이 있는 유저 엑세스 키
AWS_S3_BUCKET_NAME = 정적 리소스 배포할 S3 버킷 이름
+덧) (2022.06.16) 위 코드를 토대로 만든 개선 버전(node 버전 명시, npm install의 캐쉬 처리, .yaml의 성공 실패 여부 슬랙 메세지 알람 등)
6. 아키텍처
7. 주의
위 배포 방법은 과금될 수 있다. 프리티어 횟수를 꼭 참고하자.
S3 : 매달 5GB의 Amazon S3 스토리지(S3 Standard 스토리지 클래스), 20,000건의 GET 요청, 2,000건의 PUT, COPY, POST 또는 LIST 요청, 100GB의 데이터 송신 혜택
용량 : Amazon CloudFront를 사용하면 매월 1TB의 데이터 전송, 10,000,000개의 HTTP 및 HTTPS 요청, 2,000,000개의 CloudFront 함수 호출
Invalidation(무료화) 요청 : 매달 추가 비용 없이 초기 1,000개의 경로에 대한 무효화 요청. 이후로 무효화 요청 경로당 0.005 USD가 청구
⛓ Reference
리액트 앱 AWS S3, CloudFront에 배포하기
'👾 Server > ☁️AWS' 카테고리의 다른 글
VPC Peering(피어링) (1) | 2022.08.01 |
---|---|
VPC Endpoint(엔드포인트) (3) | 2022.07.11 |
AWS - VPC(Virtual Private Cloud)이란? : AWS에서 독립된 가상 네트워크 만들기 (1) | 2022.03.31 |
AWS - IAM(Identity and Access Management)이란? (0) | 2022.03.30 |
AWS Service 소개 (0) | 2022.03.29 |
댓글