NodeJS

Dockerized Node.js for development

iKay 2019. 12. 29. 01:16
반응형

도커라이징(dockerize)의 장점 중 하나는 개발/배포시 환경에 신경을 덜 쓰면서 앱 개발에만 신경쓸 수 있다는 점이라고 생각한다. 윈도우 랩톱에서 개발된 웹 어플리케이션이 리눅스 서버에서 정상적으로 동작한다는 것을 장담하기가 매우 어렵다. 물론 docker가 윈도우나 Mac 상에서 리눅스 가상머신을 사용하기 때문에 리눅스와 100% 동일한 환경에서 동작할 것이라고 장담하기는 어렵지만, 그래도 전자의 경우보다는 정상적으로 동작활 확률이 더 높다고 본다.

 

Node.js 앱을 개발할 때도 배포환경과 유사하게 도커라이징하면, 개발/배포 환경 간의 간극을 줄일 수 있지 않을까 고민이 들어서 오늘은 개발 환경을 위한 Node.js 도커라이징 방법을 소개한다.  

 

Node.js 앱

프로젝트 디렉토리를 적당히 하나 생성한 후, 프로젝트 디렉토리에 `package.json`을 만든다. `express`를 이용한 웹 앱을 만들어 볼 것이고, `pm2`로 개발환경에서 웹 앱을 watch, restart 등을 하기 위해 설치한다.

 

pm2 옵션을 좀 더 살펴보자. 

 

--restart-delay: 웹 애플리케이션이 어떤 이유에 의해 종료된 후 재시작하게되는 delay로 ms 단위이다. 

--watch: 소스 코드 파일의 변화를 감지한다. 

--ignore-watch: 특정 디렉토리의 변화는 감지하지 않는다.  

--no-daemon: 애플리케이션을 실행할 때 attach 해서 log가 출력되게 한다. pm2는 기본적으로 detach 된다.

 

nodemon이 아니라 pm2를 사용한 이유는 pm2가 더 기능이 많아서인데, pm2가 nodemon보다 무겁기 때문에 pm2를 사용하는 것이 반드시 좋다고 할 수는 없다. 많은 기능을 사용할 필요가 없다면 nodemon을 사용해도 충분할 것 같다. 

{
  "name": "web_app_on_docker",
  "version": "1.0.0",
  "description": "Node.js development on Docker",
  "author": "First Last <first.last@example.com>",
  "main": "src/server.js",
  "scripts": {
    "start": "pm2 start --restart-delay=5000 --watch --ignore-watch=\"node_modules\" --no-daemon node -- src/server.js"
  },
  "dependencies": {
    "express": "^4.17.1"
  },
  "devDependencies": {
    "pm2": "^4.2.1"
  }
}

 

그 후, 터미널에서 `npm install` 를 입력해서 express를 설치한다. 

 

$ npm install                 

 

`~/src/server.js`에 서버를 실행하는 소스코드를 작성한다. 

 

const express = require('express');

const PORT = 8080;
const HOST = '0.0.0.0';

const app = express();
app.get('/', (req, res) => {
  res.send('Hello world\n');
});

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

 

작성 후, 터미널에서 `npm run start`를 입력하면 pm2가 실행되고 웹 브라우저에서 `http://localhost:8080`로 접속하면 "Hello world"가 출력되는 것을 확인할 수 있다. 

 

Dockerfile

프로젝트 디렉토리에 `Dockerfile`을 만들고 아래와 같이 작성하자. 

 

FROM node:12

# 앱 디렉터리 생성
WORKDIR /usr/app

# 앱 의존성 설치
# 가능한 경우(npm@5+) package.json과 package-lock.json을 모두 복사하기 위해 와일드카드를 사용
COPY package*.json ./

RUN npm install
# 프로덕션을 위한 코드를 빌드하는 경우

# 앱 소스 추가
COPY . .

EXPOSE 8080
CMD [ "npm", "run", "start" ]

 

`dockerignore`도 만들어 `node_modules`가 이미지를 빌드할 때 추가되지 않도록 하자.

node_modules

 

Build & Run Docker image

`package.json`을 열어 docker-build, docker-run을 하는 스크립트를 추가한다. 

 

  docker-build: image를 빌드한다.

  docker-run: docker image를 실행한다. 

    -v: 현재 소스 디렉토리와 컨테이너 안의 소스 디렉토리를 연결해서 파일 수정시 변화를 감지하도록 한다. 

    --rm: 컨테이너 종료시 삭제되도록 한다.

    -p: 8080:8080 컨테이너 밖과 안을 모두 8080으로 연결한다.  

 

{
  ...
  "scripts": {
    "start": "pm2 start --name web_app --restart-delay=5000 --watch --ignore-watch=\"node_modules\" --no-daemon node -- src/server.js",
    "docker-build": "docker build -t ikay/web-app .",
    "docker-run": "npm run docker-build && docker run -v $(pwd):/usr/app --rm -p 8080:8080 ikay/web-app"
  },
  ..
}

 

`npm run docker-run`을 실행시키면 도커 이미지가 빌드되고 애플리케이션이 실행된다. 

 

애플리케이션이 실행된채로 소스코드, `src/server.js`를 수정해보자. 

const express = require('express');

const PORT = 8080;
const HOST = '0.0.0.0';

const app = express();
app.get('/', (req, res) => {
  // 소스 코드 수정
  res.send('Hello world, 안녕 세계!\n');
});

app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

 

실시간으로 변경된다. 

 

 

반응형