Node.js + forever はアップデートにご注意を

  • 今回は、Node.js をアップデートしたら予期せぬトラブルを経験したので対処方法を紹介します。これまで、Node.js アプリケーションをデーモン化(永続化) するため、forever モジュールを使っていました。また、Linux起動時にforever も同時に開始したかったので、systemd でforever を自動起動させていました。
  • トラブルのきっかけは、Node.js の脆弱性対策です。Node.js を最新バージョンへアップデート後に以下のWarning メッセージが出力され、Linux起動時にforever が動作しません。
  • 原因は、Node.js 14.x 以降にアップグレードすると、forever モジュールに非対応となる様です。
forever: #033[33mwarn#033[39m: --minUptime not set. Defaulting to: 1000ms
forever: #033[33mwarn#033[39m: --spinSleepTime not set. Your script will exit if it does not stay up for at least 1000ms
forever: (node:1221) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency
forever: (Use `node --trace-warnings ...` to show where the warning was created)
forever: (node:1221) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency

 

ステップ1. forever からsystemd 管理に移行する

  • Node.js アプリケーションのデーモン化(永続化) はforever モジュールを使わなくとも、systemd だけで実現できました。

Node.js の停止

  • systemd に foreverを開始するための “app.service” サービスを登録していると仮定します。
  • Node.js を停止するため、forever コマンドでアプリケーションを停止、systemctl コマンドでサービスを停止します。
# forever stopall
# systemctl stop app.service
# forever list

→「info:    No forever processes running」が表示されることを確認する

# systemctl status app.service

→ 「Active: inactive (dead)」が表示されることを確認する

 

systemd の設定変更

  • 以下変更前の systemd のユニットファイルです。
/etc/systemd/system/app.service
[Unit]
Description=Node.js Application
After=multi-user.target systemd-update-utmp-runlevel.service
[Service]
Type=forking
User=root
WorkingDirectory=/home/ec2-user/node
ExecStart=/usr/bin/forever start app.js
KillMode=process
Restart=on-failure
RestartSec=60s
[Install]
WantedBy=multi-user.target

 

# vi /etc/systemd/system/app.service
  • 以下変更後の systemd のユニットファイルです。Node.js アプリケーションを常時起動させるため、「Restart=always」を設定します。「Restart=on-failure」を指定しない理由は、Node.js が正常終了した際に起動しないからです。
/etc/systemd/system/app.service
[Unit]
Description=Node.js Application
After=multi-user.target systemd-update-utmp-runlevel.service
[Service]
Type=simple
User=root
WorkingDirectory=/home/ec2-user/node/
ExecStart=/usr/bin/node app.js
KillMode=process
Restart=always
[Install]
WantedBy=multi-user.target

 

Node.js の起動

  • systemctl コマンドでサービスを起動します。
  • サービス起動後、forever プロセスは存在せず、node プロセスが存在することを確認できます。
# systemctl daemon-reload
# systemctl start app.service
# systemctl status app.service

→ 「Active: active (running)」が表示されることを確認する

# ps aux | grep node | grep -v grep
root      3199  2.1  1.0 976220 39280 ?        Ssl  06:17   0:00 /usr/bin/node app.js

→ /usr/bin/node プロセスが存在することを確認する

 

ステップ2. Node.js を最新にアップデートする

Node.js のアップデート

  • libuvのバージョンが 1.43.0 未満の場合、libuvを事前にアップデートします。libuvをアップデート後、バージョンが 1.43.0 以上、取得先のリポジトリが @epel であることを確認します。
  • Node.js のアップデートは、yum update nodejs にて行います。Node.js をアップデート後、Node.jsのバージョンが更新されたことを確認します。
# systemctl stop app.service
# yum list installed | grep libuv
libuv.x86_64                          1:1.23.2-1.amzn2.0.2           @amzn2-core
# yum update --disablerepo='*' --enablerepo=epel libuv

# yum list installed | grep libuv
libuv.x86_64                          1:1.44.2-1.el7                 @epel

# node -v
v6.17.1

# yum update nodejs

# node -v
v16.18.1

 

Node.js の起動

  • systemctl コマンドでサービスを起動します。
# systemctl start app.service
# systemctl status app.service

→ 「Active: active (running)」が表示されることを確認する