Node.jsを構築する
NODE.JS 02 / 02

Node.jsをサービス化しWeb公開する

専用ユーザーでアプリを常駐させ、Nginx・Apache・Caddyから一つを選んで転送します。

01 / 専用ユーザーと配置を用意

Node.jsの導入とアプリテストを完了したUbuntu Server 22.04で操作します。物理・仮想共通です。特記以外、サーバーの一般管理者で実行します。この例はNode.jsアプリを専用ユーザーで常駐させ、選んだWebサーバーから転送します。PHPやPostgreSQLは必須ではありません。

nodeguideはログインできない実行専用ユーザー、/opt/node-guide/appは読み取り用コード、3001はサーバー内だけの待受ポートです。同名ユーザー・ファイル・サービスや3001番の利用がある場合は上書きせず、用途を確認して別名を決めてください。

getent passwd nodeguide
sudo ss -lntp
sudo test ! -e /opt/node-guide
sudo test ! -e /etc/node-guide.env
sudo test ! -e /etc/systemd/system/node-guide.service

ユーザーの照会は未作成なら終了コード2です。既存構成と競合しないことを確認して、導入します。コードはroot所有にし、サービスユーザーが自分の実行コードを変更できないようにします。このアプリは書込ディレクトリや外部依存を必要としません。

sudo useradd --system --user-group --home-dir /nonexistent --no-create-home --shell /usr/sbin/nologin nodeguide
sudo install -d -o root -g root -m 755 /opt/node-guide/app
sudo install -o root -g root -m 644 "$HOME/node-guide-work/server.cjs" /opt/node-guide/app/server.cjs
printf '%s\n' 'PORT=3001' | sudo tee /etc/node-guide.env >/dev/null
sudo chown root:root /etc/node-guide.env
sudo chmod 600 /etc/node-guide.env

設定は公開コードから分離します。この例のPORTは秘密ではありませんが、将来パスワード等を入れる場合もGitやWeb公開先へ置かず、ログ・画面へ表示しないでください。

02 / systemdで起動・監視

次の内容を/etc/systemd/system/node-guide.serviceへsudoで保存します。/usr/bin/nodeは前ページで確認した実行ファイルです。異なる場合は正しい絶対パスを確認してください。

[Unit]
Description=Node.js guide application
After=network.target

[Service]
Type=simple
User=nodeguide
Group=nodeguide
WorkingDirectory=/opt/node-guide/app
Environment=NODE_ENV=production
EnvironmentFile=/etc/node-guide.env
ExecStart=/usr/bin/node /opt/node-guide/app/server.cjs
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target
sudo systemd-analyze verify /etc/systemd/system/node-guide.service
sudo systemctl daemon-reload
sudo systemctl enable --now node-guide.service
systemctl is-active node-guide.service
systemctl is-enabled node-guide.service
systemctl show node-guide.service -p User -p MainPID -p NRestarts
sudo ss -lntp | grep ':3001'
curl --fail --show-error http://127.0.0.1:3001/health

active・enabled、User=nodeguide、127.0.0.1:3001での待受、ヘルスチェックを確認します。0.0.0.0や外部向けIPで待ち受けていたら公開前に修正してください。UFWやクラウドFWで3001番を公開する必要はありません。

このunitはコードの書換えやホームへのアクセスを制限します。書込みが必要な実アプリでは必要最小限の別ディレクトリを設計し、制限をまとめて外さないでください。ログはsudo journalctl -u node-guide.service -b --no-pagerで確認します。

03 / Webサーバーを一つ選ぶ

Nginx・Apache・Caddyのうち、すでに利用する一つの設定だけを追加します。三つを直列に導入したり、既存サイトの設定を丸ごと置き換えたりしません。80/443番の競合を確認してください。

以下のnode-guide.testは検証用ホスト名です。実際のドメインに置換してください。ドメイン未定の場合、閉じた管理ネットワークのクライアントでhosts設定等を使って確認できます。HTTPでログインや秘密を送信せず、一般公開前に選んだ製品のHTTPSを構成してください。公開DNS・証明書の発行・更新は本手順の実測対象外です。

設定変更前に対象ディレクトリを/root下のアクセス制限された退避先へコピーし、保存場所を記録してください。Nginxなら/etc/nginx、Apacheなら/etc/apache2、Caddyなら/etc/caddyをコピーします。Nginxの例:

BACKUP="$(sudo mktemp -d /root/server-guide-node-web.XXXXXX)"
# 選んだ製品だけ実行する例(Nginxの場合)
sudo cp -a /etc/nginx "$BACKUP/"
printf '保存先: %s\n' "$BACKUP"
Nginxを使う場合

Nginxの基本設定を先に済ませます。新規/etc/nginx/sites-available/node-guideを作成します。

