AWS CodeDeploy와 Github Action을 이용해 EC2에 Nuxt3 배포하기 - 코드 작성

Server |

Language : KO,EN

안녕하세요 Lovefield입니다.

이번 글은 “AWS CodeDeploy와 Github Action을 이용해 EC2에 Nuxt3 배포하기 - 설정”글에 이어서 Github Action과 AWS CodeDeploy를 이용해 Nuxt3을 배포하는 방법 중 코드 작성 부분을 서술합니다.

1. AWS CodeDeploy Agent 설치

먼저 배포를 실행할 인스턴스에 AWS CodeDeploy Agent를 설치해야 합니다. 아래의 명령어 순서대로 입력해 설치를 진행합니다.

sudo apt update
sudo apt install ruby-full
sudo apt install wget
cd /home/ubuntu
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
sudo ./install auto

주의할 점은 인스턴스에 IAM 설정을 준 이후에 하시는 걸 추천해 드립니다. CodeDeploy Agent가 설치된 이후 인스턴스에 IAM 역할을 부여하셨다면, 역할이 적용되지 않아 CodeDeploy Agent를 재 시작 해야합니다. 그런 경우 sudo service codedeploy-agent restart명령어를 통해 재시작 해주세요.

다음 명령어를 통해 CodeDeploy Agent가 설치 및 실행되고 있는지 확인 할 수 있습니다.

sudo service codedeploy-agent status
> codedeploy-agent.service - LSB: AWS CodeDeploy Host Agent
> Loaded: loaded (/etc/init.d/codedeploy-agent; generated)
> Active: active (running) since Tue 2023-11-07 10:20:04 KST; 5h 39min ago

2. Github Action 작성하기

Github Action을 설정하기 위해서 .github/workflows/deploy.yml 파일을 생성합니다.

name: Deploy

on:
   pull_request:
       branches:
           - stage
       types: [closed]

on 부분은 트리거를 지정하는 부분입니다. pull_request가 발생했을 때를 트리거로 잡았습니다. 속성으로 PR이 stage로 향했을 때와 PR이 closed 된 경우를 추가해 줍니다. PR은 merged도 closed로 인식하기 때문입니다.

jobs:
   buildAndDeploy:
       if: github.event.pull_request.merged == true
       runs-on: ubuntu-latest
       permissions:
           contents: read
           packages: write

jobs 부분은 실제로 명령 구문을 작성하는 공간입니다. buildAndDeploay라는 업무를 하나 생성했으며 그 하위에 조건들을 붙여줍니다. 해당 if 문은 PR이 실제로 merged가 된 경우인지를 확인합니다. merged 된 경우만 buildAndDeploy 항목이 실행됩니다.

Github Action에서는 가상컴퓨터로 Ubuntu, Mac os,window를 사용할 수 있습니다. 그중 저희는 친숙한 Ubuntu를 사용하도록 지정했습니다. permissions는 권한 관련 설정입니다. build를 해야 하므로 각각 read, write 권한을 부여합니다.

       steps:
           - uses: actions/checkout@v3

           - uses: actions/setup-node@v3
             with:
                 node-version: "18.x"
                 registry-url: https://npm.pkg.github.com
                 scope: "@octocat"

           - uses: szenius/set-timezone@v1.2
             with:
                 timezoneLinux: "Asia/Seoul"

           - name: Check version
             run: node -v

           - name: Get current date
             id: date
             run: echo "date=$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT

steps 부분은 Github Action의 가상 컴퓨터에서 순차적으로 실행되는 명령 리스트들입니다. 먼저 기본적으로 필요한 항목들을 실행합니다.

actions/checkout@v3 부분은 현재 저장소의 브런치를 사용할 수 있도록 해주는 명령어입니다. Action 작성에서 빠질 수 없는 부분이죠.

Node.js를 이용해야 하기에 actions/setup-node@v3 을 통해 사용할 노드의 버전을 지정해 줍니다.

Github Action이 동작하는 컴퓨터의 시간은 기본적으로 UTC 시간대입니다. 한국의 시간대를 사용할 예정이므로 “Asia/Seoul”로 지정해 줍니다.

