[Nestjs & AWS EB(linux2) & github action] yarn berry zero install 도전기!!

Collin·2022년 10월 30일
1

👋 해볼것!!!

  1. Nestjs 프로젝트 npm -> yarn berry
  2. aws EB 에서 npm -> yarn berry (package manager 변경)
  3. github action 에 ci/cd 파이프라인 변경

🧐 왜 yarn berry zero install 에 도전하나요??

(대표님께 동의를 구하고 글을 작성합니다 대표님 감사해요!!!)
현재 사내 프로젝트는 aws elastic beanstalk 에 vpc 하나에
[private subnet] 환경 : backend,db
[public subnet] 환경 : frontend
이렇게 총 2개의 환경으로 배포를 전개하고 있습니다
(관련한 글도 얼렁 작성해야 하는데 ㅠㅠㅠ 쓰다 말았는데 조만간 작성 하겠습니다)

그런데 백앤드를 배포하다가... 아래와 같은 에러를 만났습니다

npm ERR! code ENOMEM
npm ERR! syscall spawn
npm ERR! errno -12
npm ERR! spawn ENOMEM

먼저 해야할 것!!! 역시나 구글링!!!

원인은 메모리부족!!!!

[현재 상황]
사실... 정석이라면 빌드 후의 결과만 올려서 쨔잔하고 배포하는 것이 맞는데
계속 에러가 나는 겁니다 (해당 과정은 다른 글에 쓰도록 할게요)
그러다 본 몇 개의 글에서 코드를 eb에 올려서 npm install, npm run build 시키는 글을 보고는
눈 찔끔 감고 했지만 마음에는 큰 죄책감이 있었어요

[해결을 위한 시도들]

  1. 우선 메모리 부족을 해결 위해서 max-old-space-size 옵션을 줘봤지만 ❌
  2. 역시 build를 올려서 하는게 메모리를 너무 많이 잡아 먹는다
    build한 결과물만 올리자 우여곡절 끝에 되긴 했는데 프로젝트 더 개발하니
    그 뒤에도 똑같이 에러가 나더라고요
    npm install할 때 에러가 나는 것 같았습니다 (eb-engine.log)
    (혹시 선배님들 제가 잘못 알고 있는 것이 있다면 따끔하게 말씀해주시면 감사하겠습니다)

결국 npm install 이 문제라고 판단했고
얼마전 저희 회사 CTO분이 npm, yarn, yarn berry 에 대해서 이야기 해주셔서
yarn berry zero install에 도전하게 되었습니다

(저는 yarn을 dependency에 대한 안전성이 더 높다고 판단해서 선호하는 편인데
aws eb 에서 yarn을 사용하기 위해선 추가적으로 진행해야하는 작업들이
프로젝트 처음 ci/cd 파이프라인을 설계할 때는 너무 어렵게 느껴졌습니다
하지만 지금은 충분히 맞을 준비가 되어 있어서 도전!! 해보기로 했습니다)

yarn berry zero install에 도전




👆 Nestjs 프로젝트 npm -> yarn berry

생각보다 쉬웠습니다

1. node_modules 삭제
2. package-lock.json 삭제
3. npm install -g yarn
4. yarn set version berry
5. yarn
6. yarn add -D typescript
7. yarn dlx @yarnpkg/sdks vscode

✌️ aws EB 에서 npm -> yarn berry (package manager 변경)

사실 여기가 저 같은 백앤드 주니어한테는 너무 어려웠습니다 ㅠㅠㅠㅠ
저는 아래와 같은 순서로 npm -> yarn berry로 package manager를 변경하려합니다

  1. eb에서 npm 을 사용하지 않도록 합니다
  2. Nest 프로젝트 빌드 결과물을 배포전에 eb에 yarn berry 설정을 진행합니다
  3. 배포 결과물을 실행합니다

아래와 같이 디렉토리 구조를 가져갔습니다 postdeploy, prebuild, predeploy와 같은 용어들은
eb lifecycle에 관련된 내용입니다

.platform
└── hooks
    ├── postdeploy
    │   └── del-nodemodules.sh
    ├── prebuild
    │   └── prevent-npm.sh
    └── predeploy
        └── yarn.sh


https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/platforms-linux-extend.html
aws eb docs도 같이 공유합니다

1. [eb에서 npm 을 사용하지 않도록 합니다]

사실 전 2번만 잘 실행하면 알아서 1번이 되는 줄 알았는데 amazon linux2 에서
npm 을 실행시키지 않는 방법에 대한 한 줄기의 빛이 되어 주셨던 글 먼저 공유하겠습니다
https://stackoverflow.com/questions/41657226/customize-aws-elasticbeanstalk-nodejs-install-use-yarn
이 글의 제일 마지막 댓글을 보시게 되면

node_modules 폴더가 존재하면 npm install 을 실행하지 않습니다!!!!

실제로도 저 방법 이외는 eb 는 무적권 npm install --production을 실행시키더라고요 ㅠㅠㅠ

eb에서 알아서 npm install --production을 실행시키기 전에 node_modules 라는 이름의 빈 폴더를
만드는 방식으로 eb에서 npm install --production을 실행시키지 않도록 했습니다

prevent-npm.sh

#!/bin/bash
# 빈 node_modules 생성시 npm install --production 실행X
app="/var/app/staging/"
cd "${app}"
mkdir node_modules

