◆ PM2 はメンテ不足で issues で放置されてるバグ多め
◆ 古いけど forever 使うことにした
◆ OS 起動時に自動起動してくれるようなサービス機能は無し
  ◆ 別パッケージの forever-service は systemd 対応してない

forever 使うことにした

Node.js のプロセスをデーモン化するツールは前は PM2 を使いました
PM2 のほうが新しくて高機能ですし 公式サイトも綺麗です
http://pm2.keymetrics.io/
https://pm2.io/runtime/
今更 Node.js 初期の頃の forever なんて使わなくていいでしょ ということで PM2 を選びました

ですが 使ってみると PM2 は機能は多いもののバグもありますし なにより最近はあまりメンテされてないようです
Issues や PR は多いのにファイル一覧の最終更新をみると README とかを除けば数ヶ月前や 1 年以上前の更新ばかりです
以前バグを報告したこともありますけど 完全に無視されてコメント 0 状態でした
Issues は多いですが他もそういうのばかりで積極的には対応してないみたいです
普通の OSS なら管理してる人も忙しいしとわかるのですが PM2 は有料プランもあって企業でやってるプロダクトだったと思います
それなのにこれってどうなんだろうと思います

そんな感じであまり期待できないですし けっこう困るバグだったので自分でコード修正したりしましたが 規模が大きい分複雑でわかりづらかったです
とりあえず目的の動きはできるようにしたものの他がどうなってるか 自分が使ってない部分への影響はわからないくらいです
そんななので いっそのこと別のツールにしようと思って探してみたのですが 競合ツールはこれといって見つからずやっぱり forever くらいでした

forever も結構古い分最近はあまり更新されてませんし 公式サイトもなくてドキュメントもそこまで揃ってないです
一応最近 1.0.0 がリリースされたのですが 大きく変わったというわけではなく breaking change があったからメジャーバージョンを上げただけみたいです
それも追加機能というよりは修正で .forever フォルダが作れなかったらエラーが伝わらず終了してたのを修正したというものみたいです
https://github.com/foreversd/forever/issues/415

一応こういう修正はあるわけですし 昔からある分バグは少ないと言えるのかなと forever を使うことにしました
PM2 と同じようなものという可能性も 0 ではないですけど
ここからは PM2 との比較を交えながら forever の機能とか使っていて気づいたことの紹介です

プロセス管理方法

PM2 も forever もやることは プロセスを起動してエラーが起きてプロセスが終了したら再起動するというものなので 使い方も基本的なものは似ています
start コマンドに JavaScript ファイルのパスを渡すだけです

プロセスを監視する方法は forever と PM2 で異なっていました
PM2 では God Daemon というプロセスが常駐します
list コマンドでプロセス一覧の表示などであっても なにかの PM2 コマンド実行時に God Daemon がいなければ起動されます
起動するとずっと常駐して PM2 で起動したすべてのプロセス監視します
God Daemon 自体は PM2 の kill コマンドで止めることができます
実行するユーザが違えば God Daemon が複数いることもあります

forever の場合はプロセスごとに monitor プロセスが起動します

# ps aux | grep node
root 15457 1.0 2.5 572244 25496 ? Ssl 18:47 0:00 /usr/bin/node /opt/test08/node_modules/forever/bin/monitor app.js
root 15477 0.0 1.3 494180 13448 ? Sl 18:47 0:00 /usr/bin/node /opt/test08/app.js 1
root 15488 1.4 2.3 572280 23388 ? Ssl 18:47 0:00 /usr/bin/node /opt/test08/node_modules/forever/bin/monitor app.js
root 15508 0.1 1.5 494180 15484 ? Sl 18:48 0:00 /usr/bin/node /opt/test08/app.js 2
root 15522 0.0 0.0 112724 988 pts/0 R+ 18:48 0:00 grep --color=auto node

forever で起動するプロセスが多くなれば monitor のプロセスも増えます
しかし forever を使ったプロセスを止めると monitor も止まるのですべて止めると完全に forever 関係のプロセスがなくなります
PM2 みたいに God Daemon が常駐しません

起動方法

start コマンドを使うとデーモンとして monitor プロセスを起動して forever コマンドのプロセスはすぐに終わります
start コマンドを使わず直接スクリプトだけ指定するとデーモンなしで実行して forever コマンドは終わらず標準出力にログを出力します

デーモン化しない場合は lib/forever/cli.js の cli.run が実行されます
https://github.com/foreversd/forever/blob/1.0.0/lib/forever/cli.js#L610
中では tryStart → forever.start → forever.startServer が実行されます

デーモン化する場合はこっちです
https://github.com/foreversd/forever/blob/1.0.0/lib/forever/cli.js#L312
tryStart → forever.startDaemon が実行されます
startDaemon では bin/monitor の JavaScript ファイルを新しいプロセスとして起動します
ipc 通信でオプションを送信して コマンドで実行した forever プロセスは終了します

bin/monitor では forever.Monitor のインスタンスを作って start メソッドを実行しています
forever.Monitor は forever-monitor という別パッケージになってるものです
start イベントを受け取ると forever.startServer を実行します

