Block Rockin’ Codes

back with another one of those block rockin' codes

Node.js の起動オプション、環境変数、npm start の話

Node は起動時に色々オプションをつけることができます。
面白いもの、有益なものあるんですが、あまり言及されてないので、
ちょっと紹介してみようかと思ってます。


最後の npm start の話は、それ単体で書いても良いかと思っていたんですが、
関連するし良い機会なので書きます。
そして、オプション周り興味がない方も、Node やってる方は最後の npm start の話だけでも、
読んでいただけるとと思ったりします。(知らない方が多いようなので)


ここで紹介している Node のバージョンは v0.7.7 です。しかし v0.6.x あたりでは、
v8 のバージョンが古く、オプションが微妙に違います。そこは v0.6.12 での結果を載せている場合もあります。

-h

まあ、とりあえず全ては -h から始まる。ということで、実行すると以下が出ます。

Usage: node [options] [ -e script | script.js ] [arguments] 
       node debug script.js [arguments] 

Options:
  -v, --version        print node's version
  -e, --eval script    evaluate script
  -p, --print          print result of --eval
  -i, --interactive    always enter the REPL even if stdin
                       does not appear to be a terminal
  --v8-options         print v8 command line options
  --vars               print various compiled-in variables
  --max-stack-size=val set max v8 stack size (bytes)

Environment variables:
NODE_PATH              ':'-separated list of directories
                       prefixed to the module search path.
NODE_MODULE_CONTEXTS   Set to 1 to load modules in their own
                       global contexts.
NODE_DISABLE_COLORS    Set to 1 to disable colors in the REPL

Documentation can be found at http://nodejs.org/

ここから面白いものを抜き出し、ここに無いものもあわせて紹介します。

debug

gdb ライクな debugger を起動できます。

$ node debug sample.js
< debugger listening on port 5858
connecting... ok
break in sample.js:1
  1 var http = require('http');
  2 
  3 debugger;
debug> help
Commands: run (r), cont (c), next (n), step (s), out (o), backtrace (bt), setBreakpoint (sb), clearBreakpoint (cb),
watch, unwatch, watchers, repl, restart, kill, list, scripts, breakOnException, breakpoints, version

help と入力するとコマンド一覧が出ます。
n, s, bt あたりを覚えておけばステップ実行によるデバッグができます。

--debug, --debug-brk

node 組み込みのオプションではない(と思う。。)ですが、
node-inspector を使う場合は、このオプションを付けます。

詳しくは node-inspector の解説に譲ります。

-e

渡した文字列をスクリプトとして実行できます。

$ node -e "console.log('hello world');"

http server

$ node -e "require('http').createServer(function(req, res) { res.end('hello world'); }).listen(3000);"

--max-stack-size=val

set max v8 stack size (bytes)

v8 のスタックの最大サイズを byte 単位で指定。

--v8-options

node というより v8 のオプションの一覧です。

$ node --v8-options

沢山のオプションが出てきます。


見ての通り、色々細かいオプションを指定できたりします。
また、 strict-mode や ES-harmony の機能など、
新しい機能を有効にするオプションもあります。


特に harmony は、ブラウザで使おうとすると互換性の問題がでてきますが、
サーバサイドであれば使いたい放題です。
これまでブラウザという制約上我慢していた方々も思う存分 harmony できますw


たくさんあるので、全部は紹介しません。
ここでは、いくつか知っておくとよいものだけを紹介します。

--strict_mode

allow strict mode directives

strict_mode を有効にします。

ECMA-262 » ECMA-262-5 in detail. Chapter 2. Strict Mode.

--harmony_typeof

enable harmony semantics for typeof

typeof null が "object" ではなく "null" になります。

harmony:typeof_null [ES Wiki]

--harmony_scoping

v0.6.x あたりまでは --harmony_block_scoping でした。

enable harmony block scoping

block scope を有効にします。
block scope については、下記あたりを参照。

harmony:block_scoped_bindings [ES Wiki]
ECMAScript 6th, Harmony と JS++ - hogehoge @teramako の 1.ブロックスコープ あたりです。


