PM2の深淵:Zero Downtimeを実現するプロセス・オーケストレーションの極意
多くのエンジニアがPM2を単なる「プロセスの自動再起動ツール」と誤解している。しかし、本質を見抜くアーキテクトにとって、PM2はNode.jsというシングルスレッドの制約を打破し、OSリソースを極限まで使い切るための「高機能なプロセス・マネジメント・レイヤー」である。
本稿では、表面的な使い方ではなく、プロダクション環境で「秒単位の停止すら許さない」ための、メモリ管理、シグナルハンドリング、そしてCI/CDパイプラインとの完璧な融合について詳述する。
—
1. クラスターモードの真実:OSとの協調設計
クラスターモード(`instances: ‘max’`)を使えばパフォーマンスが上がる、という安直な考えは捨てろ。重要なのは「CPU親和性」と「メモリオーバーヘッド」のトレードオフだ。
メモリとインスタンス数の最適化
Node.jsのV8エンジンは、デフォルトで一定のヒープメモリを確保する。`max`インスタンスを指定すると、物理メモリを食いつぶし、OSレベルでのスワップが発生してパフォーマンスが急落する。
アーキテクトの推奨設定:
// ecosystem.config.js
module.exports = {
apps: [{
name: ‘api-service’,
script: ‘dist/main.js’,
// 物理コア数に応じて柔軟に設定。論理コアが多い環境では制限を設ける
instances: ‘max’,
exec_mode: ‘cluster’,
// メモリリークによるOOM(Out of Memory)を未然に防ぐ生命線
max_memory_restart: ‘1G’,
// リロード時のオーバーラップによるポート衝突を防ぐ
wait_ready: true,
listen_timeout: 5000,
kill_timeout: 3000,
}]
};
ここで重要なのは `wait_ready: true` だ。これを有効にすると、Node.js側で `process.send(‘ready’)` を発火するまでPM2は旧プロセスを殺さない。この同期的なハンドシェイクこそが、Zero Downtimeの要諦である。
—
2. Zero Downtime Reloadの解像度を高める
`pm2 reload` は内部でシグナルを送信し、新しいプロセスを立ち上げてから古いプロセスをGraceful Shutdownする。だが、アプリケーション側が `SIGINT` や `SIGTERM` を正しく処理できなければ意味がない。
Graceful Shutdownのシグナル制御
Node.js側で接続を閉じ、キューイングされているリクエストを処理し切る時間を計算に入れろ。
// main.ts (アプリケーション側)
process.on(‘SIGINT’, async () => {
console.log(‘Shutdown signal received…’);
await server.close(); // 新規接続を遮断
await database.disconnect(); // 接続プールを安全にクローズ
process.exit(0);
});
// PM2に準備完了を通知
if (process.send) {
process.send(‘ready’);
}
—
3. Docker環境における「PM2の落とし穴」と対策
コンテナ環境でPM2を動かす際、最大のアンチパターンは「PID 1問題」を考慮せず、PM2をメインプロセスにしてしまうことだ。DockerはSIGTERMをPID 1に送るが、PM2がそれを適切に転送し損ねると、コンテナの停止時にゾンビプロセスが発生する。
解決策:`tini` を使用し、PM2をフォアグラウンドで実行する
Dockerfile
ENTRYPOINT [“/sbin/tini”, “–“]
CMD [“pm2-runtime”, “start”, “ecosystem.config.js”]
`pm2-runtime` はDocker専用に設計されており、環境変数やシグナルを適切にハンドリングする。これを使わずに `pm2 start` を行うのはエンジニアとしての怠慢と言える。
—
4. CI/CDパイプラインとの高度な統合
デプロイフローにおいて、PM2のプロセスを更新するタイミングで「ヘルスチェック」を挟むのは必須だ。
自動化スクリプトによるデプロイ後の検証
`pm2 reload` を叩いて終わりではない。APIを叩き、ステータスコードを確認し、エラーが発生していないことを確認するまでがデプロイである。
deploy.sh
pm2 reload ecosystem.config.js –update-env
PM2のプロセス状態が安定するまで待機
sleep 5
ヘルスチェック: 失敗したら即座にロールバック
if curl -f http://localhost:3000/health; then
echo “Deployment successful.”
else
echo “Health check failed! Rolling back…”
pm2 revert ecosystem.config.js
exit 1
fi
—
5. ログローテーションの管理(PM2-Logrotate)
運用が長くなると `~/.pm2/logs/` 下のファイルがディスクを圧迫する。標準の `pm2-logrotate` は優秀だが、デフォルトのローテーションサイズ(10MB)は大きすぎる場合が多い。
コマンドによる高度なチューニング:
ログサイズを200MB以下に制限し、圧縮を有効化
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 200M
pm2 set pm2-logrotate:compress true
pm2 set pm2-logrotate:retain 7 # 7世代分のみ保持
—
結論:PM2を「運用のインフラ」にする
PM2は単なるコマンドツールではない。それはNode.jsアプリケーションにおける「ランタイム・カーネル」に近い存在だ。
1. `wait_ready` と `SIGTERM` ハンドリングで非同期境界を制御する
2. `pm2-runtime` を使いコンテナ・オーケストレーションの作法に従う
3. CI/CDパイプラインでヘルスチェックとロールバックを自動化する
これらの知見を実装に落とし込むことで、あなたのサービスは「再起動の恐怖」から解放される。真のDevOpsとは、ツールを使いこなすことではなく、ツールに「壊れない仕組み」を語らせることである。さあ、今すぐコードを書き換え、あなたのアーキテクチャを堅牢なものへ昇華させよ。