본문 바로가기

프로젝트/푸드피커

[푸드피커] Github Actions 을 통한 AWS S3+CloudFront 배포 자동화 구축

반응형

[이전 포스트] AWS S3 + CloudFront 를 사용한 정적사이트 배포

 

[푸드피커] AWS S3 + CloudFront 를 활용한 정적사이트 배포

시작에 앞서이번에 푸드피커 사이트를 S3와 Cloud Front 를 사용하여 배포를 해보았는데, 이에 대한 정리를 나름 간략하게 정리해보았습니다.  해당 포스트의 내용은 AWS 공식 문서와 유튜브의 배포

duklook.tistory.com

 
 


들어가기 전 

저번 시간에는 수동으로 S3 + CloudFront 배포를 진행했습니다. 이번에는 깃허브 액션을 통해 전 과정을 자동화하는 작업을 시도해 봅니다.

 

 

 

이 부분은 건너 뛰어도 문제 없습니다. 작업을 들어가기 전에 개념을 정리해보는 부분 입니다.

 

이번에는 이전 포스트에 배포한  리액트 프로젝트를 CI/CD 를 통해 자동화할 수 있도록 설정해볼 것입니다. 그리고 제목에도 나와 있듯이 자동화 도구로는 Github Actions 을 사용합니다.
 
일단 대략적인 과정은 이렇습니다. 우선 Github Actions 를 사용하여 종속성을 설치합니다(node_modules) 그 후 일부 테스트를 실행하고(프로세스 정상 동작 유무 확인), 테스트를 통과한다면 AWS S3 에 파일을 업로드 합니다. 예를 들어 빌드 과정 중에 빌드가 실패한다면, 다음 작업의 단계로 넘어가지 못하고 종료됩니다.
 
마지막으로, 들어가기 앞서 S3 단독으로 배포하는 경우와 S3 + Cloud Front 를 짬뽕해서 배포하는 경우 어떤 차이점과 이점 혹은 제한점이 있을지 표로 정리해보고, 이에 대해 글로 풀어서 설명하듯이 정리하고 넘어 가겠습니다.

배포 방식의 차이
배포 방식 S3 S3 + Cloud Front
지연 시간 사용자와의 거리로 인해 지연 시간이 발생할 수 있음 엣지 로케이션을 통해 지연 시간이 최소화됨
캐싱 기본적으로 캐싱이 없음 엣지 로케이션에서 콘텐츠를 캐싱
보안 기본 HTTPS 지원, 추가 보안 기능 제한적 SSL/TLS 인증서, WAF, 추가 보안 기능 제공
비용 저장 및 데이터 전송 비용만 발생 CloudFront 추가 비용 발생, 데이터 전송 비용 절감 가능
설정 및 관리 상대적으로 간단 상대적으로 복잡

 
S3 로만 배포하는 경우에는 구성이 단순하기 때문에 배포 방법이 단순해진다는 점과 저장 및 데이터 전송 부분만 비용을 감당하면 되므로 적은 트래픽을 가지는 경우에는 더 비용 효율적일 수 있습니다. 다만, 캐싱이 불가능하고, 지리적으로 먼 거리에 있는 유저가 이용하는 경우 S3 객체에 저장된 데이터를 전송하는 데 지연 시간이 발생할 수 있다는 점이 단점이고, HTTP 로만 가능하기에 네트워크 요청 사이에 데이터가 그대로 노출된다는 점이 큰 문제라고 보입니다.
 
반면에 Cloud Front 와 S3 를 같이 사용하는 경우 HTTPS 사용으로 네트워크 트래픽을 암호화하여 보안상 이점을 가질 수 있고, 엣지 로케이션이라고 부르는 각 지역에 배치된 데이터 센터를 통해 사용자에게 데이터를 지연시간을 최소화하여 제공할 수 있다는 장점이 보입니다. 현재 프로젝트에서는 설정하지 않았지만, WAF 라는 웹 애플리케이션 방화벽을 추가로 설정함으로써 디디도스와 같은 보안상 취약점을 최소화할 수 있고, 데이터 요청에 대한 전송비용도 S3 에 비해 적은 편이기 때문에 비용 효율적이기도 합니다. 
 
