CLOVER🍀

That was when it all began.

Dockerコンテナ内で、PM2を䜿っおアプリケヌションを起動する

これは、なにをしたくお曞いたもの

前に、PM2を䜿っおNode.jsアプリケヌションのクラスタヌ化をしおみたした。

PM2を使って、Node.jsアプリケーションをクラスター化(CPUスケーリング)させてみる - CLOVER🍀

今床は、Dockerコンテナ内でPM2を䜿い、アプリケヌションをCPUスケヌリングできるようにしたいず思いたす。

Dockerコンテナ内でPM2を䜿う

PM2の方にドキュメントがありたす。

PM2 - Docker Integration

簡単に蚀うず、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

Launch PM2 in no deamon

あず、Graceful Shutdownに぀いおも芋おおいた方がよいでしょう。

PM2 - Graceful Start/Shutdown

ちなみに、PM2自䜓のDockerむメヌゞもあるみたいですが、今回はパスしたす。

DockerHub / keymetrics/pm2

環境

今回の環境は、こちら。

$ 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をトラップするようにしおいたす。

PM2 - Graceful Start/Shutdown

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を䜿う時には、芚えおおきたしょう。