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のプロパティを増やせばいいですね。