502 Bad Gatewayの悪夢と実体験から学んだ教訓
午前2時、スマホが激しく震えました。電話の向こうでは上司が怒鳴っています。「ウェブサイトが落ちてるぞ!客が決済しようとしたら502エラーが出たらしい」。私は慌ててPCを立ち上げ、CentOS Stream 9のサーバーにSSHで接続しました。ps aux | grep node と打ち込みましたが、結果は空っぽでした。
原因は非常に初歩的なものでした。昨夜、npm start を実行した後、そのままターミナルを閉じて寝てしまったのです。SSHセッションが終了すると、CentOSは子プロセスを自動的にスキャンしてクリーンアップします。その結果、アプリケーションは即座に消えてしまいました。あるいは、小さな uncaughtException が発生しただけでも、Node.jsはクラッシュし、誰かが助けに来るまで停止したままになります。
5台のサーバーをCentOS 8からCentOS Stream 9へ移行した後、私は確信しました。安眠したいのであれば、本番環境でNode.jsを直接動かしてはいけません。信頼できるプロセス管理ツール(Process Manager)が必要です。そこで登場するのが PM2 です。
なぜNode.jsアプリケーションは頻繁に停止してしまうのか?
Node.jsはシングルスレッドで動作します。ハンドルされていないロジックエラーが1つあるだけで、プロセス全体が停止します。開発環境であれば手動で再起動できますが、本番環境で24時間365日サーバーを見守り続けるわけにはいきません。
CentOS上でNode.jsアプリを停止させる「4つの刺客」は以下の通りです:
- セッションタイムアウト: ターミナルウィンドウを閉じると、プロセスも終了します。
- ランタイムエラー: コードのバグにより、プロセスが予期せずクラッシュします。
- サーバーの再起動: クラウドプロバイダーのメンテナンスやカーネルアップデートによる再起動。
- OOM (Out of Memory): 低スペックのVPS(例:1GB RAM)では、アプリがリソースを消費しすぎると、OS保護のためにシステムによって強制終了されます。
PM2:nohupやSystemdに代わるプロフェッショナルな解決策
初心者の多くは nohup node app.js & を使いがちです。これならターミナルを閉じてもアプリは死にませんが、ログの管理が極めて困難です。また、Systemd を使って .service ファイルを作成する人もいます。これはLinuxの標準的な方法ですが、アプリを追加するたびに設定ファイルを記述するのは非常に手間がかかります。
PM2 (Process Manager 2) は、これらの中で最も最適な選択肢です。クラッシュ時の自動再起動、ログの集中管理、リアルタイムのリソース監視など、数個のシンプルなコマンドですべてを提供してくれます。
CentOS Stream 9へのNode.jsインストール:安定性のためにLTS版を選択
CentOS Stream 9は AppStream を通じてソフトウェアを管理します。デフォルトリポジトリにある古いバージョンではなく、ライブラリが最もスムーズに動作する長期サポート(LTS)版を選択しましょう。
ステップ 1:利用可能なNode.jsバージョンの確認
sudo dnf module list nodejs
システムには16、18、20などのストリームが表示されます。Node.js 20を選択することをお勧めします。これは現行のLTS版であり、Next.jsやNestJSなどのフレームワークを最適にサポートしています。
ステップ 2:有効化とインストール
# バージョン20のストリームを選択
sudo dnf module enable nodejs:20 -y
# nodejsとnpmをインストール
sudo dnf install nodejs -y
# バージョンを確認
node -v
PM2 — アプリケーションのための専属ボディーガード
Node.jsをインストールしたら、システム上のどこからでも操作できるようにPM2をグローバルにインストールします。
sudo npm install pm2 -g
最初のアプリケーションを起動する
プロジェクトが /var/www/api-service/index.js にあると仮定しましょう。通常のnodeコマンドではなく、PM2に管理を任せます:
cd /var/www/api-service
pm2 start index.js --name "api-production"
これでアプリは安全です。SSHをログアウトしても、アプリケーションはバックグラウンドでユーザーへのサービスを継続します。
トラブルシューティングに役立つクイックコマンド
運用中、トラブル対応によく使うコマンドは以下の通りです:
pm2 list: アプリがどれくらいのRAMとCPUを消費しているか確認します。pm2 logs api-production: リアルタイムログを表示します。500エラーの原因を特定するのに非常に役立ちます。pm2 restart api-production: コードを更新した後にアプリを再起動します。
システムの自動起動設定
これは多くの人が見落としがちな最も重要なステップです。サーバーが再起動した場合、スタートアップスクリプトを設定していない限り、PM2は自動的に再開されません。
まず、PM2にsystemd用の起動コマンドを生成させます:
pm2 startup
システムは sudo env PATH=... で始まる長いコマンドを返します。その行をすべてコピーしてターミナルに貼り付け、Enterキーを押してください。
最後に、現在の状態を保存して、PM2が実行中のアプリを記憶するようにします:
pm2 save
これで、sudo reboot を試すことができます。サーバーの起動が完了すると、手動で操作しなくてもアプリは自動的に「復活」します。
CentOS Stream 9でのファイアウォールポート開放
アプリがポート3000で動作していても、ブラウザからアクセスできない場合は、firewalld が通信を遮断している可能性が高いです。以下のコマンドでポートを開放してください:
sudo firewall-cmd --permanent --add-port=3000/tcp
sudo firewall-cmd --reload
高度なテクニック:クラスターモードの活用
Node.jsはデフォルトで1つのCPUコアのみで動作します。サーバーに4つのコアがある場合、ハードウェア性能の75%を無駄にしていることになります。PM2には、複数のインスタンスを同時に実行できる クラスターモード があります:
# 利用可能なすべてのCPUコアを活用する
pm2 start index.js -i max
この設定では、1つのインスタンスがクラッシュしても、他のインスタンスがリクエストを処理し続けます。これにより、ダウンタイムほぼゼロ(Zero Downtime)を実現できます。
最初から正しい手順で設定を行えば、CentOS Stream 9でのNode.js管理は非常に快適になります。これらの実践的な経験が、大量のトラフィックにも動じない堅牢なシステムの構築に役立つことを願っています。
