AWS EC2에서 React + Next.js 배포하기

NCOOKIE·2023년 12월 15일
0
post-thumbnail

들어가며

IMAD 프로젝트에 새로 합류하게 된 웹 담당 멤버가 들어와서, AWS 계정을 같이 사용하기로 했다. 이를 위해 IAM 계정 및 역할 등을 새로 추가하고 자동배포 파이프라인을 만드는데 도움을 주었다. 대부분의 내용은 이전에 작성했던 글과 많은 부분이 겹쳐서 간단하게 작성했다.

AWS 관련 설정

IAM Identity Center

웹 프론트 개발자(?)가 사용할 계정을 IAM Identity Center에서 생성해주었다.

권한 세트를 생성한다. 권한은 직접 정의해서 사용할 수도 있지만, 나를 포함한 프로젝트 멤버들 모두가 AWS 기능에 대해 잘 파악하고 있는 것도 아니고 필요에 따라 권한을 확장할 일이 많을 것 같아 PowerUserAccess라는 사전 정의된 권한 세트를 선택했다.

생성한 사용자와 권한 세트를 묶어줬다.

이후 사용자 생성 시 기입했던 이메일 주소로 발송된 메일을 확인하여 비밀번호를 설정하고 로그인 링크를 받을 수 있다.

이전 포스트 참고

https://velog.io/@ncookie/Github-Actions-AWS-EC2-Spring-Boot-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-CICD-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%84%A4%EC%A0%95

위의 링크 블로그 글에서 배포하기 챕터 중 EC2 관련 설정태그 추가, S3 버킷 생성, CodeDeploy 설정, Github actions 설정까지 따라한다.

네트워크 관련 설정

현재 구축한 AWS의 EC2 인스턴스 위에서 동작하고 있는 스프링 서버까지 트래픽이 도착하기 위한 과정을 간단하게 그림으로 표현했다. 진행하고 있는 프로젝트에서는 React + Next.js를 스프링 서버와 같은 EC2 인스턴스에서 배포하려고 한다. 그림의 오른쪽에 있는 요소부터 설정해보자.

대상그룹

3000번 포트를 대상으로 가지는 대상 그룹을 생성한다.