다만, Cloud Front 와 S3 를 연결하는 과정이 추가됨에 따라 다소 복잡해질 수 있다는 문제가 있습니다. 그럼에도 하나씩 단계를 나눠서보면 어렵지는 않기 때문에, 이러한 불편 사항보다도 이점이 많기에 굳이 S3 만 따로 사용하는 일은 거의 없을 것 같습니다. 


 
글이 길었습니다. 다만 중요한 포인트라고 생각했기에 한 번 장문으로 정리해보고 갑니다. 그럼 시작하겠습니다.

배포 자동화를 위한 기초 셋팅

우선 배포자동화 이전에 프로젝트 파일을 올리고, 깃허브 액션을 실행하기 위한 몇 가지 조치가 필요합니다. 따라서 이에 대한 부분 부터 정리하고 가겠습니다.
 

.github 작업 워크플로우 생성

깃허브 액션을 실행하기 위해서는 프로젝트의 루트 경로(src 폴더와 나란히 있는 깊이)에서  .github 폴더를 만들고 아래와 같은 구성이 되도록 해주어야 합니다. 
 

 
그 후 CI/CD 를 위한 워크 플로우를 아래와 같이 작성해줍니다.

name: Build & Deploy to AWS CloudFront
on: 
    push:
        branches: [ deploy ]
jobs:
    build-and-deploy:
        name: Build And Deploy
        runs-on: ubuntu-latest # 깃허브 액션을 실행할 운영체제(기본 우분투)
        env: # 환경변수 설정
            BUCKET: example # 버켓이름
            DIST: build # 빌드 파일 이름
            REGION: ap-northeast-2 # S3 배포 지역이름
            DIST_ID: E1PGJUPT8GO29O  # Cloud Front 배포 파일의 식별자 
            
            ## 그 외 프로젝트에서 사용되는 환경변수들
            VITE_FOOD_KEY: ${{secrets.VITE_FOOD_KEY}}
            VITE_KAKAO:  ${{secrets.VITE_KAKAO}}
            VITE_NAVER_CLIENT_ID:  ${{secrets.VITE_NAVER_CLIENT_ID}}
            VITE_NAVER_CLIENT_SECRET:  ${{secrets.VITE_NAVER_CLIENT_SECRET}}
            VITE_PUBLIC_KEY:  ${{secrets.VITE_PUBLIC_KEY}}
            

       
        steps: # build-and-deploy 워크 플로 작업 실행 시 순차적으로 실행할 작업의 순서

        # 1. 단계 | 깃허브 리포지토리 접근
        - name: Checkout # 작업 이름
          uses: actions/checkout@v4 # 워크 플로 작업을 위한 저장소 액세스를 위한 도구

        # 2. 단계 | AWS 자격증명 구성 및 AWS CLI 설치
        # 참고 | https://docs.github.com/ko/actions/learn-github-actions/contexts#secrets-context
        # https://github.com/aws-actions/configure-aws-credentials/tree/v4/
        - name: Configure AWS Credentials
          uses: aws-actions/configure-aws-credentials@v4
          with:
            aws-access-key-id: ${{secrets.AWS_ACCESS_KEY}} 
            aws-secret-access-key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
            aws-region: ${{env.REGION}}
        
        #.3. 단계 | 프로젝트 관련 종속성을 설치할 노드 설치
        - uses: actions/setup-node@v4
          with:
           node-version: '20'
        
        # 4. 단계 | 종속성 설치(node_modules)
        - name: Install Dependencies
          run: |
            node --version
            npm ci          # OR 타입스크립트 안 쓰면 npm ci --production  

        # 5. 단계 | 정적 파일 빌드(build 생성)
        - name: Build 
          run: npm run build

        # 6. 단계 | AWS CLI 를 사용하여 S3 에 배포 파일을 복사(즉, 업로드)
        - name : Copy files to the production website with the AWS CLI
          run: |
            aws s3 sync --delete ${{env.DIST}} s3://${{env.BUCKET}}

        # 7. 단계 | 캐시 무효화 : 즉, 이전 정적 파일 캐시 버리고, 새로 업로드된 파일이 보이도록
        - name : Clear Cash
          run: |
            aws cloudfront create-invalidation \
              --distribution-id ${{ env.DIST_ID}} \
              --paths "/*"

 

