課題:深夜に発生する「アプリ停止」の悪夢
このシナリオは、多くのエンジニアが経験したことがあるはずです。素晴らしいNode.jsアプリを完成させ、サーバーにSSHで入り、node app.jsを実行. ポート3000でスムーズに動くのを確認して安心して寝たものの、翌朝起きると「ウェブサイトにアクセスできません!」という大量のエラーメールが届いている…。
Node.jsを直接コマンドで実行するのは、あくまでクイックテスト用です。本番環境(Production)はもっと過酷です。メンテナンス後のサーバーの自動再起動や、メモリリーク(memory leak)による論理エラーが発生した際、自己修復メカニズムがなければ、あなたのウェブサイトは即座にダウンしてしまいます。
分析:なぜ通常の実行方法では「落ちて」しまうのか?
Node.jsはシングルスレッド(single-thread)で動作します。つまり、未処理の例外(unhandled exception)が1つでも発生すると、プロセス全体が停止してしまいます。さらに、Node.js自体にはシステム起動時に自動実行する機能がありません。VPSが再起動した場合、手動でコマンドを打ち込むまでアプリは停止したままになります。
また、ユーザーに対してポート3000を直接公開するのはセキュリティ上の大きなミスです。HTTPS化、Gzip圧縮、トラフィックの制御などを行う「盾」が前面に必要です。だからこそ、プロフェッショナルなデプロイフローが必要なのです。
解決策:Ubuntu + PM2 + Nginxの三種の神器
システムを安定稼働させるために、私は常にこの構成を推奨しています。プロセス管理にPM2、リバースプロキシにNginx、そし無料SSL証明書にCertbotを使用します。この方法なら、アプリの生存率が高まるだけでなく、ユーザー急増時のスケールアップも容易になります。
ステップ1:NVM経由でのNode.jsインストール
Ubuntuのaptリポジトリから直接Node.jsをインストールするのは避けましょう。バージョンが古く、不安定なことが多いためです。私の経験上、NVM (Node Version Manager)を使うのがベストです。これならNodeのバージョン切り替えも一瞬で行えます。
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v # LTS版がインストールされているか確認
ステップ2:PM2によるアプリケーション管理
PM2は単なるプロセス管理ツールではありません。Cluster Mode(クラスターモード)機能を使えば、サーバーのCPUコアを最大限に活用できます。デフォルトのNode.jsは1コアしか使いませんが、4コアのサーバーでクラスターモードを使わなければ、ハードウェアリソースの75%を無駄にしていることになります。
PM2をグローバルにインストールします:
npm install pm2 -g
クラスターモードを有効にして、CPUコア数に応じて自動で負荷分散させます:
pm2 start app.js -i max --name "my-app"
よりプロフェッショナルに管理するために、ecosystem.config.jsファイルを作成しましょう。環境変数(environment variables)の管理が非常に効率的になります:
module.exports = {
apps : [{
name: "my-app",
script: "./app.js",
instances: "max",
exec_mode: "cluster",
env_production: {
NODE_ENV: "production",
PORT: 3000
}
}]
}
そして、サーバーのクラッシュや再起動後にアプリを自動復旧させる「魔法のコマンド」を忘れないでください:
pm2 startup
# 画面に表示されたコマンドをコピーして実行してください
pm2 save
ステップ3:Nginxをリバースプロキシとして設定する
現在、アプリは内部のポート3000で動作しています。Nginxを使ってポート80/443でリクエストを受け取り、それを3000へと流します。これにより、内部構造を隠蔽し、静的ファイルの処理速度を向上させることができます。
サイトの設定ファイルを作成します:
sudo nano /etc/nginx/sites-available/my-app
以下の内容を貼り付け、yourdomain.comを自分のドメインに書き換えてください:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
設定を有効化し、再起動前に構文エラーがないかチェックします:
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
ステップ4:Let’s EncryptによるSSL化
今日、HTTPS化されていないサイトはブラウザから即座に「保護されていない通信」と警告されます。高額なSSL証明書を買わなくても、Certbotを使えば完全無料で取得できます。
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com
Certbotは自動的にNginxファイルを修正し、HTTPSを設定し、期限が切れる前に自動更新まで行ってくれます。運用コストはほぼゼロです。
最適化のコツ:実務から学んだノウハウ
サーバーのディスク容量が突然いっぱいになり頭を抱えた経験から、一つの教訓を得ました。それは「ログ管理の重要性」です。PM2は詳細なログを記録しますが、放置すると数週間でログファイルが数十GBに膨れ上がることがあります。
pm2-logrotateモジュールをインストールして、古いログの自動圧縮と削除を行いましょう:
pm2 install pm2-logrotate
また、コード更新(デプロイ)時の重要な注意点として、pm2 restartではなくpm2 reloadを使ってください。reloadは各インスタンスを順次再起動するため、ウェブサイトを1秒たりとも中断させないゼロダウンタイム(Zero-downtime)を実現できます。
まとめ
Node.jsアプリをプロフェッショナルに運用するための4つの柱は、バージョン管理のNVM、アプリを常時稼働させるPM2、トラフィックを制御するNginx、そしてセキュリティのためのCertbotです。このワークフローにより、私は1日あたり数千人のユーザーが訪れる多くの実案件を安定して運用できています。皆さんのデプロイの成功を祈っています!

