Nodejsアプリを自前サーバでサービス化する

# この記事の内容

* Nodejsアプリをどうデプロイしようか
* pm2というマネージャ
* サービス化

## どうサービスに乗せるのか

Nodejsを基盤とするWebアプリケーションはその守備範囲の広さから、アプリそのものが直接リクエストを受け付けることができます。
Javaや.NETではアプリケーションサーバ(GlassfishやIIS)がクライアントからのレスポンスを受け付けて、そこからレスポンスをアプリケーションに投げるわけですが、一方でNodejs Webアプリケーションはクライアントからのリクエストを直接受け取り、それを処理します。

Javaや.NETにおいてはアプリケーションサーバという階層があり、サービスとしての起動などはこの階層でよしなにやってくれます。
しかし、Nodejsではどうでしょう？
HerokuやOpenShiftなどのクラウドにデプロイしているから意識していない？
シェルスクリプトで ``cd ${applicationRoot}; node ./bin/app.js`` を実行させる？　スマートじゃないですね。

## サービス化するための選択肢

自前のサーバ上でNodejsアプリケーションをサービス化する試みはいくつかあって、

* winser
    * windows上でサービス化するためのライブラリ
* forever
    * linux/macOS上でのサービス化するためのデーモンマネージャ
* pm2
    * Advanced, production process manager for Node.js

と言ったところがすぐ見つかるところ。

## LinuxならPM2

Windows上でサービス化しようとするとwinserが唯一の選択肢、と言ったところですが、Linux/UNIXではforeverとpm2があります。
先発プロダクトがforeverで、後発がpm2です。
後発だけあって、pm2はかなり機能が充実しています。

1. 自前ロードバランサ搭載で、クラスタ化可能
    * pm2自体がロードバランサとして動作することで、アプリケーションのクラスタリングを簡単に行うことができます。
      つまり、アプリケーション側でクラスタリングを実装する必要がないということ！
1. 分散したログの集約閲覧
    * アプリのクラスタリングをするとノード単位にログが出力され、何が時系列に挙動を追跡するのが難しくなります。
      しかしpm2ではそれらログを統合し、一挙に確認できるようにしています。
1. 設定ファイルで簡単起動
    * デプロイするサービスを設定ファイル（js/json/yml）に記述し、起動時に読み込めばもろもろの設定込みで起動ができます。
1. 環境スイッチ付き
    * サービス設定ファイルに環境別の環境変数を設定することで、起動時にどの環境を使用するかスイッチすることができます。
1. デプロイ機能つき
    * Gitのリポジトリを利用したリモートサーバへのデプロイ機能があります。

いろいろと機能があって全てを網羅するのは大変ですが、pm2を使用してNodejsアプリのサービス化を行ってみたいと思います。

## pm2でサービス化

ここではトランスパイルしたファイルを使用し、Herokuなどではなく自前のサーバでサービス実行するまでの手順を説明します。
このセクションでは、以下のサーバ・所有者で実行することを目指します。

### 前提

* バージョン

| mw / lib | version |
|---------:|:---------|
| node  | v9.0.0    |
| pm2   | 2.7.2 |

* サービス化環境

| key | value |
|---:|:----|
| サーバ | sampleServ |
| デプロイ先 | /opt/NodeApps/sample |
| 実行ユーザ | sample:sample。 sudoコマンド実行可能 |

デプロイ先/opt/NodeApps/sampleにはすでにアプリ資産がリリースされており、 ``npm install`` も完了している状況です。
本当はpm2のデプロイ機能も使いたかったけれど、サービス化するのが一杯一杯だったので。

### サービス化手順

まずはpm2がなければ意味がないのでインストールします。

``` console#sampleServ
# npm install -g pm2
```

今度は実行ユーザにスイッチして、Nodejsのアプリケーションルートに移動します。pm2コマンドを実行するユーザがアプリの実行ユーザとなるので、
また、アプリケーション以下全てのファイルが実行ユーザの所有者である必要があります。

ではpm2で起動！　となりますがちょっと待って。
設定ファイルを作りましょう。

``` pm2-service.json
{
    "apps": [
        {
            "name"        : "sample",
            "script"      : "./bin/application.js",
            "instances"  : 0,
            "watch"     : true,
            "exec_mode"  : "cluster",
            "source_map_support": true,
            "env": {
                "PORT": 8000,
                "NODE_ENV": "development"
            },
            "env_production" : {
                "NODE_ENV": "production"
            }
        }
    ]
}
```

pm2に吸い込ませる設定ファイルはappsプロパティの配列として定義します。これは複数のNodejsアプリを同一設定で呼び出すことができることを示しています。

それぞれのパラメータはpm2のリファレンスを見てもらうとして、特記すべきは ``exec_mode`` と ``instances`` ですね。
``exec_mode: "cluster"`` を設定すればクラスタリングの設定となり、 ``instances : 0`` とすればサーバが持つCPUコアの数だけノードを展開します(1以上の値を設定すれば使用するコア数=ノード数を設定できる)。

設定ファイルが書き上がってようやく起動です。

``` console#sampleServ
# cd /opt/NodeApps/sample
# pm2 start ./pm2-service.json --env production
```

pm2コマンドをsudoで実行すればrootが、sudoなしで実行すればカレントユーザーがサービスの実行ユーザとなります。
そして、 ``--env`` オプションが環境スイッチです。
 ``pm2-service.json`` にある ``env.env_production`` が環境変数として設定されて実行します。
 ``env.env`` はデフォルト設定、 ``env_*`` とあるものは ``--env`` に*を指定します。

e.g.) ``--env staging`` とすれば ``env.env_staging`` の設定が読み込まれます。

起動したしコレでOK、と思ってはいけません。pm2でのサービス起動の永続化には、 __「現在の状況を記憶」__ させる必要があります。

pm2では

1. 最終的にあるべきサービス状態を構築
1. 状態を記録し、永続化する

という流れでサービス化します。

``` console#sampleServ
# sudo pm2 startup [platform] -u sample
# pm2 save
```

[platform]は実行サーバのinitシステムによって変化します。

* systemd
* upstart
* launchd
* rcd

気をつけなければならないことは、実行ユーザをroot以外にする場合 ``-u`` オプションを忘れてはならないということです。
コレを忘れてしまうと、たとえ想定する実行ユーザで操作をしていたとしても、サーバ再起動をするとrootでアプリが動作します。

## 参考文献

[PM2 - Advanced Node.js process manager](http://pm2.keymetrics.io)
[foerver](https://www.npmjs.com/package/forever)
[winser](https://www.npmjs.com/package/winser)