--harmony_modules

enable harmony modules (implies block scoping)

module を有効にします。
module については、下記あたりを参照。(正直あまり知らないです、すいません。)

harmony:modules [ES Wiki]

--harmony_proxies

enable harmony proxies

harmony proxie を有効にします。
proxie については、下記あたりを参照。

harmony:proxies [ES Wiki]
ES Harmony の Proxy について #fxdevcon で LT してきました - mooz deceives you

--harmony_collections

v0.6.x あたりまでは --harmony_weakmaps のみがありました、
v0.7.7 では set, map, weakmap がこのオプションですべて有効になります。

enable harmony collections (sets, maps, and weak maps))

harmony set, map, weakmap を有効にします。
これらについては、下記あたりを参照。

harmony:simple_maps_and_sets [ES Wiki]
harmony:weak_maps [ES Wiki]
Let's WeakMap - 枕を欹てて聴く

--harmony

v0.6.x あたりではありませんでした。
これをつけると harmony_typeof 以外の harmony オプションがすべて有効になります。

enable all harmony features (except typeof)

その他

v8-options の一覧に出るオプションのほとんどは、特殊なデバッグやプロファイルなどをしない場合、あまり使う機会は無いと思います。
また、正直「このオプション何ができるの?」と思っても、あまり情報が無い場合も多いです。自分もほとんどのものは使ったことがありません。
ただ、一度は grep なんかを駆使して色々見てみるといいと思います。


見ておきたいもの。

$ node --v8-options | grep log
$ node --v8-options | grep prof
$ node --v8-options | grep trace
$ node --v8-options | grep gc
$ node --v8-options | grep debugg
$ node --v8-options | grep size

なんかおもしろい or 有益なオプションがあったらぜひ教えてください。

環境変数

オプションとは違いますが、起動時にあわせて使います。
基本は、 xxx=yyy という変数をつけて実行すると、ランタイムからはprocess.env.xxx で yyy の値を文字列値として受け取れることを利用しています。
慣習的に NODE_XXX という変数が使われます。例えば独自変数 NODE_MY_FLG を指定したい場合。

$ NODE_MY_FLG=true node server.js

このように指定できます。


また環境変数には、慣習としてみんなが使っているものがあります。
以下に代表的なものを紹介しますので、覚えておくと便利だと思います。

NODE_ENV

実行環境を切り替えます。いわゆる production mode, development mode などの切り替えに使用します。

$ NODE_ENV=production node server.js

これを設定することで process.env.NODE_ENV が "production" になりますので、これを用いた実行モードの切替を行うことができます。
Express や Socket.IO などは、development, production の2つの場合は自動で確認して、モードを切り替えてくれます。

NODE_DEBUG

デバッグをする上で、知っておくと便利です。

Node の標準モージュールと、この作法に則った一部のモジュールは、このオプションにモジュール名を渡すと、デバッグを出力するようになります。
以下は、 http, net モジュールを指定して、作成した http サーバを起動してデバッグ出力を見ています。
複数指定するときの区切りはカンマです。

$ NODE_DEBUG=http,net server.js
NET: bind to 0.0.0.0
NET: onconnection
HTTP: SERVER new http connection
NET: onconnection
HTTP: SERVER new http connection
HTTP: server response shouldKeepAlive: true
HTTP: outgoing message end.
HTTP: server response shouldKeepAlive: true
HTTP: outgoing message end.

こんな感じです。

これは、 NODE_ENV と同様、 process.env.NODE_DEBUG を見て、デバッグの出力をトグルするコードが、モジュールの中に含まれているからです。

標準モジュールで対応しているのは以下。