デーモン化しない場合も forever.start 内で forever.Monitor の start が実行されて startServer は start イベントを受けてからなので monitor プロセスで実行するかどうかの違いで同じ処理になってます

forever.startServer では monitor に対して forever.Worker を作って start しています
https://github.com/foreversd/forever/blob/1.0.0/lib/forever/worker.js

monitor では実際に実行するスクリプトを起動しています
worker では socket を使ったサーバを起動して受け取ったメッセージごとに処理を行ってます
使われてるのは nssocket というパッケージで これも forever の管理してるものです
用途は forever コマンドで起動中のサーバに対して stop や restart の命令を送って それを処理するものです

だいたいこんな感じで少し複雑でしたがなんとなく全体がわかりました

config

forever ではできる設定は少なめです
ソースを見てるともうちょっと多めだと思ったのですが オプションはあるのに cli オプションで受け取るオプションを制限してるので使えないのもありました
JavaScript から使えば使えるかも?
ただそういうのは README の説明には書いてないので使わないほうがいいのかもしれません

forever には起動するプロセス自体の設定と全体設定があり 全体設定は実行したユーザのホームの .forever フォルダの config.json に保存されます
全体設定は 「forever set KEY VALUE」 設定できますが値が true でも文字列になるなどイマイチです
ただそれを前提に === "true" が条件にされていて直接 JSON に true と設定しても有効にならない設定もあります
forever コマンドを実行するときの環境変数 FOREVER_ROOT で .forever のパスは変えられます
変わると参照する config.json も変わり保持している PID や sock ファイルのリストも変わるので PM2 の別ユーザで God Daemon 起動みたいな扱いになります
JSON ファイルなので __dirname とか使えず少し不便です

個別プロセスの設定は start コマンドなどでコマンドラインオプションで設定できるものです
起動するスクリプトに .json ファイルを指定すればそこに書かれた設定で起動できます
こっちも JSON なので __dirname とか使えず不便です
.js だと実行対象なのか設定対象なのかわからないというデメリットはありますが PM2 みたいに .config.js を拡張子とするとかしてくれると良いんですけどね

ログは 3 種類あり -l, -o, -e で設定できますが基本 -l だけで良さそうです
-o は stdout で -e は stderr を保存します
-l はそれ以外の forever システムのログかなと思ったら stdout と stderr も含んでいます
別々に分けたいと言うならともかく単に出力を指定の場所に保存したいというのなら -l だけの指定で十分でした
また -l は指定しなくても自動で名前がつけられて出力されますが -o と -e は指定しないと出力されません

起動するときには -a の append にしないとすでに同名のログがある場合に起動できないです
それなら自動で追記モードにすればいいのにと思います

sourceDir はスクリプトの相対パスの基準になる場所を指定できるものです
わざわざ分けなくてもスクリプトに直接パスを書けばいいじゃん と思って sourceDir は使わずスクリプトに直接絶対パスを書いてました
ですが コードを見てみると watch オプションを使って watchDirectory を指定しない場合のデフォルトとして使われてました
watch するなら sourceDir に指定しておいたほうが watchDirectory の指定不要で楽そうです

サービス

forever はあくまでデーモン化するだけで OS 起動時に自動起動はしてくれません
PM2 だと systemd の登録機能がありましたが forever ではそういう機能はないです

なにか無いかなと探してみると forever-service というのがありました
サービス登録とログのローテートをしてくれるようです
使ってみたのですが作られた設定ファイルは init.d でした
systemd はサポートしてないそうです

systemd にしたかったので結局自分でユニットファイルを作りました
起動タイミングなどは PM2 と同じで良いので PM2 のをテンプレートに変更すると簡単です
https://github.com/Unitech/pm2/blob/3.5.1/lib/templates/init-scripts/systemd.tpl

ExecStart や ExecStop を書き換えます
PM2_HOME の Environment は要らないので消して Description などもこのままだとわかりづらいので変更します
PIDFile ですが forever のモニタプロセスが PID を書き込むので PID の取得はできます
場所は .forever の pids に置かれてオプションで名前の指定もできます
ただ 特に指定しなくても status でみると自動でフォークされた monitor プロセスの PID を取得できてました

forever では PM2 と違って God Daemon に当たるものがいないので systemd の登録は forever としてひとつ登録するのではなく forever start で起動するスクリプトごとに設定する必要があると思います
それが嫌ならまとめて forever の起動と停止をするスクリプトを用意して ExecStart と ExecStop に設定すれば一応できそうではありますが その場合 systemd がサービスの状態を調べるときにどのプロセスを参照すればいいのかわからない問題が起きそうです
対処しようとすると自分で PM2 の God Daemon にあたるものを作ることになって大変そうなので ユニットファイルを複数作るのが無難だと思います

結局どっちがいいの?

forever は数時間で必要そうなところは一通りソースに目を通せるレベルなので 困ったときの対処も比較的楽そうです
機能はそこまで多くないですが systemd ファイルを自分で作ればあとは別に困るほどでもないです

今のところ 基本は forever で forever だとできなくて PM2 でできそうなことが必要になったら PM2 にすれば良いかなと思ってます