Skip to content

Kyunghwa Yoo

Jenkins와 AWS Beanstalk 을 이용한 NodeJS 배포 삽질기

aws, deploy2 min read

Jenkins 와 AWS Elastic Beanstalk 를 이용해서 NodeJS 서버를 자동으로 배포하는 기능을 회사 프로젝트에 넣었다. Jenkins 는 전 직장에서도 사용했기 때문에 부담감이 덜 했지만 Beanstalk 은 처음 접해보는 거라 긴장감이 있었다. 2일동안 작업을 하면서 겪었던 삽질들을 정리해보고자 한다.

개념정리 부터...

Jenkins 란?

  • 로컬 환경에서 프로젝트를 개발하면서 변경된 사항을 자동으로 리모트 환경 (ex. 실서버) 에 반영해주는 것을 배포 라고 한다.
  • 프로젝트마다 배포하는 패턴이 정해져 있고 변경사항이 발생했을 때 그 패턴대로 대신 배포해주는 행위를 CI (continuous integration) 라고 한다.
  • Jenkins 는 CI 툴 소프트웨어 중 하나다.

Elastic Beanstalk 이란?

  • Beanstalk 는 웹 서비스를 배포해주고 로드밸런싱, 오토스케일링 등을 편리하게 해주는 서비스다. (공식 설명)
  • 비슷한 서비스인 CodeDeploy는 배포하는데 초점이 맞춰져있고 배포 코드를 짜야되지만, Beanstalk 은 Zip/War 파일을 통해 배포하며 로드밸런싱, 오토 스케일링 등의 역할도 함께 해주는 패키지 서비스다. (Raddit 자료)
  • Jenkins와 차이점은 Jenkins 는 프로젝트에 변경이 있을 때 배포 동작을 수행하는 것이고, Beanstalk은 그 배포 동작을 통해 실제 서버에 반영하는 것이다.

배포 FLOW

  1. Github repository 의 master 브랜치에서 remote push 가 발생하면 미리 설정된 Webhook 을 Jenkins 서버에 날린다.
  2. Webhook 을 받은 Jenkins 서버는 해당 repository 에 연결된 Job item (배포동작이 등록된 명령어 모음집) 을 실행시킨다.
  3. Jenkins 서버에서 소스코드 변경사항을 업데이트 한다.
  4. Jenkins job item 에서 미리 정의된 빌드 스크립트를 통해 Beanstalk 에서 사용할 배포파일(Zip/War) 을 만든다.
  5. Jenkins plugin 을 이용해서 S3 bucket 에 배포파일을 전송한다.
  6. S3 bucket에 올라온 배포파일(Zip/War)을 이용해서 Beanstalk 가 배포작업을 수행한다.

여기서 Jenkins plugin 은 배포파일을 만들어주고 (직접 만들어도 된다), 그 파일을 S3 bucket에 올려주고 Beanstalk에서 배포하는데 사용할 수 있게 해 준다. 그래서 Beanstalk Environment 를 자알 생성하고 Jenkins Build Step 을 자알 설정하면 알.아.서. Beanstalk 이 배포에 성공한다. (사실 이부분이 삽질기의 핵심이다.)

Build Step 1. 빌드 스크립트

1yarn install // 빌드하기 위한 모듈 설치
2yarn run build // 프로덕션 번들 빌드
3
4rm -rf node_modules && yarn install --production // 프로덕션용 모듈 설치
5
6zip -rq bundle.zip app.js node_modules/ // 배포용 압축파일을 만든다.

Build Step 2. Deploy into AWS Elastic Beanstalk

  • 배포파일을 지정하고, 파일을 업로드할 S3 bucket을 지정하고, 배포를 수행할 Beanstalk 환경을 지정한다.
  • Root Object 값에 zip파일을 지정해주면 플러그인이 S3에 업로드해준다.

삽질 1. Beanstalk Environment 만들기

공식 문서 를 보고 시작했다.

새 환경 생성 페이지에서 도메인은 필수값이다. 배포된 웹 서비스를 확인할 수 있는 URL을 가리킨다. 근데 입력폼에서 필수값 표시도 안되고 별 설명도 없어서 그냥 넘겨보니 넘어가진다. 10~20분 정도를 기다리다 보면 생성에 실패한다. 꼭 입력하자.

기본 구성만으로 빌드 단계까지는 넘어가도 생성이 안될 수 있다. 회사의 경우 보안때문에 필수적으로 네트워크 구성을 달리 해주어야 한다. 이를 생각하지 못하고 계속 기본값으로 생성을 시도했고, Beanstalk log 에서는 [재시도->실패] 를 30분정도 반복하다 죽어버린다. 무엇이 문제인지 얘기해주지 않는다. 정상적으로 동작하면 3~5분만에 생성이 완료되니 그 이상 걸릴 경우 환경 구성에 문제가 있는 것이다.

삽질 2. Beanstalk 자체 npm install 기능

배포파일에 package.json 파일이 있으면 Beanstalk 은 자체적으로 npm install 을 수행한다. 설치되는 위치는 배포 파일들이 생성되는 /var/app/current/ 다. 그런데 만약 npm install 을 하던 중에 실패하면 npm install 자체가 실패한다.

나의 경우, firebase 모듈의 firestore > grpc > node-pre-gyp 가 node v8.11 과 호환되지 않는다는 에러를 뿜었다. 그러면서 node-pre-gyp install --fallback-to-build 명령어를 수행하는데 이 때 권한 문제(Permission Denied)에 부딫혀 npm install 이 실패했다. firebase가 firestore 만 덜어낼 수 없는 구조였기 때문에 npm install 을 성공시킬 방법이 없었다. package.json dependency 에서 따로 exclude 하는 방법도 없고...

결국 node_modules 을 번들파일과 함께 압축해서 S3에 업로드하는 방식을 선택했다. 그리고 기본적으로 npm install 이 실행되는 것을 .ebextensions 폴더의 config 파일을 생성해서 막았다. 그렇기 때문에 S3에 업로드되는 배포파일의 용량이 커진다는 문제점이 남아 있다.

(공식문서에는 없는) Beanstalk 주요 파일 위치

  • 배포 파일들: /var/app/current/
  • 로그 파일들: /var/log/
  • 배포될 때마다 실행되는 코드:
    • /opt/elasticbeanstalk/hooks/appdeploy/pre/50npm.sh
    • /opt/elasticbeanstalk/containerfiles/ebnode.py
  • node 명령어, npm 명령어: /opt/elasticbeanstalk/node-install/{노드 버전}/bin/
  • 배포가 실패했을 때 배포 중이던 디렉토리: /tmp/deployment/application (배포성공하면 삭제된다)
  • nginx conf: /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf
© 2020 by Kyunghwa Yoo.