运行 使用 Yarn PNP 构建部署时 NestJS 处于 PM2 集群模式

Running NestJS in PM2 cluster mode when building a deployment with Yarn PNP

从 npm 迁移到 yarn pnp

几个月前,我们开始为我们的 monorepo 使用 yarn2 (pnpify),因为 node_modules 确实增长到 200K 个包。感谢 yarn2,我们将所有包的构建和部署时间从 40 分钟减少到 4-5 分钟,这真的很棒。

前端包很容易进行 tree-shaking 和捆绑,以创建一个小工件并上传到存储容器。
后端包(Nestjs Rest 和 GraphQl API's)为了 运行 在集群模式下使用 pm2 构建有点棘手。

PM2

使用 PM2,您可以 运行 您的应用处于 forkcluster 模式。
当运行在同一端口上连接您的应用程序时,您需要使用cluster模式。
Fork 模式在启动第一个 fork 后一直说端口已被使用(完全合法)

因为我们使用的是 yarn2,所以我们只能 运行 我们的应用程序使用 yarn 作为解释器,方法是 运行 将其设置为:

yarn node ./build/main.js

为了获得正确的模块解析,因为节点不理解它。

问题来了:
yarn(和 npm)在集群模式下表现不佳。
这是因为您需要使用节点本身作为解释器而不是纱线(或 npm)
所以我们最终得到以下 ecosystem.config.js

{
    "apps": [
        {
            "args": "node ./build/main.js",
            "exec_mode": "cluster",
            "instances": "max",
            "interpreter": "bash",
            "name": "api",
            "script": "yarn",
            "time": true
        }
    ]
}

我们将部署转移到具有 1 个以上 CPU 核心的 VM,并使用新的生态系统重新加载 pm2 服务。一切都是绿色的,但我们注意到只有 1 个进程实际上在监听端口 3000,所有其他进程确实抛出了 EADDRINUSE 错误。 yarn 发出错误,而不是抛出错误,因此 PM2 认为应用程序仍然存在。
或者至少,这是我们的结论...

捆绑 NestJS 是不行的:bundled-nest

我唯一的解决方案是通过执行以下操作对 nestjs 本身进行集群化:

import { Injectable } from '@nestjs/common';
import { fork, isMaster, on } from 'cluster';
import * as os from 'os';

const numCPUs = os.cpus().length;
// const randomNumber = (min: number, max: number) =>
//   Math.floor(Math.random() * max) + min;

@Injectable()
export class ClusterService {
  // eslint-disable-next-line @typescript-eslint/ban-types
  static clusterize(callback: Function): void {
    if (isMaster) {
      // eslint-disable-next-line no-plusplus
      for (let i = 0; i < numCPUs; i++) {
        fork();
      }

      on('exit', (worker, code) => {
        fork();
        // eslint-disable-next-line no-console
        console.log(
          `[Cluster] worker[${worker.process.pid}] died with status: [${code}], creating new worker`,
        );
      });
    } else {
      callback();
    }
  }
}

和 运行 PM2 在单个实例上,但这感觉有点古怪,因为 PM2 可以为你做这件事......以更好的方式。

有没有办法用 yarn2 “弹出”node_modules,这样我们就可以 运行 应用程序作为一个真正的节点进程?
有没有办法在使用 yarn 作为解释器的同时在同一端口上以集群模式 运行 PM2?
如何在 yarn2 中抛出错误而不是发出错误以便 PM2 将创建一个新进程?

... 或者是否有另一种解决方案可以在 gitlab 中仅使用 npm 而不必等待 40 分钟来构建包和 运行 带有节点解释器 n pm2 的 nestjs 应用程序?

yarn exec pm2 start ecosystem.config.js并且不保存pm2进程。 pm2只能在npm模式下复活,不会在yarn模式下执行你的脚本

缺点是您无法使用 pm2 启动在从 pm2 重新启动后仍然存在。所以你需要自己实现一个systemd服务,它会在启动时执行yarn pm2:start

2 种可能的特征解决方案:

  • yarn2 将能够在构建步骤后弹出 .yarn 缓存以重新创建 node_modules 以便在 npm 中启动您的应用程序。
  • pm2 将添加一项功能以结合集群模式在 yarn 而不是 npm 中启动您的应用程序

更新

您不需要 yarn 来 运行 具有 yarn 模块分辨率的应用程序。
您唯一需要做的就是在使用节点作为解释器 运行 连接应用程序时要求 .pnp.js。 将 "interpreter_args": "--require /path/to/.pnp.js", 添加到您的 pm2 配置

ecosystem.config.js:

{
  "apps": [
    {
      "name": "my-app",
      "script": "./main.js",
      "interpreter_args": "--require /path/to/.pnp.js",
      "exec_mode": "cluster"
     }
  ]
}

现在您可以执行 pm2 start ecosystem.config.js 而不会遇到有关未找到节点模块的任何错误。