avatarBloKoo
About Me
[AWS] Elastic Beanstalk + ECR + Git Action 으로 Nest.js 무중단 배포하기
[AWS] Elastic Beanstalk + ECR + Git Action 으로 Nest.js 무중단 배포하기
2022-03-26 16:19:00

회사에서 EB와 ECR을 통해 Nest.js를 배포하기로 정하여 몇번의 수고 끝에 해당 스택으로 진행 해보고자 한다.

개인적으로 스타트업에는 잘 맞는 플랫폼이라고 생각하며 빠르고 간편하게 인프라를 구성할 수 있고 신경써서 한번만 구성하게 된다면 오토스케일링, 로드밸런싱, 모니터링, 용량 등등 신경 써야할 부분이 많이 줄어들기 때문에 시도했다.

ECR을 사용한 이유는 확장되면서 도커를 사용할 일이 많아 미리 연습차원에서 적용해보자 이번 배포 스택에 추가했다.

기존에 사용중이었던 CodePipeline, CodeBuild, CodeDeploy 보다는 개인적으로 더 간편하고 좋은 것 같았다. 그래서 이번에 잘 정리하여 나중에 다른 프로젝트에서 해당 스팩을 적용하기 편하도록 글을 작성해보고자 한다.

기본적으로 Nest.js 프로젝트, AWS 계정, Docker가 준비되면 될 것 같다.

1. Dockerfile 만들기

프로젝트에 먼저 Dockerfile을 만들고 해당 파일을 로컬 Docker에서 테스트를 한다.

먼저 잘 동작하는 Dockerfile이 있어야 후에 덜 수고스러운 것 같아서 먼저 만들어보길..

Root에 docker라는 folder를 만들고 dev, prod으로 나누며 본 글에서는 dev를 서버에 올리는 작업으로 예시를 만들 예정이다.

파일 트리

.dockerignore

node_modules
dist

Dockerfile

해당 Dockerfile은 퍼블릭한 코드를 많이 가져왔는데 추가한 것은 .env 파일이다. 해당 부분이 빠지면 실행을 할때 적절한 환경변수를 찾지 못해 실행이 되지 않거나 문제가 생기기 때문에 추가함.

FROM node:14.17.6-alpine as builder

WORKDIR /app

COPY . /app

RUN npm ci \
    && npm i @types/express \
    && npm i -g @nestjs/cli \
    && npm run build

FROM node:14.17.6-alpine

WORKDIR /app

COPY --from=builder /app/package*.json /app/
COPY --from=builder /app/node_modules/ /app/node_modules/
COPY --from=builder /app/dist/ /app/dist/
COPY --from=builder /app/.env.development /app/
EXPOSE 3001
CMD ["npm", "run", "start:dev"]

이제 프로젝트 루트에 터미널을 켜서 실제 이미지 빌드를 진행하고자함.

{ docker image name } 에 이미지 명을 입력해하면 됩니다.

docker build -f docker/dev/Dockerfile -t { docker image name } .
빌드 중

빌드가 완료되면 빌드 때 정한 { docker image name } 으로 컨테이너를 띄워 본다.

docker run -p 3001:3001 { docker image name }
실행 중

이제 기본적인 Dockfile은 준비가 끝. 이제 Github action을 구성한다.

2. Github workflows 만들기

루트에 .github/workflows 폴더를 만들고 그 안에 dev-deploy.yml을 만든다.

* 그대로 복사해서 사용하면 안되고 한 번씩 흐름을 따라가면서 수정할 부분에서 수정해야함.

1. 해당 flow가 발생할 브랜치명

2. AWS_ACCESS_KEY_ID

3. AWS_SECRET_ACCESS_KEY

4. env

5. ECR 레포지토리 이름

6. Beanstalk 어플리케이션 이름

7. Beanstalk 환경 이름

이렇게만 추가하시면 끝! 굉장히 간단하다.

2-4의 경우는 github의 secrets를 이용하면 좋다. 직접 입력해도 상관없지만 보안의 문제로 secrets을 사용하면 좋은데 아래의 링크에서 추가하는 방법을 확인할 수 있다.

name: Deploy to ECR

on:
  push:
    branches: [ 해당 flow가 발생할 브랜치명 ]

jobs:
  build:
    name: Build Image
    runs-on: ubuntu-latest
   
    steps:
    - name: Check out code
      uses: actions/checkout@v2
    
    - 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: ap-northeast-2

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Create env file
      run: |
        touch .env.development
        echo DB_HOST=${{ secrets.DB_HOST }} >> .env.development
        echo DB_PORT=${{ secrets.DB_PORT }} >> .env.development
        echo DB_USERNAME=${{ secrets.DB_USERNAME }} >> .env.development
        ...
        cat .env.development

    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: ECR 레포지토리 이름
        IMAGE_TAG: latest
      run: |
        docker build -f docker/dev/Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
    - name: Get current time
      uses: 1466587594/get-current-time@v2
      id: current-time
      with:
        format: YYYYMMDD_HH-mm-ss
        utcOffset: "+09:00"
    - name: Generate deployment package
      run: |
        mkdir -p deploy
        cp docker/dev/Dockerrun.aws.json deploy/Dockerrun.aws.json
        cd deploy && zip -r deploy.zip .
    - name: Beanstalk Deploy
      uses: einaregilsson/beanstalk-deploy@v14
      with:
        aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        application_name: Beanstalk 어플리케이션 이름
        environment_name: Beanstalk 환경 이름
        version_label: earth-docker-${{steps.current-time.outputs.formattedTime}}
        region: ap-northeast-2
        deployment_package: deploy/deploy.zip
        wait_for_environment_recovery: 200