위 코드와 관련한 주요사항
빌드 부분에서 타입선언 문제가 계속 발생하여 그 대안으로 npm ci 만 입력하여 모든 종속성이 반영되도록 하였습니다. 이 부분에 있어서 좋은 아이디어가 있으신 분이 있다면 댓글로 꼭 피드백 주신다면 고맙겠습니다.

 

깃허브 액션에 대한 접근 권한 부여를 위한 정책생성

이번에는 깃허브 액션을 통해 S3 에 접근할 수 있도록 권한을 가진 사용자를 생성하도록 할 것입니다.
 

AWS IAM 에 접속 후 정책 생성 탭 들어가기

IAM 접속 후 '정책' 탭에서 우측 상단의 '정책생성' 을 클릭해 줍니다.

 
 
그 후 오른쪽의 탭에서 JSON 을 클릭해주고, 정책 편집기에서 몇 가지 정책을 추가할 준비를 합니다.

 

정책 생성

각각은 S3 객체에 대한 액션, 접근 후 실행 가능한 액션, CloudFront 의 캐시 무효화에 대한 권한을 지정한 것입니다. 여기서 주의할 점은 Resource 에 들어갈 value 은 S3 버킷의 이름이 arn:aws:s3:::버킷이름"이 들어가야 하고, AllObjectActions 부분에서의 Resoucre의 경우에는 /* 이 끝에 붙여 주어야 합니다. 

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "ListObjectsInBucket",
			"Effect": "Allow",
			"Action": [
				"s3:ListBucket"
			],
			"Resource": "arn:aws:s3:::foodpick.co.kr"
		},
		{
			"Sid": "AllObjectActions",
			"Effect": "Allow",
			"Action": [
				"s3:*"
			],
			"Resource": "arn:aws:s3:::foodpick.co.kr/*"
		},
		{
			"Sid": "Statement1",
			"Effect": "Allow",
			"Action": [
				"cloudfront:CreateInvalidation"
			],
			"Resource": "*"
		}
	]
}

 