$ grep NODE_DEBUG ./node-v0.7.7/lib/*
./node-v0.7.7/lib/cluster.js:if (process.env.NODE_DEBUG && /cluster/.test(process.env.NODE_DEBUG)) {
./node-v0.7.7/lib/http.js:if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) {
./node-v0.7.7/lib/module.js:if (process.env.NODE_DEBUG && /module/.test(process.env.NODE_DEBUG)) {
./node-v0.7.7/lib/net.js:if (process.env.NODE_DEBUG && /net/.test(process.env.NODE_DEBUG)) {
./node-v0.7.7/lib/timers.js:if (process.env.NODE_DEBUG && /timer/.test(process.env.NODE_DEBUG)) {
./node-v0.7.7/lib/tls.js:if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {||<

他にも、この変数をチェックしてデバッグ出力に対応したモジュールもちらほらあります。
自分でモジュールを公開している方は、ぜひ対応してみるといいと思います。

NODE_PATH

これは慣習じゃなくて、仕様です。
モジュールを PATH に追加します。
これにより、標準モジュール以外のモジュールも、相対パスを書かないで require できるようになります。


具体的にはこういうことです。

まず、こんなディレクトリ構成だったとします。

$ tree .
.
├── lib
│   └── my_util.js
├── models
│   ├── index.js
│   └── posts
│       └── index.js
└── server.js

3 directories, 4 files


通常、この構成だと、server.js と models/index.js 、 models/posts/index.js から lib/my_util.js を使おうとしたら、こうなります。

server.js

var my_util = require('lib/my_util.js');


models/index.js

var my_util = require('../lib/my_util.js');


models/posts/index.js

var my_util = require('../../lib/my_util.js');

このように各ファイルで共有したいものも、遠い階層からの参照は、指定が汚くなります。
ここで lib を NODE_PATH に加えると以下のようになります。

$ NODE_PATH=lib node server.js

この方法で実行した場合は、 lib は PATH を解決できるので、全てのスクリプト

var my_util = require('my_util');

で my_util.js を require できるようになります。

以前 nodejs_jp の ML でも少し議論になりましたが、
自分はこの、

「共有したいものはディレクトリにまとめて NODE_PATH に追加」

が割りとすっきりしてていいんじゃないかと思います。
(ここで、 NODE_PATH の指定方法はどうやってユーザに伝えるの?という疑問が出た方は、最後に書くのでもう少し待ってください)


オプションの指定方法

多くのプロジェクトは、例えば git clone してきたら

$ node server.js

などとすることで起動できる、と思ってていいのはオプションがない場合だということがわかったと思います。


もしかしたらこのプロジェクトは

$ NODE_ENV=production NODE_PATH=lib node --harmony_typeof --harmony

で起動する必要があるかもしれません。
まあ、この例では reademe とかに書いてあればいいかもしれませんが、
例えば git push でデプロイするような PaaS なんかに上げて、
自動で起動してもらう場合にこれらをどう指定するかも問題になります。


そこで使うのが npm start です。

npm start

npm には npm start というコマンドがあります。
これは、 package.json の scripts.start に指定された文字列を実行するものです。
npm は、現在 node 本体に入っているので、この起動方法は node の実行環境において妥当な方法と考えています。

"scripts": {
  "start": "NODE_ENV=production NODE_PATH=lib node --harmony_typeof --harmony"
}


ちなみに npm についてまとめてみる - need something more... によると、
start 以外に prestart, poststart も書ける模様。知らなかった。。


Joyent の no.de の環境では、 git push したら npm start が実行されるので、この方法が使えます。
プロジェクトを作成する側は、 package.json をきちんと書き、clone した人は npm start をまず叩く。
README.md に「起動は node server.js で」とか書くより合理的でしょう。


なにより、 Node に対応している各 PaaS 提供業者様におかれましては、
勝手な起動仕様を作るより npm start に対応されることを、おすすめ and/or お願いしたいなと個人的には思っております。


npm start はもっとデファクトになっていいと思うんですが、あまり知られてすらいない気がする。(というか npm がデカすぎるせいなのかも)

まとめ

長かったのでまとめます。

  • node -h
  • node --v8-options
  • NODE_PATH, NODE_DEBUG, NODE_ENV
  • npm start


こうした部分はドキュメント化されているものが少なかったりしますが、
知っていると有益かと思い、とりあえず書いてみました。
細かいところまでは書けていませんが、何かわかったり要望があれば追記します。
情報も募集します。よろしくおねがいします。