为什么我的预构建 Cloud Foundry 节点应用程序试图获取已经在 node_modules 中的模块?

Why is my pre-built cloud foundry node app trying to fetch modules that are already in node_modules?

我有一个预构建的节点应用程序,如果 npm install 在启动之前是 运行,则该应用程序不得尝试访问网络 - 目的是让一切都已经存在于 node_modules 目录。它正在部署在 cloud foundry 环境中。

这不是代理问题 - 在 Cloud Foundry 应用暂存期间不得尝试访问注册表 url。我正在寻找它为什么要这样做的想法。

当此应用程序部署到 cloud foundry 时,即使它在暂存期间检测到 node_modules 目录的存在,它仍会尝试检索基本依赖模块(如 @node/types)已经出现在 node_modules 中,当然它会在尝试访问这些环境中不允许的注册表时超时。有数百个其他依赖项cies 没有尝试检索,但出于某种原因它认为它确实需要一些模块。例如:

   2021-03-17T16:29:57.71-0700 [STG/0] OUT        Installing any new modules (package.json + package-lock.json)
   2021-03-17T16:32:31.78-0700 [STG/0] OUT npm ERR! code ECONNREFUSED
   2021-03-17T16:32:31.78-0700 [STG/0] OUT npm ERR! errno ECONNREFUSED
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR! FetchError: request to https://<registry-fqdn>/@types%2flong failed, reason: connect ECONNREFUSED 10.x.x.x:443
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at ClientRequest.<anonymous> (/tmp/contents784086672/deps/0/node/lib/node_modules/npm/node_modules/node-fetch-npm/src/index.js:68:14)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at ClientRequest.emit (events.js:315:20)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at TLSSocket.socketErrorListener (_http_client.js:469:9)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at TLSSocket.emit (events.js:315:20)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at emitErrorNT (internal/streams/destroy.js:106:8)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at emitErrorCloseNT (internal/streams/destroy.js:74:3)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at processTicksAndRejections (internal/process/task_queues.js:80:21)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!  FetchError: request to https://<registry-fqdn>/@types%2flong failed, reason: connect ECONNREFUSED 10.x.x.x:443
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at ClientRequest.<anonymous> (/tmp/contents784086672/deps/0/node/lib/node_modules/npm/node_modules/node-fetch-npm/src/index.js:68:14)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at ClientRequest.emit (events.js:315:20)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at TLSSocket.socketErrorListener (_http_client.js:469:9)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at TLSSocket.emit (events.js:315:20)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at emitErrorNT (internal/streams/destroy.js:106:8)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at emitErrorCloseNT (internal/streams/destroy.js:74:3)
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!     at processTicksAndRejections (internal/process/task_queues.js:80:21) {
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!   type: 'system',
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!   errno: 'ECONNREFUSED',
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!   code: 'ECONNREFUSED',
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!   parent: 'app'
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR! }
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR!
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR! If you are behind a proxy, please make sure that the
   2021-03-17T16:32:31.79-0700 [STG/0] OUT npm ERR! 'proxy' config is set properly.  See: 'npm help config'
   2021-03-17T16:39:12.54-0700 [STG/0] OUT npm ERR! A complete log of this run can be found in:
   2021-03-17T16:39:12.54-0700 [STG/0] OUT npm ERR!     /home/vcap/.npm/_logs/2021-03-17T23_32_31_800Z-debug.log
   2021-03-17T16:39:12.56-0700 [STG/0] OUT        **ERROR** Unable to build dependencies: exit status 1
   2021-03-17T16:39:13.07-0700 [STG/0] ERR Failed to compile droplet: Failed to run all supply scripts: exit status 14
   2021-03-17T16:39:13.09-0700 [STG/0] OUT Exit status 223
   2021-03-17T16:39:13.28-0700 [STG/0] OUT Cell 5cee670a-6f6c-4510-a274-5584f197038c stopping instance 59dda306-be2f-4d08-830c-77c08ffab3f5
   2021-03-17T16:39:13.28-0700 [STG/0] OUT Cell 5cee670a-6f6c-4510-a274-5584f197038c destroying container for instance 59dda306-be2f-4d08-830c-77c08ffab3f5
   2021-03-17T16:39:13.76-0700 [API/1] ERR Failed to stage build: staging failed

有什么想法吗?

编辑#1 其他事实:

  1. 应用程序作为 zip 存档推送
  2. zip 文件系统包含 node_modules 目录,该目录由 npm install 命令 运行 在构建 zip
  3. 的“构建”过程中生成
  4. zip 文件系统包括 package-lock.json(来自源)
  5. zip 存档中没有任何 .cfignore 文件
  6. zip 'build' 曾经是在 windows 机器上进行的,之前在随后推送到 cf
  7. 时没有这个问题
  8. zip 'build' 最近从 k8s 集群迁移到 gitlab ci 运行ner,这可能使用了 centos 派生的图像
  9. buildpack 版本为 1.7.32
  10. 构建机器上node(14.14.0)的版本与buildpack使用的版本匹配,但npm版本(7.6.1)不匹配(buildpack使用6.14.8)
  11. 将 zip 组装的构建映像更改为 Ubuntu Bionic Beaver (18.04.4 LTS) 没有任何区别

不能 100% 确定这会解决问题,但这是我经常看到的绊倒人们试图供应商 Node.js 依赖项的情况:

首先,查看文档中的说明:https://docs.cloudfoundry.org/buildpacks/node/index.html#vendoring

  1. 运行 npm install(你已经做到了)
  2. 确保您有一个 package-lock.json 文件(您可能有这个)。这会锁定要使用的版本,并且应该保证 node_modules/ 中的内容与将要安装的内容相匹配。如果你没有这个,你会看到来自 buildpack 的 Warning: package-lock.json not found. 输出。
  3. 确保您向上推送 package.jsonpackage-lock.json 和完整的 node_modules/ 目录,以及所有应用程序代码。

这些没有记录在案,但我观察到与其他人一起工作时的一些提示:

  1. 确保您没有 .cfignore 文件,因为这可能会意外导致 node_modules/ 无法推送。您通常可以判断 node_modules/ 是否未被推送,因为被推送的文件数量和大小将大大增加。如果 node_modules/ 目录不存在,您还会看到消息 It is recommended to vendor the application's Node.js dependencies

  2. 当您在本地 运行 npm install 时,您需要将其 运行 连接到 Ubuntu 仿生虚拟机或容器中。这是因为 NPM 通常会安装需要本机代码的模块。它会自动处理这个问题,但是 node_modules/ 中的内容是特定于 OS 和 运行 npm install 的架构的。因此,如果您在非 Ubuntu Bionic OS(例如 Windows 或 MacOS)上 运行 npm install,它将编译本机代码对于您的本地机器。当你向上推送时,它不会匹配,NPM 将尝试重新安装可以触发访问 Internet 的包。

  3. 确保您使用的是最新的 Node.js 构建包和 Node.js 版本。拥有包含错误修复的最新代码总是有帮助的。

  4. NODE_ENV=production 将由 buildpack 设置,这有时会导致行为差异。它还会跳过安装开发依赖项。不太可能是这里的问题,但值得一提,因为这会误导一些人。

这个问题原来是由生成 package-lock.json 文件的机器上的 npm 版本不匹配引起的,node_modules 目录被填充以包含在 zip 分发中的机器(npm v7.x),以及 cloud foundry 构建包使用的是什么 (npm v6.x)。

更改开发和构建机器以使用 npm v6.x 生成 package-lock.json 文件并填充 node_modules 目录导致成功销售 nodejs 应用程序。