Github Action, EC2, S3, CodeDeploy를 사용한 자동 배포(feat. Nest.js)

박진(TsTunas)·2023년 3월 13일
0

세팅

1. 서비스를 제공할 EC2 생성

사용할 EC2는 EC2 기본 권한에 더해서 S3에도 접근 가능한 권한이 필요하다. 그러니 IAM에 가서 Roles를 만들자. entity type은 EC2에 부여할 거니까 AWS Service로 하고, Common Use Case인 EC2를 선택하고 다음으로 넘어가자. 그리고 s3를 검색해서 FullAccess 권한을 추가해주면 된다. 이름은 chalkak-ec2(필자 프로젝트명이 chalkak) 정도로 하자.
이제 EC2를 만들어줄 때 role을 chalkak-ec2로 주자. 만약 이미 만들어진 EC2에 줘야한다면 적용한 뒤에 한 번 재부팅이 필요하다.
나는 우분투를 사용하였고, 미리 사용해야하는 명령어는 다음과 같다.

# codedeploy-agent 설치
sudo apt update
sudo apt install ruby-full
sudo apt install wget
cd /home/ubuntu
# 내 경우에는 서울 리전이어서 이렇게 된다.
# 원래는 https://<storage name>.s3.<region>.amazonaws.com/latest/install 사용
wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install
chmod +x ./install
 sudo ./install auto > /tmp/logfile
 sudo service codedeploy-agent status
# nodejs 설치(18.x)
sudo apt install curl
curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install nodejs

# pm2 설치
sudo npm i -g pm2

2. 배포 프로세스를 담당할 CodeDeploy 설정


필자가 만들 때에는 Deployment configurations를 먼저 건드렸지만, 어떻게 배포할건지 설정할 때 기존에 미리 준비해둔 설정(전부 다 새로운 배포환경으로 or 적어도 하나는 서비스 살아있게 무중단 배포 등) 중에 선택한다면 굳이 필요없다.
우리가 배포할 서비스를 애플리케이션에 등록하자.

이렇게 만들어주면 된다. 간단하다. 다음은 배포 그룹을 만들면 된다.

여기서 중요한 부분은 IAM에서 role에서 codeDeploy commonuse를 골라 만들어서 서비스의 권한을 설정하는 것과 어떤 방식으로 배포를 할지 정한다.

일단 단일 인스턴스로 배포를 원하는 경우에는 in-place, Amazon EC2 instances, CodeDeployDefault.AllAtOnce로 하고 그보다 아래에 있는 로드밸런서 설정은 풀어주자.
Amazon EC2 instances를 선택하게 되면 Tag로 어떤 인스턴스를 대상으로 배포할건지 지정할 수 있는데, Name Tag로 미리 만들어준 EC2의 이름을 적으면 된다.

3. 실행에 필요한 코드를 담아둘 S3 생성

그냥 이미지 저장에 쓰는 S3 만들듯이 하나 새로 만들어주면 된다.

4. 해당 AWS에 접근할 유저를 IAM에서 생성

권한을 EC2, S3, CodeDeploy에 접근할 수 있을 정도로 만들어주자.
그 다음에 유저에 들어가 Create access key를 눌러 생성하자.

얻어낸 키들은 나중에 깃허브 액션에서 쓸 수 있도록 리포지토리의 secret에 저장해놓자.

AWS_REGION은 서울의 경우에는 ap-northeast-2와 같이 저장해놓으면 된다.

5. GitHub Action 워크플로우 설정

name: Deploy NestJS Application

on:
  push:
    branches:
    - dev # 임시로 dev, 원래는 main이 되어야함.

env:
  S3_BUCKET_NAME: chalkak-s3 # 실행에 필요한 파일들이 담길 s3 버킷 이름
  PROJECT_NAME: chalkak # 파일들이 들어갈 폴더 이름
  AWS_CODEDEPLOY_APPLICATION_NAME: chalkak-app # 아까 codeDeploy에서 application 만들 때 쓴 이름
  AWS_CODEDEPLOY_DEPLOYMENT_GROUP_NAME: chalkak-deployment # delevelopment group

