pm2で環境によって設定を変更したい

pm2を使ってnodeアプリのプロセス管理を行っていますが、大抵のアプリは複数の環境が存在していると思います。

  • 本番環境:production
  • ステージング環境:stg

など。

pm2ではecosystem.config.jsで各設定が可能です。

module.exports = {
    apps: [
        {
            name: 'product',
            exec_mode: 'cluster',
            instances: 'max',
            script: '.output/server/index.mjs',
            args: 'start',
            env: {
                HOST: '0.0.0.0',
                PORT: 3000,
            },
        },
    ],
};

こんな感じに。

環境変数を切り替える

通常はpm2 startで立ち上げるとアプリルートにecosystem.config.jsが存在すればそれを読み込んでくれるので、上記の設定が機能します。

でも本番環境とステージング環境で環境変数が同じだと困りますよね。

例えば上記の設定だと同じサーバーで本番とステージングを起動させているとポートが競合してあとから起動しようとしたやつが落ちるか次のポート番号に切り替わってしまいます。

そこで立ち上げコマンドをpm2 start --env stagingのようにすると環境変数を切り替えることができます。

module.exports = {
    apps: [
        {
            name: 'product',
            exec_mode: 'cluster',
            instances: 'max',
            script: '.output/server/index.mjs',
            args: 'start',
            env: {
                HOST: '0.0.0.0',
                PORT: 3000,
            }
            env_staging: {
                HOST: '0.0.0.0',
                PORT: 3001,
            },
        },
    ],
};

こんな感じですね。

env_{コマンド引数}の環境変数が適用されることになります。

なのでこれで--env stagingの場合はポート3001で起動し、--envなしの場合はポート3000で起動します。

環境変数以外を切り替えられないの?

さて、上記の設定で一つ問題があります。

それはどちらもproductという名前でpm2に登録されるため、あと勝ちで登録が上書きされてしまいます。

これだとせっかく環境変数を切り替えたのに共存できないので意味がないですね。

そうしたらステージングの場合は別の名前にしましょう。

PM2_NAME=production-stg pm2 startで起動することによって、ecosystem.config.js内でprocess.env.PM2_NAMEを使用することが可能となります。

module.exports = {
    apps: [
        {
            name: process.env.PM2_NAME || 'product',
            // 中略
        },
    ],
};

こうすることによって名前も切り替えが可能です。

項目ごとに環境変数を追加するの面倒だし管理が……

さて、次に問題となってくるのは環境によってpm2の設定が異なる項目が多くなるほど上記の方法では環境変数をどんどん追加していかないといけなく、メンテナンス性が悪くなってしまいます。

たとえばプロセス数をステージングでは1、本番ではmaxにしたい。

あとステージングでクラスターにする必要性がないからステージングではfork、本番ではclusterにしたい。

コマンドは……

PM2_NAME=production-stg PM2_PROCESS_COUNT=1 PM2_MODE=fork pm2 start --env staging

設定は……

module.exports = {
    apps: [
        {
            name: process.env.PM2_NAME || 'product',
            exec_mode: process.env.PM2_MODE || 'cluster',
            instances: process.env.PM2_PROCESS_COUNT || 'max',
            script: '.output/server/index.mjs',
            args: 'start',
            env: {
                HOST: '0.0.0.0',
                PORT: 3000,
            },
            env_staging: {
                HOST: '0.0.0.0',
                PORT: 3001,
            }
        },
    ],
};

うーん、まだ2環境しかないからマシな方だけど、環境が増えたり項目が増えるとこれは辛いなぁ……

ということで環境名で判断するようにしようと思います。

環境名で設定項目を変更

pm2では特に環境名で設定を変更するような機能はないので、--env {環境名}を使用したいと思います。

nodeではprocess.argvでコマンドが配列として取得できます。

そこで--env stagingを取得できればいいので、まず--envを取得します。

process.argv.indexOf('--env')で取得できれば整数が返ってきて、取得できなければ-1が返ってきます。

もし整数が返ってきているのなら+1したindex値が環境を指定しているstaging部分となります。

それを用いて各種の設定値を作ればいいわけですね。

const envIndex = process.argv.indexOf('--env');
const env = envIndex !== -1 ? process.argv[envIndex + 1] : null;

const params = {
    name: 'product',
    exec_mode: 'cluster',
    instances: 'max',
};

if (env === 'staging') {
    params.name = 'product-stg';
    params.exec_mode = 'fork';
    params.instances = 1;
}

module.exports = {
    apps: [
        {
            name: params.name,
            exec_mode: params.exec_mode,
            instances: params.instances,
            script: '.output/server/index.mjs',
            args: 'start',
            env: {
                HOST: '0.0.0.0',
                PORT: 3000,
            },
            env_staging: {
                HOST: '0.0.0.0',
                PORT: 3001,
            }
        },
    ],
};

これでそれぞれステージングはpm2 start --env staging、本番はpm2 startで起動することによって環境毎に設定を変えて起動することができます。

環境が増えた場合はifを増やせばいいし、項目が増える場合はparamsのプロパティを増やせばいいですね。