NodeJS / Process management

nodejs app을 단일 node 명령으로 실행후 백그라운드 혹은 시스템 데몬으로 다룰 수 있도록 해주는 프로세스 관리 유틸리티를 알아보자:

  1. nodemon
  2. pm2
  3. forever

1. nodemon

1
nodemon ./server.js localhost 8080

express app

nodemon으로 express 의 기본 템플릿을 시작시 bin/www 를 실행애 주어야 한다. app.js 만 실행시 www 에 명시한 포트가 인식 안된다.

1
$ nodemon bin/www

혹은 PORT 변수를 입력하고 실행한다.

1
$ PORT=3000 DB_USERNAME='mongo' DB_PASSWORD='' DB_SERVER='localhost' DATABASE='student' nodemon server.js

2. pm2 설치

pm2는 nodejs app을 실행하고 관리할 수 있는 전문적인 도구이다.
node.js 앱을 시스템 서비스로 등록하기 위해서 pm2 를 설치한다.

1
npm i -g pm2

예를 들어 express 앱이 있으면 다음 같이 pm2로 시작한다.

1
2
cd www-app
pm2 start -n "www-app" bin/www

pm2 log format

pm2 이전 버전은 시작시 timestamp를 로그에 저장하고 싶으면 시작시 --log-date-format 옵션을 이용

1
pm2 start app.js --log-date-format 'DD-MM HH:mm:ss.SSS'

시작 설정 파일에 log_date_format 옵션을 줄 수 있다.

1
"log_date_format"   : "DD-MM HH:mm:ss.SSS",

pm2 2.x 이후 부터는 log 의 형식을 --format 옵션을 지정할 수 있다.

1
pm2 logs --format

pm2 startup

startup 시 pm2 start 로 생성되는 .pm2 디렉토리의 pid 와 app.js 파일을 실행해 준다.

pm2 startup systemd 로 스타트를 하면 2개의 프로세스가 만들어 진다.

방법은,,,

  1. 먼저 앱을 시작해 둔다.
1
pm2 start -n "www-app" bin/www
  1. dump를 생성한다.

pm2로 현재 실행중인 프로세스 정보를 save로 덤프하게 저장한다. systemd 서비스 스크립을 작성하는데 유용하다.

1
pm2 save
  1. pm2 startup 명령

pm2 startup 명령은 pm2로 실행중인 프로세스를 systemd 서비스 유니트 파일로 제어 할 수 있다. 명령을 실행하면 sudo 명령으로 실행할 수 있는 스크립을 출력해 준다.

1
2
3
$ pm2 startup systemd
...
sudo env PATH=$PATH:/home/foo/.nvm/versions/node/v8.8.1/bin /home/foo/.nvm/versions/node/v8.8.1/lib/node_modules/pm2/bin/pm2 startup systemd -u foo --hp /home/foo

이 스크립을 실행해 주면 pm2-foo.service 서비스 유니 파일이 생성된다.

1
2
Target path
/etc/systemd/system/pm2-foo.service

이 서비스 파일을 활성화하고 시작해준다.

1
systemctl enable pm2-foo

이제 시스템을 재시작해도 pm2 로 실행중인 프로세스는 자동으로 시작된다.

3. forever

https://github.com/foreverjs/forever

백그라운드 로그 출력

nodejs app을 백그라운드로 실행하기 위해서 로그 정보를 생성할 수 있다.

The forever process will continue to run outputting log messages to the console.

1
forever -o out.log -e err.log my-script.js

Daemon으로 실행하기

forever 프로세스는 대상 프로세스를 백그라운드로 실행할 수 있는 데몬으로 동작이 가능하다. 이것은 nohup 같은 시스템 도구 없이도 가능다. 데몬 실행을 위해 -o -l, & -e 를 사용하도록 권장한다.

1
2
$ forever start -l forever.log -o out.log -e err.log my-daemon.js
forever stop my-daemon.js

그리고 -m 옵션으로 재시작 횟수를 지정할 수 있다.

here are several examples designed to test the fault tolerance of forever. Here’s a simple usage example:

1
$ forever -m 5 examples/error-on-timer.js

설정파일 지원