부가 설명
"Resource": "arn:aws:s3:::foodpick.co.kr" 은 foodpick.co.kr 버킷에게 권한을 부여한다는 의미입니다.
"Resource": "arn:aws:s3:::foodpick.co.kr/*" 은 foodpick.co.kr 버킷 내(/*)에서의 지정한 액션에 대한 권한을 모두 부여한다는 의미입니다.

 
 
그 후 다음 화면으로 넘어가면 식별하기 쉬운 정책이름을 지정해주고, 필요에 따라 설명을 추가해준 후 정책을 생성해줍니다.

 
 

IAM 사용자 생성 및 정책 연결

사용자 생성

IAM 의 좌측 액세스 관리에서 '사용자' 를 찾아 들어가줍니다. 그 후 우측 상단에 '사용자 생성' 을 클릭해줍니다.

 
 사용자 이름을 작명하고, 다음을 클릭해 줍니다.

 
 

정책 연결

직접 정책 연결 을 선택 후 앞서 생성한 정책인 S3WebAccess 를 체크 하고, 다음으로 넘어가 줍니다.

 
생성된 내역을 확인하고, 사용자 생성을 클릭하여 나와 줍니다.

 
 
앞서 과정까지 끝났다면,  생성된 사용자 이름을 확인할 수 있을 겁니다.

 
그리고 해당 사용자 이름을 클릭해 줍니다. 그럼 보안 자격 증명 탭이 보일 텐데 이를 클릭 후 하단의 액세스 키 만들기를 눌러서 키를 만들어 줍니다. 

 
사용 사례는 기타를 클릭하고 바로 넘어가면, 아래 화면이 바로 보일 겁니다. 액세스 키와 비밀 액세스 키를 안전한 곳에 보관해두세요. 이를 깃허브 설정에서 비밀키로 추가하여 깃허브 액션의 환경변수를 통해 접근할 수 있도록 할 겁니다.

 


 
여기 까지 끝내셨다면, 다음에는 깃허브 액션의 워크 플로에서 사용할 몇 가지 비밀키를 등록해야 합니다.

깃허브 액션에서 사용할  몇 가지 비밀키 생성 | AWS S3 액세스 키를 .env 로

키 생성 화면 들어가기

깃허브의 해당 프로젝트 코드를 보관 중인 리포지토리 메인 화면으로 들어와 줍니다. 그리고 우측 상단 탭에 Settings 를 눌러줍시다. 그러면 최하단에 Secrets and variables 가 있는 것을 볼 수 있습니다. 이것을 클릭해 줍니다.
 

 
 
그럼 아래 목록이 드롭다운 되는데 그 중에서 Actions 를 눌러줍시다.

 
그 다음 화면에 표시되는 버튼 중 New repository secret 를 클릭해줍니다.

 

키 등록

키 생성 화면에 들어오셨다면, 앞서 AWS 자격증명을 생성하면서 발급받은 액세스 키와 비밀 액세스 키를 각각 환경변수에 등록해줍니다. 그 후 제대로 생성이 되었는지 확인해봅니다. 아래와 같이 생성된 키가 보인다면 성공입니다. 다음으로 넘어갑시다.

 
다시 상기하자면, 해당 키들은 secrets. 접근자를 통해 접근하여 사용할 수 있습니다. 아래와 같이 말이죠. 그런데, 현재 이미지와 같이 노란줄이 뜬다는 것은 오타가 발생하였거나 등록된 키가 아니라는 의미입니다. 이를 염두에 두고 진행합시다.

 
 
참고로, 위에서는 ACCESS_KEY 등을 저장하는 예시만 있지만, 실제 여러분이 사용되는 프로젝트 내 환경변수들을 모두 저장해두면 됩니다.  예를 들어, 저 같은 경우에는 아래와 같이 생성해두었습니다.
 

 
그리고 이들 키를 사용하려면 워크 플로 파일(.yaml) 에서 env 에 등록하여 사용할 수 있습니다.
 

 
 

깃허브 액션을 이용한 CI/CD 파이프 라인 테스트 및 배포

앞서 환경변수에 S3 버킷 접근에 필요한 접근키와 비밀 접근키를 등록하였다면, 이제 깃허브 액션을 통해 제대로 빌드 후 배포가 되는지 테스트해볼 차례입니다.
 

커밋 후 deploy 브랜치로 push

저의 경우에는 배포를 위한 브랜치로 deploy 브랜치를 따로 만들어 주었습니다.(git branch deploy)  그 후 해당 브랜치로 이동 후(git switch deploy) git add . -> git commit -m  -> git push origin deploy 를 차례대로 입력해줍니다.
 

 
 
생성된 혹은 기본의 deploy 브랜치로 전환 후 Actions 로 들어가시면 워크플로가 등록되어 대기중인 상태를 확인할 수 있습니다.
 

깃허브 액션이 정상적으로 마무리 되었다면 갈색 표시가 초록색 마크 형태로 변경되었을 겁니다.

 
그 후 AWS S3 객체에 들어가보면 마지막 수정 부분이 바뀐 것을 볼 수 있습니다. 제가 현재 이 포스트를 작성하고 있는 시간이 저녁 5:58 이므로 정상적으로 업로드 되었습니다.

 
사이트도 정상적으로 접속되는 것을 보니 잘 마무리 되었네요.

 
 

[나가는 말] 생각보다 많은 시간이 소요되었습니다.

깃허브 액션을 이용해 CI/CD 를 구축하는 것이 처음에는 부담으로 다가왔습니다. 과거와 달리 좋은 자료들이 인터넷에 널리고 널렸다 해도, 지피티와 같은 유용한 도구들이 많이 존재한다 해도 프로젝트 환경은 참고하는 자료마다 조금씩 변경되거나 차이가 나는 부분이 있어서, 해당 부분을 현 프로젝트의 상황에 맞게 수정하는 작업은 생각보다 순탄하지는 않았던 것 같습니다. 또한,익숙하지 않은 커맨드는 지금도 배포를 완료한 현재에도 어색한 느낌을 지울 수 없습니다. 다
 
이번 기회를 통해 프로젝트를 배포해보는 과정에서 AWS S3 만을 이용해 배포하는 경우 어떤 부분이 비용적으로나 보안상으로 문제가 될 수 있는지, Cloud Front 를 같이 사용했을 때 생겨나는 이점이 무엇인지 등이 글로만 보았을 때하고는 확실히 다르게 와닿는 경험이었습니다. 무엇이든 글로만 보는 것 보다 직접 해봐야 체득되는게 많음을 느꼈기에 생각보다 많은 시간을 소요 되었지만, 좋은 경험이었네요.

 

다들 막힘없이 배포 잘 끝내셔서 좋은 성과 있으시면 좋겠습니다. 저는 이만 물러가 봅니다.
 


[번외] 트러블 슈팅 보다는 사소한 문제들

깃허브 액션 실패 | 자격증명 로드 불가

깃허브 액션이 실패했는데, 그 이유가 자격증명 로그 불가 라고 합니다.

 
일단 이러한 문제의 대다수는 오타에서 기인하기 때문에 액세스 키 관련 코드를 살펴봤습니다. 아랫부분은 워크플로 파일에서 작성한 자격증명 부분입니다.

 
그리고 이 부분은 깃허브 레포지토리의  환경변수 설정하는 부분입니다. 역시, 오타가 문제였습니다. 위에는 _ID 를 붙여 놓고 아래에서는 붙여놓지 않았다는 사실을 확인하고, 워크플로 파일을 수정합니다.

 
수정 하고 나니 노란줄도 사라졌습니다. 시스템에서는 문제가 되는 부분을 알려주고 있었습니다.
 

 
 
해당 조치 후 다시 커밋 후 올려봅니다. 자격증명 부분을 클리어 하고 다음 단계로 넘어갔습니다. 

 
그래서 결론은 오타 검수를 잘해두자 입니다. 이런 사소한 실수가 적지 않은 부분에서의 시간 소모를 유발하는 것 같습니다. 
 

깃허브 액션 실패 | 종속성 설치 실패 => 타입선언 누락이 되면 빌드를 실패합니다.

앞서 자격증명 문제를 해결하니 이번에는 빌드 실패가 발생했습니다.

 
혹시 로컬에서도 빌드가 실패하는가 싶어서 npm run build 를 입력해주었습니다. 그 결과, 로컬 빌드는 성공했습니다. 

500KB 넘어갔으니 최적화 좀 하라고 합니다. 확실히 파일이 크면 돈 빌려쓰는 클라우드에서 돈 먹는 하마가 될 수 있음을 알기에 최적화에 대해 도전해 봐야 겠습니다.

 
근데 에러 메시지를 자세히 보니 처음 부터 일관 되게 타입선언과 연관되어 있다는 점을 확인할 수 있었습니다. 이는 빌드 시에 타입선언과 관련한 종속성 설치에 실패하였다는 것 입니다.

 
예제를 참고하여 작성했을 때, npm run build 단계를 시작하기 전에 package.lock.json 에 있는 모듈 정보를 토대로 종속성을 설치할 수 있도록 npm ci --production 을 실행하고 있습니다. 저는 여기서 --production 이라는 config 을 붙여서 사용하였습니다. 이렇게 되면, 개발 의존성이 아닌 의존성 모듈만을 설치합니다. 

 
그런데 npm run build 시에는 개발 의존성에 있는 모듈 이 아닌 의존성 모듈만을 설치하므로 개발 의존성에 모여있는 타입 선언 관련 모듈들이 인지되지 못함에 따라 해당 문제가 발생한 것이라 추측 됩니다. 그래서 해당 부분을 수정하여 아래와 같이 해주었습니다.

          run: |
            node --version
            npm ci

 
 
그후  다시 저장소에 푸시 해봅니다. 다행히 정상적으로 설치가 완료되었습니다. 애초에 단순하게 생각해보면, 로컬에서도 npm run build 할 때 타입 선언이 누락되었거나 이상한 점이 있으면 빌드 에러를 띄웠습니다. 이 부분도 결국 같은 맥락에서 발생한 문제였구요. 다음에는 해당 문제 가지고 실수는 하지 않을 것 같다는 점에서 좋은 배움의 기회였던 것 같습니다. 

 

반응형