jobs:
  deploy:
    name: test and deploy nodejs app to S3 bucket
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x]

    steps:
    - uses: actions/checkout@v3
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-verison: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test
    # 여기까지는 CI 테스트. 코드에 문제없는지 확인.
    # zip file로 저장하기 위해 압축 작업을 합니다. 크기를 줄이기 위해 필요없는 파일들은 정리합니다.
    - name: Make a zip File
      run: zip -r ./$GITHUB_SHA.zip . -x "node_modules/*" "coverage/*" "src/*" "test/*" "README.md" ".env*" "*.git*"
      shell: bash
    # AWS에 인증정보를 설정합니다.
    - 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 S3
      run: aws s3 cp --region ${{ secrets.AWS_REGION }} ./$GITHUB_SHA.zip s3://$S3_BUCKET_NAME/$PROJECT_NAME/$GITHUB_SHA.zip

	# CodeDeploy에게 s3위치를 알려주며 해당 위치에 있는 zip 파일을 가지고 development group의 전략에 따라 배포하라고 하기.
    - name: Request Deployment
      run: aws deploy create-deployment --application-name $AWS_CODEDEPLOY_APPLICATION_NAME --deployment-config-name CodeDeployDefault.AllAtOnce --deployment-group-name $AWS_CODEDEPLOY_DEPLOYMENT_GROUP_NAME --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=$PROJECT_NAME/$GITHUB_SHA.zip

이 과정에서 압축 파일의 이름을 동일하게 하고 싶다면 $GITHUB_SHA.zip 대신에 chalkak.zip이라고 쓰고 S3를 만들때 Versioning을 하면 될 수도 있겠으나 저는 $GITHUB_SHA.zip로 했습니다.
이제 dev 브랜치에 push를 하게 되면 자동으로 EC2에 배포가 되게 됩니다.
하지만 아직 끝이 아닙니다. 어느 EC2에 배포를 할 건지는 정했지만 배포의 동작은 정하지 않았습니다.

6. appspec.yml과 이벤트 후크에 쓰일 script들

어떻게 앱이 배포될 것인지를 적은 appspec.yml을 봅시다.

version: 0.0
os: linux
# 아래 files는 압축이 풀린 뒤에 /로 압축된 모든 것을 사용하겠고,
# 그 위치는 /home/ubuntu/chalkak/에 두겠다고 선언합니다.
# 만약 존재하면 덧씌웁니다.(overwrite: yes)
files:
  - source: /
    destination: /home/ubuntu/chalkak/
    overwrite: yes

# 파일의 모든 권한을 ubuntu로 설정합니다.
permissions:
  - object: /
    pattern: '**'
    owner: ubuntu
    group: ubuntu

# CodeDeploy의 배포과정에서 이벤트 훅이 여러개가 있습니다.
# BeforeInstall은 files의 destination에 파일을 넣기 전에 호출되는 훅이며,
# AfterInstall은 파일이 설치된 후에 호출됩니다.
# runas는 sudo 권한이라고 합니다.
hooks:
  BeforeInstall:
    - location: scripts/before_deploy.sh
      runas: root
  AfterInstall:
    - location: scripts/after_deploy.sh
      runas: root

각 이벤트 단계에서 미리 만들어둔 스크립트 파일로 명령어를 실행할 수 있다.

# scripts/before_deploy.sh
#!/bin/bash
REPOSITORY=/home/ubuntu
cd $REPOSITORY


sudo pm2 kill # pm2를 사용할 경우 pm2를 죽임

sudo rm -rf chalkak
# scripts/after_deploy.sh
#!/bin/bash
REPOSITORY=/home/ubuntu/chalkak
cd $REPOSITORY

cp  ../.env* ./ # 비밀 env는 home/ubuntu에 만들어놓고 배포할 때마다 복사해서 씀.


sudo npm i
sudo pm2 start dist/main.js
profile
자바스크립트 전문가가 되고 싶은 아마추어

0개의 댓글