forever에 스크립트 경로와 함께 JSON 설정 파일을 전달해 스크립트의 구성요소를 제공할 수 있다. 예를 들어 아래 같은 앱 디렉토리에 developement.json 설정 파일이 있다고 하자,

1
2
3
4
.
├── forever
│ └── development.json
└── index.js

developement.json 설정 파일은 아래 같은 내용으로 구성할 수 있다:

1
2
3
4
5
6
7
8
9
// forever/development.json
{
// Comments are supported
"uid": "app",
"append": true,
"watch": true,
"script": "index.js",
"sourceDir": "/home/myuser/app"
}

NodeJS - Patterns

원문 https://darrenderidder.github.io/talks/ModulePatterns/ 요약

Node.js Module Patterns

이것은 같단한 모듈을 hello-module.js 소스에 선언한 것이다.

1
2
// hello-module.js
console.log("Hello World");

선언된 소스는 require() 로 들여올 수 있다:

1
require("hello-module.js");

Define a global

모듈 foo.js 를 글로벌

1
2
3
4
// foo.js
foo = function () {
console.log("Im foo");
};

모듈 foo.js를 들여와 전역에 선언된 함수 foo()를 사용한다.

1
2
require("foo.js");
foo();

그러나 글로벌 영역을 오염 시키지 않는다.

export an anonymous function

module 객체에 export 한다.

1
2
3
4
// bar.js
module.exports = function () {
console.log("Im bar");
};

모듈 bar.js를 들여와 전역에 선언된 함수 foo()를 사용한다.

1
2
var bar = require("./bar.js");
bar();

export a named function

module 객체의 이름 속성으로 export 한다.

1
2
3
4
// bar.js
module.fiz = function () {
console.log("fiz");
};

모듈 bar.js를 들여와 전역에 선언된 함수 foo()를 사용한다.

1
2
var fiz = require("./fiz.js").fiz;
fiz();

export an anonymous object

1
2
3
4
5
6
7
// buz.js
var Buz = function() {};

Buz.prototype.log = function() {
console.log('buz');
}
export.modules = new Buz();

모듈 buz.js를 들여와 객체의 메서드를 호출한다.

1
2
var buz = require("./buz");
buz.log();

export a named object

1
2
3
4
5
6
7
// buz.js
var Baz = function() {};

Baz.prototype.log = function() {
console.log('baz');
}
export.Baz = new Baz();

모듈 baz.js를 들여와 속성 메서드를 호출한다.

1
2
var baz = require("./baz.js").Baz;
baz.log();

export an anonymous prototype

module.exports 에 객체를 노출한다.

1
2
3
4
5
6
7
// doo.js
var Doo = function() {};

Doo.prototype.log = function() {
console.log('doo!');
}
export.exports = new Doo();

모듈 doo.js를 들여와 객체의 메서드를 호출한다.

1
2
3
var Doo = require("./doo.js");
var doo = new Doo();
doo.log();

export a named prototype

module 에 이름 속성을 노출한다.

1
2
3
4
5
6
7
// qux.js
var Qux = function() {};

Qux.prototype.log = function() {
console.log('qux!');
}
export.Qux = Qux;

모듈 qux.js를 들여와 객체의 메서드를 호출한다.

1
2
3
var Qux = require("./qux.js").Qux;
var qux = new Qux();
qux.log();

module.exports

modules.exports는 exports로 가명을 가지고 있다. 이름 있는 속성을 사용할 때 아래 같이 선언한다.

1
2
3
4
> module.exports.fiz = 'Fiz';
> exports.buz = 'buz';
> module.exports === exports;
true

exports 에 직접 대입하면 exports alias를 덮어 쓰게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> module.exports.qux = "qux";
> exports
{ qux: "qux" }
> exports === module.exports
true
> exports = "wobble wibble wubble!";
> exports === module.exports
false
> exports
"wobble wibble wubble!"
> module.exports
{ qux: "qux" }
// module.exports is canonical

pros, cons

  • Named exports: 한 모듈에 여러 개체 및 속성을 노출할 수 있다.
  • Anonymouse exports: 간단한 클라이언트 인터페이스로 적합