아래 로드밸런서와 Route 53 내용을 따라하면 추가 요금이 부과될 수 있다. 나머지는 이후에 작성한 글(AWS 로드밸런서로 서브도메인 설정하기
을 참고하자.

로드밸런서

위에서 생성한 대상그룹을 매칭시켜주는 로드밸런서도 생성해준다.

Route 53

https://velog.io/@ncookie/AWS-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%9D%B4%EC%A0%84-%EB%B0%8F-HTTPS-%EC%A0%81%EC%9A%A9 참고

기존의 HTTPS를 적용하기 위해 만들었던 Route 53의 호스팅 영역에 레코드를 추가할 것이다.

3000번 포트에 연결된 로드밸런서를 값으로 넣어준다. 나는 웹 서비스를 iimad.com 도메인에서 제공하고 싶어서 subdomain을 비워뒀다.

만약 web.iimad.com에서 서비스하고 싶다면 레코드 이름의 subdomain에 web을 넣어주면 된다.

Github 설정 및 관련 파일 추가

Github Actinos yml 파일 작성

name: Deploy # Workflow 이름
on: # Event 감지
  push:
    branches:
      - release # 해당 브랜치의 푸쉬가 일어날 때 CI/CD를 진행하겠다는 뜻
env:
  S3_BUCKET_NAME: ryuforaws01-github-actions-s3-bucket
  CODE_DEPLOY_APP_NAME: deploy-application-front
  CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: deploy-group-front
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code. # Repo checkout
        uses: actions/checkout@v2

      - name: Check Node v # Node v 확인
        run: node -v

      - name: Install Dependencies # 의존 파일 설치
        run: yarn install 

      - name: Build # React Build
        run: yarn build
        env:
          CI: ""

      - name: zip create
        # zip 파일 생성
        run: zip -qq -r ./build-fe.zip .
        shell: bash

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

      # 빌드 결과물을 S3 버킷에 업로드
      - name: Upload to AWS S3
        run: |
          aws s3 cp --region ap-northeast-2 ./build-fe.zip s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip

      - name: Deploy # Deploy to EC2
        run: |
          aws deploy create-deployment \
            --application-name ${{ env.CODE_DEPLOY_APP_NAME }} \
            --deployment-config-name CodeDeployDefault.AllAtOnce \
            --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \
            --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip

release 브랜치에 push 이벤트가 발생할 때마다 미리 설정된 동작을 수행하는 yml 파일을 추가한다. 프로젝트 루트 경로 기준 .github/workflows/deploy.yml 이다. 파일 이름은 임의로 수정할 수 있다.

appspec.yml

version: 0.0
os: linux

files:
  - source: /
    destination: /home/ec2-user/apps/imad-web
    overwrite: yes
permissions:
  - object: /
    pattern: "**"
    owner: ec2-user
    group: ec2-user
    mode: 755
hooks:
  AfterInstall:
    - location: scripts/stop.sh
      timeout: 60
      runas: ec2-user
  ApplicationStart:
    - location: scripts/start.sh
      timeout: 60
      runas: ec2-user

CodeDeploy에서 배포를 위해 참조할 AppSpec 파일을 작성한다. AppSpec 파일을 사용해서 프로젝트의 어떤 파일들을 EC2의 어떤 경로에 복사할지 설정하고, 배포 작업 이후에 수행할 스크립트를 지정하여 자동으로 서버가 실행되게 할 수 있다. AppSpec 파일은 기본적으로 프로젝트의 루트 디렉터리에 위치해야 한다.

스크립트 작성

프로젝트 루트 경로 기준 scripts/에 스크립트 파일을 추가한다. 이들은 CodeDeploy의 배포 과정 중 AfterInstall, ApplicationStart 등의 이벤트가 발생할 때 실행된다.

stop.sh

cd /home/ec2-user/apps/imad-web

fuser -k 3000/tcp

echo 서버종료

start.sh

#!/usr/bin/env bash

PROJECT_ROOT="/home/ec2-user/apps/imad-web"

APP_LOG="$PROJECT_ROOT/application.log"
ERROR_LOG="$PROJECT_ROOT/error.log"
DEPLOY_LOG="$PROJECT_ROOT/deploy.log"

TIME_NOW=$(date +%c)

cd $PROJECT_ROOT
nohup yarn start > $APP_LOG 2> $ERROR_LOG &

echo "$TIME_NOW > yarn 실행 중" >> $DEPLOY_LOG

배포 테스트

위의 deploy.yml, appspec.yml, stop.sh, start.sh 등의 파일들을 추가했다면, 배포를 진행해보자. featuremaster 브랜치의 커밋들을 release 브랜치에 pull request를 날려 merge 시켜보자. Github의 Actinos 탭에서 스크린샷과 같이 지정된 동작들을 수행하는 모습을 확인할 수 있다.

Github Actions의 동작이 끝난다면 AWS CodeDeploy에서 배포를 확인해보자. 별 다른 문제가 없다면 배포 상태가 성공으로 바뀐 것을 볼 수 있고,

사이트 접속도 잘 되는 것을 확인할 수 있다.

에러 케이스

CodeDeploy timeout 에러

CodeDeploy의 배포 중 계속해서 timeout이 발생하는 문제가 있었다. timeout 제한 시간을 아무리 넉넉하게 줘도 같은 문제가 발생했다. 특이한 점으로는 배포 시작 후 잠시 후에는 서버가 정상적으로 동작했지만, timeout이 발생하면서 중단됐다.

nohup yarn start

문제는 start.sh 파일에서 yarn start 명령어를 실행했을 때 백그라운드에서 동작하지 않았기 때문이다. 위의 스크립트는 처음에 작성했던 start.sh의 일부다.

리눅스 시스템에서 nohup는 쉘 스크립트 파일을 데몬 형태로 실행시키는 명령어다. 터미널을 실행한 세션이 끊겨도 실행한 프로세스는 계속 동작하게 한다.

& 기호를 명령어 맨 뒤에 붙이면 Foregroud가 아닌 Background에서 실행된다. & 기호로 실행한 명령어는 세션이 종료되면 해당 프로그램도 함께 종료된다.

그렇기 때문에 리눅스 시스템에서 세션이 종료되도 프로그램이 종료되지 않고 백그라운드에서 계속 동작시키고 싶다면 nohup&를 조합해서 사용하면 된다.

nohup yarn start > $APP_LOG 2> $ERROR_LOG &

nohup&를 적용하면 위와 같은 코드가 된다. 서버 실행 시 표준출력과 표준에러 로그를 기록하고 싶어서 리다이렉션으로 빼주었다.

참고

배포

에러

profile
일단 해보자

0개의 댓글