【问题标题】:Running NestJS in PM2 cluster mode when building a deployment with Yarn PNP使用 Yarn PNP 构建部署时以 PM2 集群模式运行 NestJS
【发布时间】:2020-12-21 16:21:43
【问题描述】:

从 npm 迁移到 yarn pnp

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

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

PM2

使用 PM2,您可以在 fork 或 cluster 模式下运行您的应用程序。
在同一个端口上运行您的应用程序时,您需要使用cluster 模式。
Fork 模式在启动第一个分叉后一直说端口已在使用中(这是完全合法的)

由于我们使用的是 yarn2,因此我们只能使用 yarn 作为解释器来运行我们的应用,方法是:

yarn node ./build/main.js

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

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

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

我们将部署交付到具有多个 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 应用程序?

【问题讨论】:

  • 修复问题:package.json 在 package.json 中添加 "pm2:start": "yarn pm2 start ecosystem.config.js" ecosystem.config.js 删除 interpreter 和 @987654332 @ 并且只需添加 javascipt 文件以在 script 中执行。所以现在你可以运行yarn:pm2start
  • ...yarn pm2:start

标签: gitlab nestjs yarnpkg pm2


【解决方案1】:

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 中启动您的应用

【讨论】:

    【解决方案2】:

    更新

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

    生态系统.config.js:

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

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

    【讨论】:

      猜你喜欢
      • 2022-01-06
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-05
      • 1970-01-01
      • 2021-11-19
      相关资源
      最近更新 更多