2. [Nest 프로젝트 빌드 결과물을 배포전에 eb에 yarn berry 설정을 진행합니다]

yarn.sh

#!/bin/bash

# install node
curl --silent --location https://rpm.nodesource.com/setup_16.x | sudo bash -;

# install yarn
curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo;
yum -y install yarn;
yarn set version berry

# 환경변수를 변경했을때 아래의 설정을 하지 않으면 잘 동작하지 않을 수 있다고 합니다
mkdir -p .platform/confighooks/{prebuild,predeploy}
ln -s ../../hooks/predeploy/yarn.sh .platform/confighooks/predeploy/yarn.sh
ln -s ../../hooks/prebuild/prevent-npm.sh .platform/confighooks/prebuild/prevent-npm.sh

💦 [마주쳤던 어리둥절 1]

yarn berry zero install을 목표로 달려가고 있는데 node_modules가 존재하면 문제가 된다고 생각해서
빈 폴더인 node_modules를 삭제하는 쉘 스크립트(디버깅 포함)를 작성했는데...

del-nodemodules.sh

#!/bin/bash
ls -l
rm -rf node_modules
ls -l

이미 첫번째 ls -l 명령어를 실행했을때 이미 node_modules는 존재하지 않았습니다
혹시 선배님들 중에서 아시는 분이 있으시다면 알려주시면 감사하겠습니다

3. 배포 결과물을 실행합니다

AWS EB는 프로젝트 최상단 경로에서 app.js, server.js, Procfile을 찾고 실행시킵니다

Procfile

web: yarn run start:prod

package.json

{
  ...
  "scripts":{
    ...
	"start:prod": "cross-env NODE_ENV=production node dist/main",
	...
  }
  ...
}


🤟 github action 에 ci/cd 파이프라인 변경

.github
└── workflows
    └── cicd.yml

cicd.yml

name: cicd

on:
  push:
    branches: [ "main", "develop" ]

jobs:
  deploy:
    name: CD Pipeline
    runs-on: ubuntu-18.04

    strategy:
      matrix:
        node-version: ['16.x']
    steps:
      - uses: actions/checkout@v2
      
      - name: Run build
        run: |
          corepack yarn
          yarn set version berry
          yarn run build

      # Install AWS CLI 2
      - name: Install AWS CLI 2
        run: |
          curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
          unzip awscliv2.zip
          which aws
          sudo ./aws/install --bin-dir /usr/local/bin --install-dir /usr/local/aws-cli --update
      
      # Configure AWS credentials
      - 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 }}

      # Make ZIP file with source code
      # -x는 zip파일 생성 시에 해당 부분들을 제외한다.
      - name: Generate deployment package
        run: zip -r deploy.zip . -x '*.git*' './aws/*' './src/*' './node_modules/*' './test/*' awscliv2.zip

      # Deploy to Elastic Beanstalk
      # application_name과 environment_name을 꼭 확인하자!
      # 해당 부분은 꼭 같아야 한다!!
      - name: Deploy to EB Prod
        if: ${{ github.ref == 'refs/heads/main' }}
        uses: einaregilsson/beanstalk-deploy@v14
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          application_name: [운영시 어플리케이션 이름]
          environment_name: [운영시 환경 이름]
          region: ${{ secrets.AWS_REGION }}
          version_label: ${{github.SHA}}
          deployment_package: deploy.zip

      - name: Deploy to EB QA
        if: ${{ github.ref == 'refs/heads/develop' }}
        uses: einaregilsson/beanstalk-deploy@v14
        with:
          aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          application_name: [qa시 어플리케이션 이름]
          environment_name: [qa시 환경 이름]
          region: ${{ secrets.AWS_REGION }}
          version_label: ${{github.SHA}}
          deployment_package: deploy.zip

저는 QA와 운영 환경을 각각 사용하고 있는데 git branch 에 따라서 다른 환경에 배포하고 있습니다

Nestjs 를 사용하면서 부끄럽지만 테스트 코드를 작성하지 않고 있습니다
혼자 백앤드를 맡아서 사내 프로젝트의 mvp를 만들고 있고
nestjs & typeorm을 처음으로 실무에서 사용하고 있어서 조금 익숙해지고
mvp를 운영 배포 이후에 바로 테스트 코드 꼭!!! 꼭!!! 작성할 것입니다
(github action yml 파일에 ㅠㅠㅠ 테스트 부분이 없어도 양해부탁드립니다)



❗️느낀점

사실 처음 eb를 접하게 된 이유가 혼자 운영, 개발 등을 맡아서 프로젝트 하나를 완성해야 하는데...
러닝커브를 고려한 운영쪽의 공수를 최소한으로 하고 싶었고 aws의 멋진 엔지니어분들과 회의 끝에
elastic beanstalk 한 번 해보자!! 가 됐습니다

하지만 좀 더 세부적인 내용을 적용시키려하니 제가 아직 많이 모자라서 쉽지 않았습니다
특히 파이프라인을 설계하고 진행하는 내용은 아직 시작하기 전부터 부담감이 많이 느껴지는 것 같지만
해냈을 때의 이 성취감 때문에 절대 끊을 수 없죠 ㅋㅋㅋㅋ

앞으로도 늘 생각하고 고민하고 성장하는 프로그래머가 되기 위해서 노력 해야겠습니다

profile
세상에 선한 영향력을 끼치는 서비스를 개발하고 싶은 개발자입니다

0개의 댓글