server {
    listen 80;
    server_name node-guide.test;
    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
sudo test ! -e /etc/nginx/sites-enabled/node-guide &&
sudo ln -s /etc/nginx/sites-available/node-guide /etc/nginx/sites-enabled/node-guide
sudo nginx -t && sudo systemctl reload nginx

既存のdefaultや他サイトの有効化リンクは残します。失敗時はreloadせず構文と参照先を修正します。

Apacheを使う場合

Apacheの基本設定を済ませ、新規/etc/apache2/sites-available/node-guide.confを作成します。

<VirtualHost *:80>
    ServerName node-guide.test
    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:3001/
    ProxyPassReverse / http://127.0.0.1:3001/
</VirtualHost>
sudo a2enmod proxy proxy_http
sudo a2ensite node-guide.conf
sudo apache2ctl configtest && sudo systemctl reload apache2

既存のサイトを一括無効化しません。プロキシモジュールを将来戻す場合も、他サイトが利用していないか確認します。

Caddyを使う場合

Caddyの基本設定を済ませます。既存Caddyfileを退避し、新しいサイトブロックとして追加します。分割管理なら/etc/caddy/sites/node-guide.Caddyfileを作成し、メインの/etc/caddy/Caddyfileにimport /etc/caddy/sites/*.Caddyfileが一度だけ存在することを確認します。

http://node-guide.test {
    reverse_proxy 127.0.0.1:3001
}
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl reload caddy

http://は閉じた経路でのHTTP確認用です。公開用ドメインでCaddyの自動HTTPSを使う場合は、DNS・到達性・証明書の条件を確認して別途設定します。

3001番は閉じたまま、管理用SSHの許可を保ち、必要な80/443番だけFWの手順に従って許可します。

04 / 応答と障害時の動作を確認

まずサーバー内から確認します。検証用ホスト名を実際の設定値に合わせてください。curlの--resolveはこの通信だけを127.0.0.1へ向けます。

curl --fail --show-error --resolve node-guide.test:80:127.0.0.1 http://node-guide.test/
curl --fail --show-error --resolve node-guide.test:80:127.0.0.1 http://node-guide.test/health
curl -i --resolve node-guide.test:80:127.0.0.1 http://node-guide.test/not-found
curl -i -X POST --resolve node-guide.test:80:127.0.0.1 http://node-guide.test/

トップページとhealthが200、存在しないパスは404、POSTは405であることを確認します。別クライアントからも、設定したホスト名で同じ応答とブラウザー表示を確認します。/.envや/server.cjsが404となり、ファイル内容を返さないことも確認してください。

サービスを停止すると転送先が失われるため、Webサーバーはエラーを返します。新規の検証サイトでメンテナンス時間を確保してから停止・復旧を試します。

sudo systemctl stop node-guide.service
curl -i --resolve node-guide.test:80:127.0.0.1 http://node-guide.test/health
sudo systemctl start node-guide.service
curl --fail --show-error --resolve node-guide.test:80:127.0.0.1 http://node-guide.test/health

停止中にソースを返さず、再開後は200へ戻ることを確認します。ブラウザーの表示だけでなく、systemd状態・待受先・ログを組み合わせて判断します。

参考:実測の範囲

Ubuntu Server 22.04.5の実VMで専用ユーザー、root所有コード、systemdの制限、起動・自動起動設定、127.0.0.1:3001の待受を確認しました。異常終了を模擬したSIGKILLの後、新しいPIDで自動再起動し、healthが200に戻りました(その時点のNRestartsは1)。以下のサービス画面は、その後に明示的なrestartを行った状態でNRestartsは0です。Nginx・Apache・Caddyは一つずつ切り替え、別のNATクライアントからトップとhealthの200、存在しないパス・server.cjs・.envの404を確認しました。POSTは405、バックエンド停止中はNginx/Caddyが502、Apacheが503を返し、再開後は200に戻りました。ブラウザー画面はSSH管理トンネル経由で検証用ホスト名をローカル解決し、Caddyから専用サービスへ転送した実表示です。公開サイトの表示を検証した画像ではありません。OS全体の再起動、公開DNS・ACME証明書・インターネット公開、負荷・WebSocket・実フレームワークとの統合は未検証です。

Node.js専用サービスとループバック待受およびHTTP応答
Ubuntu Server 22.04.5の実VMで取得した画面。
専用サービスからWebサーバーを経由して表示したNode.jsアプリ
Ubuntu Server 22.04.5の実VMで取得した画面。

05 / 更新・バックアップと戻し方

更新前にコード、package.jsonとlockfile、unit、環境設定、選んだWebサーバーの設定をWeb/Gitの外へ退避します。秘密のある環境設定はrootだけが読めるようにします。この例は永続データを保存しません。実アプリではデータベース・アップロード等の保存先を別途設計し、復元も試してください。

次のコードを一般ユーザーで検証し、保守時間にrootで配置してsudo systemctl restart node-guide.serviceを実行し、healthとログを再確認します。稼働中ディレクトリでsudo npm installを実行しません。この例は単一プロセスで、更新時の無停止切替・負荷試験・WebSocketは対象外です。

公開設定を戻す場合は、追加したNginxのnode-guideリンク、Apacheのnode-guideサイト、またはCaddyのnode-guideブロックだけを外し、構文検査後に再読み込みします。既存のLaravel等のサイトや、共有のCaddy importを削除しません。利用がないことを確認してからsudo systemctl disable --now node-guide.serviceでサービスを停止します。unitや専用コードを退避して削除した場合はdaemon-reloadも実行してください。元のコード・設定を戻すときは、所有者・権限・サービス起動・HTTP応答まで確認します。