본문 바로가기

TIL(today i learned)

[230329] Docker로 nodeJS 서버 띄우기 (feat. npm code 127, bcrypt 에러)

도커를 이해하기 위해서 우선 진행 중인 개인 프로젝트 서버를 띄워보기로 했다.

nestJS로 작업 중인 간단한 API 서버를 도커 컨테이너로 띄워보기로 했다.

 

대략적인 플로우는 이렇다.

1. 앱서버 이미지를 빌드 (DockerFile, Dockerignore)

2. 빌드된 이미지를 사용해서 컨테이너 띄우기 (명령어 명령어...)

3. 앱서버 외에 DB서버도 컨테이너 등 여러 컨테이너 한 번에 관리하기 (Docker-compose.yml)

 

내 미니 프로젝트에서는 AWS RDS를 사용하고 있기 때문에, DB 컨테이너는 따로 필요가 없었다. (redis 서버도 보통 elasticache나 redis cloud를 사용한다...)

 

도커 컨테이너를 띄우며 만났던 질문들

1. RUN npm ci 했을 때, npm code 127 에러???

FROM node:lts-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "start"]

docker build를 시도했을  때 난 오류

  • 위의 Dockerfile에서는 alpine 버전의 node 이미지를 사용하여 빌드를 시도하였는데, alpine 버전에는 python이 포함되어 있지 않다. 그래서 C++로 만들어지고, python으로 래핑된어 있는 `bcrypt` 패키지를 읽어올 수 없는 것이었다!!
  • node.JS에는 bcrypt와 같은 C++ addon이 많이 있고, nodeJS에서 addon을 implement 할 수 있는 방법은 Node API, nan, 혹은 V8, libuv, Node.js library를 직접 사용하는 방식의 세가지가 있다고 한다. (하지만, 여기서 nan 방식은 어떤 건지 전혀 모르겠다...) 공식문서 참고
  • 여하튼, 그래서, 내 소스코드에서는 bcrypt의 사용을 node API를 활용하는 권장 방식으로 구현이 되어 있고, python으로 wrapping 되어 있는 addon을 빌드하기 위한 빌드 툴로 node-gyp를 사용한다. 그리고 node-gyp 또한 python이 설치된 상태여야하기 때문에, alpine 버전으로는 계속 에러가 나는 것이었다. ( bcrypt 모듈을 사용하기 위해서는 node-gyp가 필요하며, unstable한 버전의 ndoe에서 bcrypt를 소환하면 node-pre-gyp를 설치할 수 없다는 에러를 만나게 됨...! bcrypt 레포에 리포트 된 것 참고 )

node-gyp에 대한 node 공식문서 설명

 

 

node-pre-gyp에 대한 공식 문서 설명

이 npm code 127 error를 해결할 수 있는 방법은 세 가지가 있다.

1. node 이미지를 alpine 버전 말고 일반 버전으로 사용 (alpine 워딩만 삭제하면 됨)

FROM node:lts

2. alpine 버전을 사용하고, python만 추가 설치 (RUN script 한 줄 추가)

Run apk --no-cache add --virtual builds-deps build-base python

3. 소스 코드에서 bcrypt 패키지를 bcryptjs 모듈로 교체

bcrypt가 bcrypt.js에 비해 약 30% 빠르다고 한다. 하지만, 브라우저 환경에서는 C++ addon을 사용할 수 없으므로 bcrypt.js를 사용해야만한다고 한다. 공식문서

 

메모리 절약을 위해서는 2번 python을 설치하는 방식이 가장 효율이 높다고 한다. 하지만, 귀찮으면 그냥 1번 alpine 옵션을 떼고 일반 node 이미지로 빌드하면 된다. (나는 그렇게 했음..ㅋㅋ)

 

 

docker에서 bcrypt 사용하기

Docker에서 왜 bcrypt가 에러날까?

velog.io

 

 

2. 빌드된 이미지 파일 업데이트???

이미지 파일의 tag명을 동일하게 빌드하면 기존 이미지의 "Name" 컬럼이 자동으로 <none>으로 변경되고, 새로 생성된 이미지 tag에는 latest로 업데이트 된다. 이후 해당 이미지명으로 컨테이너를 실행시키면 자동으로 lts 이미지 버전으로 실행시켜준다.

하지만 컨테이너의 경우, 포트를 사용하기 때문에, 같은 포트번호로 컨테이너를 띄우려면 기존 컨테이너를 중지시키고 최신 버전의 이미지로 컨테이너를 띄워야 하는데, 이럴경우, 무중단 배포를 하기 위해서 여러개의 포트를 열어두고 하나씩 돌아가며 교체해주면 될 것 같다.