Build 파일에 timestamp를 사용하고 싶었기에 Action이 실행된 시점의 시간대를 저장해 줍니다. 다음 명령어에서 파일 이름을 지정할 때 사용할 예정입니다.

           - name: Install
             run: yarn install --frozen-lockfile

           - name: Create env file
             run: echo "$ENV_VALUE" >> .env
             env:
                 ENV_VALUE: ${{ secrets.STAGE_BUILD_ENV }}

           - name: Build
             run: yarn run build

           - name: Get file name
             id: file
             run: echo "filename=front-end-${{ steps.date.outputs.date }}.zip" >> $GITHUB_OUTPUT

           - name: Create ZIP file
             run: zip -qq -r ./${{ steps.file.outputs.filename }} .

           - name: Configure AWS credentials
             uses: aws-actions/configure-aws-credentials@v3
             with:
                 aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
                 aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
                 aws-region: ap-northeast-2

           - name: Upload Build file
             run: aws s3 cp --region ap-northeast-2 ./${{ steps.file.outputs.filename }} s3://deploy/${{ steps.file.outputs.filename }}

           - name: Create AWS codedeploy
             run: aws deploy create-deployment
                 --application-name Git-action-stag
                 --deployment-config-name CodeDeployDefault.AllAtOnce
                 --deployment-group-name git-action-stage-group
                 --s3-location bucket=deploy,key=${{ steps.file.outputs.filename }},bundleType=zip

Install 부분은 여러분에게 익숙할 겁니다. Build를 하기 전에 필요한 모듈을 설치해 줍니다.

Create env file 부분은 secrets에 등록해 둔 설정 내용들을 .env 파일로 만드는 과정입니다. 프로젝트 특성상 .env파일을 사용하다 보니 불가피하게 필요해 작성하게 되었습니다.

Build 부분은 드디어 Build를 실행하는 구문입니다.

Get file name 부분에서 사용할 파일명을 지정합니다. 위에서 지정했던 날짜와 결합하여 “front-end-YYYYMMDD-HHMMSS.zip” 형태의 파일명이 구성됩니다.

Create ZIP file 부분에서 앞서 선언한 파일명을 통해서 zip 파일을 생성해 줍니다.

Configure AWS credentials 부분에서 AWS에 접속이 가능한 권한을 설정합니다.

Upload Build file 부분에서 S3 버킷에 압축한 Build 파일을 올립니다.

Create AWS codedeploy 부분에서 S3에 올린 파일 위치와 AWS CodeDeploy에서 설정한 어플리케이션, 배포 그룹을 지정해 배포 요청을 보냅니다.

3. appspec.yml 작성

appspec.yml은 AWS CodeDeploy가 실행될 때 참조하게 되는 파일입니다.

저장소 최상위에 “appspec.yml” 파일을 생성합니다.

version: 0.0
os: linux

files:
   - source: /
     destination: /home/deploy/action
     overwrite: yes

permissions:
   - object: /
     pattern: "**"

hooks:
   ApplicationStart:
       - location: deploy.sh
         timeout: 60
         runas: root

files 부분은 S3에 업로드된 파일이 어디에 풀릴 것인지를 정합니다. source 부분은 EC2 Instance의 경로가 아니며, 압축된 파일의 경로입니다. destination 부분이 실제 EC2 Instance의 경로입니다.

permission 부분은 현재 명령어를 실행하는 사용자의 권한입니다.

hooks 는 배포 과정 중 실행할 스크립트를 지정할 수 있습니다. 여러 시점이 존재하지만, 저희는 ApplicationStart를 사용했습니다.

4. deploy.sh 작성

appspec.yml 에서 지정한 hook 파일입니다.

저장소 최상위에 deploy.sh를 생성합니다.

#!/usr/bin/env bash

cd /home/deploy/action

TIMESTAMP=`date +%Y%m%d`

echo ">>> 프론트 재시작 스크립트 - $(date +%Y-%m-%d\ %H\:%M\:%S) <<<" >> "/home/deploy/log/$TIMESTAMP.log"

echo ">>> pid 확인" >> "/home/deploy/log/$TIMESTAMP.log"
PID=$(pgrep -f ".output/server/index.mjs")

if [ -z $PID ]
then
   echo ">>> pid 없습니다" >> "/home/deploy/log/$TIMESTAMP.log"
else
   echo ">>> $PID 번 pid 종료" >> "/home/deploy/log/$TIMESTAMP.log"
   kill -9 $PID
fi


nohup node /home/deploy/action/.output/server/index.mjs 1>> /home/deploy/log/$TIMESTAMP.log 2>&1 &

echo ">>> 프론트 배포 $(date +%Y-%m-%d\ %H\:%M\:%S) <<<" >> "/home/deploy/log/$TIMESTAMP.log"

pm2를 사용할 예정이라면 pm2 명령어를 사용하시면 됩니다. 이유는 딱히 없지만 저는 pm2를 사용하지 않았습니다. 위의 스크립트를 설명하면 다음과 같습니다.

1. 작업할 폴더로 이동.

2. timestamp 생성

3. 실행중인 pid 확인

4. pid가 있다면 종료

5. nohup을 이용해 어플리케이션 실행

Lovefield

Web Front-End developer

하고싶은게 많고, 나만의 서비스를 만들고 싶은 변태스러운 개발자입니다.