이렇게 Github action의 준비 끝!

이제 여기에서 사용할 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, ECR 레포지토리 이름, Beanstalk 어플리케이션, 환경 이름 을 만들어 보자.

3. ECR 만들기

aws console에서 Elastic Container Registry를 검색!

Github action에서 사용할 ECR 레포지토리 이름을 입력한 뒤 다른 건 건드리지말고 생성

ECR도 준비 끝.

이미지 태그는 lastest으로 고정했기 때문에 필요하다면 이를 github workflow에 수정할 태그를 준비해서 넣어도 좋다.

그렇게 되면 ECR에서 태그를 관리할 수 있다. 아래에서 보이는 스크린샷은 태그가 lastest라 과거 이미지는 전부 untagged가 되어있다.

4. Elastic Beanstalk 만들기

aws console에서 마찬가지로 Elastic Beanstalk를 검색!

애플리케이션에서 새 어플리케이션 생성을 클릭!

Github action에서 사용할 BeanStalk 어플리케이션 이름을 입력하고 다른 건 건드리지말고 생성을 해줍니다.

방금 만든 어플리케이션을 클릭하여 새 환경 생성 -> 웹 서버 환경 순서로 차례차례 클릭.

Github action에서 사용할 BeanStalk 환경 이름을 입력하고 도메인도 본인이 사용하고자하는 도메인을 입력하고 가용성 확인을 클릭

플랫폼은 다양하게 있지만 ECR을 사용하고자 Docker로 선택.

추가 옵션 구성을 눌러서 이제 디테일한 설정을 해준다. 여기서는 사실 정답이 없기 때문에 배포하고자 하는 환경과 회사의 스케일 등을 고려하여 선택.

먼저 사전설정에서 사용자 지정 구성을 함.

그 다음 인스턴스에서 편집.

인스턴스 보안 그룹을 사용해야하기 때문에 사용하고자 하는 보안 그룹을 추가. 지금 안하고 구성이 완료되고 추가해도 괜찮으니 나중에 추가하시면 된다.

용량에서 편집.

오토 스케일링을 사용하고자 한다면 로드 밸런싱 수행을 선택하고 아니면 단일 인스턴스를 사용. 본 글에서는 로드 밸런싱 수행을 선택.

이제 원하는 인스턴스 유형을 선택. 이것도 사용 목적에 따라 선택하면 됨

* 로드 밸런싱을 선택하시면 조정 트리거를 선택할 수 있다.

이것도 자유롭게 설정이 가능하지만 아래 보이는 예제는 CPU 사용률이 70%이상이면 인스턴스가 추가되고 30%로 떨어지면 추가된 인스턴스가 삭제되는 조정 트리거다.

로드 밸런서에서 편집.

Application LB를 사용하고 https를 사용하고자 한다면 리스너에 SSL 인증서를 선택 추가함.

롤링 업데이트와 배포에서 편집.

배포 방식도 여러 개이지만 변경 불가능을 선택.

아래 링크에서 배포 방식에 대해서 설명 되어있으니 참고하시면 될 것 같다.

보안에서 편집.

사용할 Role과 키 페어와 인스턴스 IAM을 추가.

기본으로 구성된걸 사용하면 되는데 IAM 인스턴스 프로파일에 정책 하나만 추가해주시면 된다.

아래의 보이는 스크린샷에 빨간 박스의 정책을 추가해준다.

마지막으로 네트워크에서 편집을 눌러 VPC 설정을 해준다.

그렇게....만들고 몇분 지나면 샘플 어플리케이션이 올라간다.

5. Github action에서 사용할 IAM 만들기

aws console에서 IAM에 들어가 사용자 추가.

아래의 3가지 정책을 추가하여 사용자를 만든다.

- AmazonEC2ContainerRegistryFullAccess
- AmazonECS_FullAccess
- AdministratorAccess-AWSElasticBeanstalk

여기에서 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY 까지 준비 끝.

6. 배포

현재 배포가 되어있어서 재배포를 하여 테스트 해본다. 현재 환경에서 제공하는 url을 누르면 data가 test로 나옵니다. 이를 hello로 변경해보도록 한다.

코드를 변경하고 trigger branch에 push.

github action의 process

새로고침을 하면서 체크해보면 이전 배포의 data : test와 새로운 배포의 data : Hello가 번갈아가면서 나오면서 정상적으로 배포가 완료되면서 예전 인스턴스는 종료가 되면서 무중단 배포가 이뤄진다.!!!!

끝!!