본문 바로가기

aws

[AWS] Elastic Beanstalk + ECR + Git Action 으로 Nest.js 무중단 배포하기

회사에서 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

루트에 .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을 사용하시면 좋은데 아래의 링크에서 추가하는 방법을 확인하시면 좋을 것 같습니다.

 

https://docs.github.com/en/actions/security-guides/encrypted-secrets

 

Encrypted secrets - GitHub Docs

About encrypted secrets Secrets are encrypted environment variables that you create in an organization, repository, or repository environment. The secrets that you create are available to use in GitHub Actions workflows. GitHub uses a libsodium sealed box

docs.github.com

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가 되어있습니다.

이미지 태그

3. Elastic Beanstalk 만들기


aws console에서 마찬가지로 Elastic Beanstalk를 검색해서 들어갑니다.

 

새 애플리케이션 생성

 

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

 

새 환경 생성

웹 서버 환경 선택

Github action에서 사용할 BeanStalk 환경 이름을 입력하고 도메인도 본인이 사용하고자하는 도메인을 입력하시고 가용성 확인을 눌러줍니다. 사실 그렇게 고민할 만큼의 큰 의미가 없습니다.

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

 

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

 

먼저 사전설정에서 사용자 지정 구성을 해줍니다. 

 

인스턴스에서 편집을 눌러줍니다.

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

 

 

용량에서 편집을 눌러줍니다.

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

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

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

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

 

 

로드 밸런서에서 편집을 눌러줍니다.

Application LB를 사용하고 https를 사용하고자 한다면 리스너에 SSL 인증서를 추가해서 추가합니다.

 

 

롤링 업데이트와 배포에서 편집을 눌러줍니다.

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

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

https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/using-features.rolling-version-deploy.html

 

정책 및 설정 배포 - AWS Elastic Beanstalk

정책 및 설정 배포 AWS Elastic Beanstalk는 배포가 처리되는 방법에 대한 여러 옵션을 제공합니다. 그 중에는 배포 정책(한 번에 모두, 롤링, 추가 배치를 사용한 롤링, 변경 불가능, 트래픽 분할)과 배

docs.aws.amazon.com

 

 

보안에서 편집을 눌러서 사용하실 Role과 키 페어와 인스턴스 IAM을 추가해줍니다.

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

IAM 인스턴스 프로파일에 아래의 빨간 박스의 정책을 추가해줍니다.

 

네트워크에서 편집을 눌러 VPC 설정을 해줍니다.

 

 

만들고 몇분 지나면 샘플 어플리케이션이 올라갑니다.

 

 

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


aws console에서 IAM에 들어가 사용자 추가를 합니다.

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

 

- AmazonEC2ContainerRegistryFullAccess

- AmazonECS_FullAccess

- AdministratorAccess-AWSElasticBeanstalk

여기에서 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY 까지 준비가 끝났습니다.

 

 

 

 

 

5. 배포


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

현재상태

코드를 변경하고 trigger branch에 push 했습니다.

github action
process

 

새로고침을 하면서 보면 계속 이전 data : test와 data : Hello가 번갈아가면서 나오면서 정상적으로 배포가 완료되면서 예전 인스턴스는 종료가 되면서 무중단 배포가 이뤄집니다. 

 

 


이렇게 마무리 했습니다. 감사합니다

'aws' 카테고리의 다른 글

[AWS] CodeBuild, CodeDeploy, CodePipeline으로 node 배포하기  (0) 2022.03.21