これは、なにをしたくて書いたもの?
前に、PM2を使ってNode.jsアプリケーションのクラスター化をしてみました。
PM2を使って、Node.jsアプリケーションをクラスター化(CPUスケーリング)させてみる - CLOVER🍀
今度は、Dockerコンテナ内でPM2を使い、アプリケーションをCPUスケーリングできるようにしたいと思います。
Dockerコンテナ内でPM2を使う
PM2の方にドキュメントがあります。
簡単に言うと、PM2をグローバルインストールして、pm2-runtime
を使ってアプリケーションを起動すればOKです。
pm2-runtime
は、Node.jsバイナリの代わりに使えるCLIです。
pm2-runtime is a drop-in replacement node.js binary with some interesting production features
Docker Integration / pm2-runtime Helper
あと、Graceful Shutdownについても見ておいた方がよいでしょう。
ちなみに、PM2自体のDockerイメージもあるみたいですが、今回はパスします。
環境
今回の環境は、こちら。
$ docker version Client: Docker Engine - Community Version: 20.10.12 API version: 1.41 Go version: go1.16.12 Git commit: e91ed57 Built: Mon Dec 13 11:45:33 2021 OS/Arch: linux/amd64 Context: default Experimental: true Server: Docker Engine - Community Engine: Version: 20.10.12 API version: 1.41 (minimum version 1.12) Go version: go1.16.12 Git commit: 459d0df Built: Mon Dec 13 11:43:42 2021 OS/Arch: linux/amd64 Experimental: false containerd: Version: 1.4.12 GitCommit: 7b11cfaabd73bb80907dd23182b9347b4245eb5d runc: Version: 1.0.2 GitCommit: v1.0.2-0-g52b36a2 docker-init: Version: 0.19.0 GitCommit: de40ad0
アプリケーションを作っている時に使ったNode.js、npmのバージョンはこちらですが、最終的にはDockerイメージに
なります(Node.jsは同じバージョンを選択しますが)。
$ node --version v16.13.1 $ npm --version 8.1.2
アプリケーションの作成
まずはアプリケーションを作成します。TypeScriptでプロジェクトをセットアップ。
$ npm init -y $ npm i -D typescript $ npm i -D -E prettier
設定。
tsconfig.json
{ "compilerOptions": { "target": "esnext", "module": "commonjs", "baseUrl": "./src", "outDir": "dist", "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true, "esModuleInterop": true }, "include": [ "src" ] }
.prettierrc.json
{ "singleQuote": true }
アプリケーションはExpressを使って作成することにします。
$ npm i express $ npm i -D @types/node@v16 @types/express
依存関係は、このようになりました。
"devDependencies": { "@types/express": "^4.17.13", "@types/morgan": "^1.9.3", "@types/node": "^16.11.18", "prettier": "2.5.1", "typescript": "^4.5.4" }, "dependencies": { "express": "^4.17.2" }
scripts
はこのような定義にして、npm run build
でTypeScriptファイルをビルドできるようにしました。
"scripts": { "build": "tsc --project .", "build:watch": "tsc --project . --watch", "format": "prettier --write src" },
この時点では、PM2はインストールしません。Dockerイメージを作成する際に、グローバルにインストールします。
ソースコードはこちら。
src/app.ts
import express from 'express'; const app = express(); const address = process.env['LISTEN_ADDRESS'] ? process.env['LISTEN_ADDRESS'] : '0.0.0.0'; const port = process.env['LISTEN_PORT'] ? parseInt(process.env['LISTEN_PORT'], 10) : 3000; const logger = (fun: () => any) => { console.log(`[${new Date().toISOString()}] ${fun.call(null)}`); }; app.get('/', (req, res) => { logger(() => `access client[${req.ip}]`); res.json({ message: 'Hello World', }); }); const server = app.listen(port, address, () => logger(() => `server[${address}:${port}] startup.`) ); process.on('SIGINT', () => { logger(() => 'SIGINT signal received: closing HTTP server.'); server.close(() => { logger(() => 'HTTP server closed'); }); }); process.on('SIGTERM', () => { logger(() => 'SIGTERM signal received: closing HTTP server.'); server.close(() => { logger(() => 'HTTP server closed'); }); });
いくつか標準出力へのログ出力を行いつつ、Graceful Shutdownもこちらを見ながら入れておきました。
今回は2つのドキュメントに習い、SIGINT
とSIGTERM
をトラップするようにしています。
Health Checks and Graceful Shutdown
PM2に限って言えば、プロセス停止時にはSIGINT
が送信されます。
1度、動作確認しておきましょう。
ビルド。
$ npm run build
起動。
$ node dist/app.js [2022-01-04T10:43:49.704Z] server[0.0.0.0:3000] startup.
確認。
$ curl localhost:3000 {"message":"Hello World"}
この時のログ。
[2022-01-04T10:43:59.667Z] access client[127.0.0.1]
Ctrl-cで停止。
[2022-01-04T10:44:28.748Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:44:28.749Z] HTTP server closed
OKですね。
$ docker image build -t kazuhira/express-pm2:latest .
この時点ではPM2はまだ出てきていません。
Dockerイメージを作成する
次は、Dockerイメージを作成しましょう。
Dockerfile
は、こんな感じで作成。
Dockerfile
FROM node:16.13.1-bullseye as builder COPY src src COPY tsconfig.json tsconfig.json COPY package*.json ./ RUN npm ci && \ npm run build FROM node:16.13.1-bullseye RUN mkdir /app && \ useradd -m user && \ chown user:user /app USER user WORKDIR /app ENV NPM_CONFIG_PREFIX /home/user/node_modules ENV LISTEN_ADDRESS 0.0.0.0 ENV LISTEN_PORT 3000 ENV PATH /home/user/node_modules/bin:${PATH} EXPOSE 3000 COPY --from=builder --chown=user:user dist dist COPY --chown=user:user package*.json ./ RUN mkdir /home/user/node_modules RUN npm ci --production && \ npm i -g pm2@5.1.2 ENTRYPOINT ["pm2-runtime", "-i", "max", "dist/app.js"]
ポイントは、こんな感じでしょうか。
- ベースイメージは、Node.jsのオフィシャルイメージを利用
- マルチステージビルドを使い、TypeScriptファイルのビルドとステージを分離
- 実行時のコンテナには
devDependencies
は含めない
- 実行時のコンテナには
- ユーザーを
root
ではなく、一般ユーザーを作成してこちらを利用するように変更- 合わせて、npmパッケージのグローバルインストール先を変更
- PM2はグローバルインストール
Node.jsのオフィシャルイメージは、グローバルモジュールのインストール先が/usr/local/lib/node_modules
に
なっているので、一般ユーザーでnpm install -g
をしようとすると失敗します。
npm ERR! code EACCES npm ERR! syscall mkdir npm ERR! path /usr/local/lib/node_modules/pm2 npm ERR! errno -13 npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/pm2' npm ERR! [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/pm2'] { npm ERR! errno: -13, npm ERR! code: 'EACCES', npm ERR! syscall: 'mkdir', npm ERR! path: '/usr/local/lib/node_modules/pm2' npm ERR! } npm ERR! npm ERR! The operation was rejected by your operating system. npm ERR! It is likely you do not have the permissions to access this file as the current user npm ERR! npm ERR! If you believe this might be a permissions issue, please double-check the npm ERR! permissions of the file and its containing directories, or try running npm ERR! the command again as root/Administrator. npm ERR! A complete log of this run can be found in: npm ERR! /home/user/.npm/_logs/2022-01-04T09_52_14_306Z-debug.log
こちらを見て、修正。今回は/home/user/node_modules
としました。
Resolving EACCES permissions errors when installing packages globally | npm Docs
いったん、コンテナで利用可能なCPUの数だけプロセスを起動するようにしています。
ENTRYPOINT ["pm2-runtime", "-i", "max", "dist/app.js"]
では、ビルド。
$ docker image build -t kazuhira/express-pm2:latest .
起動してみます。
$ docker container run -it --rm --name app kazuhira/express-pm2:latest
特にリソース制限を入れていないので、8つのプロセスが起動しました(ホスト側のCPUが8個のため)。
2022-01-04T10:58:43: PM2 log: Launching in no daemon mode 2022-01-04T10:58:43: PM2 log: App [app:0] starting in -cluster mode- 2022-01-04T10:58:43: PM2 log: App [app:0] online 2022-01-04T10:58:43: PM2 log: App [app:1] starting in -cluster mode- 2022-01-04T10:58:43: PM2 log: App [app:1] online 2022-01-04T10:58:43: PM2 log: App [app:2] starting in -cluster mode- 2022-01-04T10:58:43: PM2 log: App [app:2] online 2022-01-04T10:58:43: PM2 log: App [app:3] starting in -cluster mode- 2022-01-04T10:58:43: PM2 log: App [app:3] online 2022-01-04T10:58:43: PM2 log: App [app:4] starting in -cluster mode- 2022-01-04T10:58:43: PM2 log: App [app:4] online 2022-01-04T10:58:43: PM2 log: App [app:5] starting in -cluster mode- 2022-01-04T10:58:43: PM2 log: App [app:5] online 2022-01-04T10:58:43: PM2 log: App [app:6] starting in -cluster mode- [2022-01-04T10:58:43.901Z] server[0.0.0.0:3000] startup. 2022-01-04T10:58:43: PM2 log: App [app:6] online 2022-01-04T10:58:43: PM2 log: App [app:7] starting in -cluster mode- [2022-01-04T10:58:43.971Z] server[0.0.0.0:3000] startup. [2022-01-04T10:58:43.977Z] server[0.0.0.0:3000] startup. 2022-01-04T10:58:43: PM2 log: App [app:7] online [2022-01-04T10:58:44.055Z] server[0.0.0.0:3000] startup. [2022-01-04T10:58:44.083Z] server[0.0.0.0:3000] startup. [2022-01-04T10:58:44.121Z] server[0.0.0.0:3000] startup. [2022-01-04T10:58:44.152Z] server[0.0.0.0:3000] startup. [2022-01-04T10:58:44.209Z] server[0.0.0.0:3000] startup.
動作確認。
$ APP_HOST=`docker container inspect app | jq -r '.[].NetworkSettings.IPAddress'` $ curl $APP_HOST:3000 {"message":"Hello World"}
OKですね。
この時のログ。
[2022-01-04T10:59:41.393Z] access client[172.17.0.1]
Ctrl-cで停止してみます。
こんな感じでアプリケーション自身の停止処理が動作した後、
[2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.609Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.611Z] HTTP server closed [2022-01-04T10:59:54.612Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.612Z] HTTP server closed 2022-01-04T10:59:54: PM2 log: Stopping app:app id:0 2022-01-04T10:59:54: PM2 log: Stopping app:app id:1 2022-01-04T10:59:54: PM2 log: Stopping app:app id:2 2022-01-04T10:59:54: PM2 log: Stopping app:app id:3 2022-01-04T10:59:54: PM2 log: Stopping app:app id:4 2022-01-04T10:59:54: PM2 log: Stopping app:app id:5 2022-01-04T10:59:54: PM2 log: Stopping app:app id:6 2022-01-04T10:59:54: PM2 log: Stopping app:app id:7 [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.714Z] HTTP server closed [2022-01-04T10:59:54.713Z] HTTP server closed [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.714Z] HTTP server closed [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.714Z] HTTP server closed [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.714Z] HTTP server closed [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.714Z] HTTP server closed [2022-01-04T10:59:54.713Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.714Z] HTTP server closed [2022-01-04T10:59:54.714Z] SIGTERM signal received: closing HTTP server. [2022-01-04T10:59:54.715Z] HTTP server closed
最終的にPM2も停止します。
2022-01-04T10:59:56: PM2 log: pid=31 msg=failed to kill - retrying in 100ms 2022-01-04T10:59:56: PM2 log: pid=24 msg=failed to kill - retrying in 100ms 2022-01-04T10:59:56: PM2 log: pid=17 msg=failed to kill - retrying in 100ms 2022-01-04T10:59:56: PM2 log: pid=82 msg=failed to kill - retrying in 100ms 2022-01-04T10:59:56: PM2 log: Process with pid 71 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: Process with pid 60 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: Process with pid 49 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: Process with pid 38 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: Process with pid 31 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: Process with pid 24 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: Process with pid 17 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: pid=82 msg=failed to kill - retrying in 100ms 2022-01-04T10:59:56: PM2 log: Process with pid 82 still alive after 1600ms, sending it SIGKILL now... 2022-01-04T10:59:56: PM2 log: App name:app id:2 disconnected 2022-01-04T10:59:56: PM2 log: App name:app id:1 disconnected 2022-01-04T10:59:56: PM2 log: App name:app id:3 disconnected 2022-01-04T10:59:56: PM2 log: App [app:1] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App [app:2] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App [app:3] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App [app:4] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App [app:5] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App [app:6] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App name:app id:4 disconnected 2022-01-04T10:59:56: PM2 log: App name:app id:5 disconnected 2022-01-04T10:59:56: PM2 log: App name:app id:6 disconnected 2022-01-04T10:59:56: PM2 log: App name:app id:0 disconnected 2022-01-04T10:59:56: PM2 log: App [app:0] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: App name:app id:7 disconnected 2022-01-04T10:59:56: PM2 log: App [app:7] exited with code [0] via signal [SIGKILL] 2022-01-04T10:59:56: PM2 log: pid=24 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=31 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=38 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=49 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=60 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=71 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=17 msg=process killed 2022-01-04T10:59:56: PM2 log: pid=82 msg=process killed 2022-01-04T10:59:56: PM2 log: PM2 successfully stopped
OKですね。
コンテナのCPUリソースを制限したい
と思ったのですが、PM2はホスト側のCPU数を見てしまうようです。
PM2 failed at detect cpu core count · Issue #4347 · Unitech/pm2 · GitHub
これは、Node.jsがそうだからみたいです。
Dockerコンテナ内で動作するNode.jsが認識するCPU数、メモリサイズは、ホスト側のものになるという話 - CLOVER🍀
ということは、PM2を使って起動するプロセス数はmax
ではなく具体的に指定した方が良さそうですね。
設定ファイルを使う
設定ファイルを使うパターンも試してみましょう。
Docker Integration / Starting a configuration file
こちらで作成。プロセス数は2にしました。
ecosystem.config.js
module.exports = { apps : [{ name : "express-app", script : "./dist/app.js", instances: 2, exec_mode: "cluster" }] }
Dockerfile
のCOPY
の部分と
COPY --from=builder --chown=user:user dist dist COPY --chown=user:user package*.json ./ COPY --chown=user:user ecosystem.config.js ecosystem.config.js
ENTRYPOINT
に反映します。
# ENTRYPOINT ["pm2-runtime", "-i", "max", "dist/app.js"] ENTRYPOINT ["pm2-runtime", "ecosystem.config.js"]
ビルド。
$ docker image build -t kazuhira/express-pm2:latest .
コンテナを起動すると、設定ファイルの内容を元にプロセスが起動されます。
$ docker container run -it --rm --name app --cpus 2 kazuhira/express-pm2:latest 2022-01-04T12:22:25: PM2 log: Launching in no daemon mode 2022-01-04T12:22:25: PM2 log: App [express-app:0] starting in -cluster mode- 2022-01-04T12:22:25: PM2 log: App [express-app:0] online 2022-01-04T12:22:25: PM2 log: App [express-app:1] starting in -cluster mode- 2022-01-04T12:22:25: PM2 log: App [express-app:1] online [2022-01-04T12:22:25.774Z] server[0.0.0.0:3000] startup. [2022-01-04T12:22:25.774Z] server[0.0.0.0:3000] startup.
ログ
最後に、ログを見てみます。pm2-runtime
にオプションを指定することで、「PM2で起動したアプリケーションの」
ログフォーマットを変えられるみたいです。
Docker Integration / Logging Format option
今回は、ENTRYPOINT
を使ってコンテナ内のプロセスを起動しているので、コンテナ実行時の引数にそのまま
オプションを指定すればOKです。
試しに、--json
を指定してみます。
$ docker container run -it --rm --name app --cpus 2 kazuhira/express-pm2:latest --json
こんな感じになりました。
2022-01-04T12:27:47: PM2 log: Launching in no daemon mode 2022-01-04T12:27:47: PM2 log: App [express-app:0] starting in -cluster mode- 2022-01-04T12:27:47: PM2 log: App [express-app:0] online 2022-01-04T12:27:47: PM2 log: App [express-app:1] starting in -cluster mode- {"timestamp":"2022-01-04T12:27:47.759Z","type":"process_event","status":"start","app_name":"express-app"} {"timestamp":"2022-01-04T12:27:47.766Z","type":"process_event","status":"online","app_name":"express-app"} 2022-01-04T12:27:47: PM2 log: App [express-app:1] online {"timestamp":"2022-01-04T12:27:47.791Z","type":"process_event","status":"start","app_name":"express-app"} {"timestamp":"2022-01-04T12:27:47.795Z","type":"process_event","status":"online","app_name":"express-app"} {"message":"[2022-01-04T12:27:47.959Z] server[0.0.0.0:3000] startup.","timestamp":"2022-01-04T12:27:47.960Z","type":"out","process_id":0,"app_name":"express-app"} {"message":"[2022-01-04T12:27:47.979Z] server[0.0.0.0:3000] startup.","timestamp":"2022-01-04T12:27:47.980Z","type":"out","process_id":1,"app_name":"express-app"} {"message":"[2022-01-04T12:27:54.605Z] access client[172.17.0.1]","timestamp":"2022-01-04T12:27:54.605Z","type":"out","process_id":0,"app_name":"express-app"}
PM2自身のログは、フォーマットを合わせてくれないみたいです…。
デフォルトは、--raw
みたいですね。
$ docker container run -it --rm --name app --cpus 2 kazuhira/express-pm2:latest --raw 2022-01-04T12:29:38: PM2 log: Launching in no daemon mode 2022-01-04T12:29:38: PM2 log: App [express-app:0] starting in -cluster mode- 2022-01-04T12:29:38: PM2 log: App [express-app:0] online 2022-01-04T12:29:38: PM2 log: App [express-app:1] starting in -cluster mode- 2022-01-04T12:29:38: PM2 log: App [express-app:1] online [2022-01-04T12:29:39.061Z] server[0.0.0.0:3000] startup. [2022-01-04T12:29:39.085Z] server[0.0.0.0:3000] startup. [2022-01-04T12:29:40.091Z] access client[172.17.0.1]
--format
だと、こんな感じになります。
$ docker container run -it --rm --name app --cpus 2 kazuhira/express-pm2:latest --format 2022-01-04T12:31:13: PM2 log: Launching in no daemon mode 2022-01-04T12:31:13: PM2 log: App [express-app:0] starting in -cluster mode- 2022-01-04T12:31:13: PM2 log: App [express-app:0] online 2022-01-04T12:31:13: PM2 log: App [express-app:1] starting in -cluster mode- 2022-01-04T12:31:13: PM2 log: App [express-app:1] online timestamp=2022-01-04-12:31:13+0000 app=express-app id=0 type=out message=[2022-01-04T12:31:13.874Z] server[0.0.0.0:3000] startup. timestamp=2022-01-04-12:31:13+0000 app=express-app id=1 type=out message=[2022-01-04T12:31:13.912Z] server[0.0.0.0:3000] startup. timestamp=2022-01-04-12:31:15+0000 app=express-app id=0 type=out message=[2022-01-04T12:31:15.448Z] access client[172.17.0.1]
設定ファイルに書く場合は(ドキュメントに書かれていませんが)、log_type
で指定するようです。
ecosystem.config.js
module.exports = { apps : [{ name : "express-app", script : "./dist/app.js", instances: 2, exec_mode: "cluster", log_type: "json" }] }
改行の扱いが微妙なので、オプションで指定した方が良いかもしれません…。
$ docker container run -it --rm --name app --cpus 2 kazuhira/express-pm2:latest 2022-01-04T12:40:42: PM2 log: Launching in no daemon mode 2022-01-04T12:40:42: PM2 log: App [express-app:0] starting in -cluster mode- 2022-01-04T12:40:42: PM2 log: App [express-app:0] online 2022-01-04T12:40:42: PM2 log: App [express-app:1] starting in -cluster mode- 2022-01-04T12:40:42: PM2 log: App [express-app:1] online {"message":"[2022-01-04T12:40:42.577Z] server[0.0.0.0:3000] startup.\n","timestamp":"2022-01-04T12:40:42.578Z","type":"out","process_id":0,"app_name":"express-app"} {"message":"[2022-01-04T12:40:42.616Z] server[0.0.0.0:3000] startup.\n","timestamp":"2022-01-04T12:40:42.616Z","type":"out","process_id":1,"app_name":"express-app"}
一応、PM2側のログフォーマットも変えられないか見てみましたが、こういうのなのでムリそうですね…。
console.log(`App [${env_copy.name}:${env_copy.pm_id}] starting in -cluster mode-`)
https://github.com/Unitech/pm2/blob/5.1.2/lib/God/ClusterMode.js#L36
オマケ: PM2をnpxで使わないのは?
最初はPM2をローカルインストールしてnpx
で起動しようと思い、こんな感じにしていたのですが。
ENTRYPOINT ["npx", "pm2-runtime", "-i", "max", "dist/app.js"]
これだと停止時にnpx
越しに止めることになり、npx
がエラーになります。
2022-01-04T09:50:12: PM2 log: PM2 successfully stopped npm ERR! path /app npm ERR! command failed npm ERR! signal SIGINT npm ERR! command sh -c pm2-runtime "-i" "max" "dist/app.js" npm ERR! A complete log of this run can be found in: npm ERR! /home/user/.npm/_logs/2022-01-04T09_50_12_797Z-debug.log
これが気持ち悪かったので、グローバルインストールに切り替えました…。
RUN npm ci --production && \
npm i -g pm2@5.1.2
まとめ
Dockerコンテナ内で、PM2を使う方法について調べてみました。
単に起動するだけだったらそんなに苦労しないのですが、作成するコンテナイメージやリソースの設定に踏み込んでみた
結果、予想以上に時間をかけて調べることになりましたが、まあ良いかなと…。
Dockerコンテナ内でPM2を使う時には、覚えておきましょう。