如何在 monorepo 项目中使用 docker-compose 处理 node_modules

Tdy*_*Tdy 5 node.js docker docker-compose yarnpkg yarn-workspaces

我正在使用纱线工作区运行 Node.js monorepo 项目。文件结构如下所示:

workspace_root
    node_modules
    package.json
    apps
        appA
            node_modules
            package.json
        appB
            node_modules
            package.json
    libs
        libA
            dist
            node_modules
            package.json

Run Code Online (Sandbox Code Playgroud)

所有应用程序都是独立的,但它们都需要libA

我正在使用 docker-compose 运行所有这些应用程序。我的问题是如何正确处理所有依赖项,因为我不希望node_modules文件夹与主机同步。在本地,当我yarn install在工作区根目录下运行时,它会为所有项目安装所有依赖项,并填充不同的node_modules. 在 docker-compose 中,理想情况下,每个应用程序都不应该知道其他应用程序。

到目前为止,我的方法有效但并不理想,而且可扩展性不强。

version: "3.4"

services:
  # The core is in charge of installing dependencies for ALL services. Each service must for wait the core, and then
  # just do their job, not having to handle install.
  appA:
    image: node:14-alpine
    volumes: # We must load every volumes for install
        - .:/app  # Mount the whole workspace structure
        - root_node_modules:/app/node_modules
        - appA_node_modules:/app/apps/appA/node_modules
        - appB_node_modules:/app/apps/appB/node_modules
        - libA_node_modules:/app/libs/libA/node_modules
    working_dir: /app/apps/appA
    command: [sh, -c, "yarn install && yarn run start"]

  appB:
    image: node:14-alpine
    volumes: # We must load every volumes for install
        - .:/app  # Mount the whole workspace structure
        - root_node_modules:/app/node_modules
        - appB_node_modules:/app/apps/appB/node_modules
    working_dir: /app/apps/appB
    command: [sh, -c, "/scripts/wait-for-it.sh appA:4001  -- yarn run start"]

    # And so on for all apps....
  
volumes:
    root_node_modules:
        driver: local
    appA_node_modules:
        driver: local
    appB_node_modules:
        driver: local
    libA_node_modules:
        driver: local
Run Code Online (Sandbox Code Playgroud)

我看到的主要缺点:

  • 服务appA负责安装所有应用程序的依赖项。
  • 我必须为每个应用程序创建一个卷 + 为根 node_modules 创建一个卷
  • 整个项目安装在每个服务中,即使我只使用一个特定的文件夹

我想避免为开发而构建,因为每次添加依赖项时都必须进行构建,这非常麻烦并且会减慢您的速度

Ita*_*ish 0

附加在我创建的这个答案示例存储库的底部。

基本上利用纱线工作区,我为每个包/模块创建了一个通用的dockerfile,以便在构建时使用。

为每个 docker 镜像复制整个存储库(这对于以后发布产品来说不是一个好的做法,您可能希望为此创建一个不同的流程)

因此,如果将整个存储库安装到每个正在运行的服务,您可以观察库中的更改(在存储库中我已经配置了nodemon,因此它也将监视lib文件)

总结一下:

  1. 即使库发生变化也可以热重载,因为整个项目都安装到每个服务 docker 容器中
  2. 利用纱线工作区通过方便的命令轻松管理包
  3. 为了在每次更改时构建每个库,它们应该分别拥有一个由 docker-compose 引发的 docker 容器
  4. 对于任何与生产相关的流程(例如稍后发布 docker 镜像),开发过程并不是一个好的实践,因为所有存储库都在镜像中可用
  5. 将库添加为 docker 服务后,每个库都具有热重载功能,每次进行更改时它们都会重新构建,因此无需重复docker-compose build

无论如何,一旦库被解决并且更改不那么频繁,我就不会太担心重复,docker-compose build你会发现你的自我重建更少(但我也给出了解决方案)

Github 存储库示例