💣

node.jsのインストールをaptリポジトリからnvmに切り替えたらnpm startしてたサービスが動かなくなった

2024/01/06に公開

何が起こったのか

ことの発端

Node.jsを使うアプリケーション(具体的にはmisskey)のアップデートをした際、ビルド時に「node.js v20」以上を求めてきた。
サーバには「node.js v18.x」が導入されており、アップデートの必要が生じた。

Node.jsの公式リポジトリ(nodesource)を使っており、リポジトリごと更新するのはめんどくさいな…… と思っていたところ、「Node Version Manager」の存在を知った。
これならrvenvと同じノリでサクッとバージョンを切り替えられるじゃんヤッターと考え、早速導入を行った。

異変(1): プログラムのビルドができない

最初の異変を発見したのは、NVMをインストールし、再度プログラムのビルドを行ったときだった。

nvm経由で「v20.10.0」をインストールしたにもかかわらず、ビルド時に「v18.x」を使おうとしてコケてしまっていたのだ。

(ログ残ってなかった、ごめんなさい)

原因と対応(1): 既存バージョンとの衝突

公式リポジトリ(nodesource)からパッケージマネージャ経由でインストールした「v18.x」が残っていた。
パッケージマネージャから削除してバージョンの衝突が起こらないようにした。

$ sudo apt remove nodejs

原因と対応(2): pnpmで使用するバージョンの直接指定

ビルド時に「pnpm(Node.js用パッケージマネージャ)」を使用していたが
どうやら使用するバージョンの指定ができたらしいので設定してみた。

$ pnpm add -g pnpm
Nothing to stop. No server is running for the store at /home/misskey/.local/share/pnpm/store/v3
Already up to date
Progress: resolved 1, reused 0, downloaded 0, added 0, done
Done in 3.6s
$ pnpm env use --global 20
Node.js 20.10.0 was installed
  /home/misskey/.local/share/pnpm/nodejs/20.10.0
Node.js 20.10.0 was activated
/home/misskey/.local/share/pnpm/node -> /home/misskey/.local/share/pnpm/nodejs/20.10.0/bin/node
$ 

pnpm add -g pnpm で、pnpmをグローバルパッケージとして登録
pnpm env use --global 20 で、使用するバージョンを「v20」と明示的に指定

env useしたところ、Node.js本体をダウンロードしてるような雰囲気を見せたので、もしかしたらnvm要らなかったんじゃないか疑惑もあるが、ひとまず目をつぶっておく。

異変(2): サービスを起動できない

ビルドが通って一安心、さあサービスを起動するぞと思ったところ、サービスが立ち上がらなくなっていた。

$ sudo systemctl status misskey
● misskey.service - Misskey daemon
     Loaded: loaded (/etc/systemd/system/misskey.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Sat 2024-01-06 21:16:03 JST; 2s ago
    Process: 1339 ExecStart=/usr/bin/npm start (code=exited, status=203/EXEC)
   Main PID: 1339 (code=exited, status=203/EXEC)
        CPU: 2ms

Jan 06 21:16:03 misskey systemd[1]: misskey.service: Scheduled restart job, restart counter is at 5.
Jan 06 21:16:03 misskey systemd[1]: Stopped Misskey daemon.
Jan 06 21:16:03 misskey systemd[1]: misskey.service: Start request repeated too quickly.
Jan 06 21:16:03 misskey systemd[1]: misskey.service: Failed with result 'exit-code'.
Jan 06 21:16:03 misskey systemd[1]: Failed to start Misskey daemon.

どうして……

念のため、直接プログラムを起動してみたところ問題なく動作する。

$ node -v
v20.10.0
$ NODE_ENV=production pnpm start

> misskey@2023.12.2 start /home/misskey/misskey
> pnpm check:connect && cd packages/backend && node ./built/boot/entry.js


> misskey@2023.12.2 check:connect /home/misskey/misskey
> cd packages/backend && pnpm check:connect


> backend@ check:connect /home/misskey/misskey/packages/backend
> node ./check_connect.js

  _____ _         _           
 |     |_|___ ___| |_ ___ _ _ 
 | | | | |_ -|_ -| '_| -_| | |
 |_|_|_|_|___|___|_,_|___|_  |
 v2023.12.2              |___|

 Misskey is an open-source decentralized microblogging platform.
 If you like Misskey, please donate to support development. https://www.patreon.com/syuilo

--- misskey (PID: 5605) ---
INFO *  [core boot]     Welcome to Misskey!
INFO *  [core boot]     Misskey v2023.12.2
INFO *  [core boot env] NODE_ENV: production
INFO *  [core boot nodejs]      Version v20.10.0 detected.
DONE *  [core boot config]      Loaded
DONE *  [core boot]     Misskey initialized
(以下略)

きちんとNodeもv20.10.0が使われている、問題ない。

原因と対応(1): nvmを使う場合のパス指定が必要だった

原因はサービスのユニットファイルにあった。

以下はデフォルトのサービスのユニットファイルである。

/etc/systemd/system/misskey.service
[Unit]
Description=Misskey daemon

[Service]
Type=simple
User=misskey
ExecStart=/usr/bin/npm start
WorkingDirectory=/home/misskey/misskey
Environment="NODE_ENV=production"
TimeoutSec=60
StandardOutput=journal
StandardError=journal
SyslogIdentifier=misskey
Restart=always

[Install]
WantedBy=multi-user.target

起動時に絶対パスを使用してnpmをキックしようとしているが、nvm経由でNode.jsを導入した場合のパスはバージョンごとに異なるということが分かった。

$ which npm
/home/misskey/.nvm/versions/node/v20.10.0/bin/npm
$ 

じゃあこのパスをユニットファイルに書けばいいじゃん、となるが
ちゃんとした書き方があるらしい

/etc/systemd/system/misskey.service(抜粋)
ExecStart=/home/misskey/.nvm/nvm-exec npm start
Environment="NODE_VERSION=v20.10.0"

なお、NODE_VERSIONを削ったらデフォルトを使うのかなと思ったら全く動かなくなった。どうやら指定必須らしい。

おわりに

管理コストを削減しようと思って導入したnvmだったが、扱い方をあまり理解しておらず環境を壊しかけてしまった。
今回対象のアプリケーション(Misskey)は更新ペースが比較的速く、Node.jsのバージョン更新の頻度もそれに従って上がると見込んでいるため
今回の試行錯誤は無駄ではなかったと思いたい。

Discussion