目的
pm2のclusterモードは便利ですが、リクエストのロードバランスはNginxなどで行いたい場合があります。
pm2のforkモードでCPUの数に応じて動的にサーバプロセスを立ちあげる方法について調べました。
clusterモードでは
pm2のclusterモード(Node.jsのclusterモジュール)はportを割り当てられるのはマスタープロセスだけなので、
以下の用にinstances=0としてマスタープロセス用の設定を書けば、
portを割り当てられたマスタープロセス1個と、実際にリクエストを処理するワーカープロセスがCPU数起動します。
{
"name": "pm2sample",
"script": "server.js",
"exec_mode": "cluster_mode",
"instances": 0
}
var http = require('http');
var port = 8080;
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end("PID:" + process.pid + ' pm2sample app.\n');
}).listen(port);
$ pm2 start process.json
forkモードの問題点
単純にexec_mode=forkと修正して起動すると、同じportを割り当てられたサーバプロセスを複数起動しようとして最初のプロセス以外起動しません。
{
"name": "pm2sample",
"script": "server.js",
"exec_mode": "fork",
"instances": 0
}
pm2のログを見てみると最初のプロセスは起動できていますが、それ以外のプロセスはエラーが発生しています。
events.js:85
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE
at exports._errnoException (util.js:746:11)
at Server._listen2 (net.js:1156:14)
at listen (net.js:1182:10)
at Server.listen (net.js:1267:5)
at Object.<anonymous> (/Users/duck1218/workspace/github.com/geekduck/pm2sample/server.js:8:4)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
〜省略〜
設定ファイルに個別に全てのプロセス用の設定を書いても良いですが、
本番環境と開発環境でCPUの数が違う場合などで問題が起きます。
解決方法
以下のissueに書いてあるとおり、環境変数NODE_APP_INSTANCEを使って割り当てるportを動的に決定しましょう。
NODE_APP_INSTANCEは0からはじまる連番が渡されます。
Pass instance id to app as environment variable
server.jsを修正してNODE_APP_INSTANCEを使用してportを設定します。
var http = require('http');
var port = 8080 + parseInt(process.env.NODE_APP_INSTANCE || 0); // CPUが4個の場合port8080〜8083のサーバが起動する
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end("PID:" + process.pid + ' pm2sample app.\n');
}).listen(port);
CPUが4つの環境ではlocalhost:8080, localhost:8081, localhost:8082, localhost:8083でサーバが起動します。
あとはNginxでロードバランスしてあげれば完璧です!
upstream io_nodes {
ip_hash;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
server 127.0.0.1:8082;
}