繁体   English   中英

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

[英]How to handle node_modules with docker-compose in a monorepo project

我正在使用 yarn 工作区运行一个 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

所有应用程序都是独立的,但它们都需要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

我看到的主要缺点:

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

我想避免为开发而构建,因为每次添加依赖项时都必须完成它,这很麻烦而且会减慢你的速度

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

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

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

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

总结一下:

  1. 即使库正在更改,也因为整个项目已安装到每个服务 docker 容器而热重载
  2. 利用纱线工作空间通过便捷的命令轻松管理包
  3. 为了在每次更改时构建每个库,它们应该分别拥有一个由 docker-compose 提出的 docker 容器
  4. 开发过程对于任何与生产相关的过程都不是一个好的做法,例如稍后发布 docker 映像,因为映像中的所有存储库都可用
  5. 将库添加为 docker 服务后,每个服务都带有热重载,每次您进行更改时都会重新构建它们,因此无需重复docker-compose build

无论如何,一旦图书馆安顿下来并且更改频率降低,我就不会担心重复的docker-compose build ,你会发现你的自我重建更少(但我也给出了解决方案的任何方式)

Github 存储库示例

我相信在你的情况下,你应该做的最好的事情是构建你自己的 Docker 图像而不是使用节点中的图像。 所以,让我们做一些编码。 首先,您应该告诉 Docker 忽略 node_modules 文件夹。 为此,您需要为每个应用程序创建一个 .dockerignore 和一个 Dockerfile。 因此,您的结构可能如下所示:

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

在 .dockerignore 文件中,您可以在下面重复相同的值。

node_modules/
dist/

这将使 docker 在构建过程中忽略这些文件夹。 现在是 Dockerfile 本身。 因此,为了确保您的项目在容器内正常运行,最佳做法是在容器内构建项目,而不是在容器外。 它避免了许多“在我的电脑上工作正常”的问题。 也就是说,Dockerfile 的一个示例可能是这样的:

# build stage
FROM node:14-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY prod_nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

在那种情况下,我也使用了 nginx,以确保用户通过适当的网络服务器访问容器。 最后我也会让 prod_nginx.conf。 但这里的要点是,您可以只构建该图像并将其发送到 dockerhub,然后从那里在您的 docker-compose.yml 中使用它,而不是使用原始节点图像。

Docker-compose.yml 会是这样的:

version: "3.4"

services:
  appA:
    image: mydockeraccount/appA
    container_name: container-appA
    port: 
      - "8080:80"
    ....

现在,正如承诺的那样,prod_nginx.conf

user                    nginx;
worker_processes        1;
error_log               /var/log/nginx/error.log warn;
pid                     /var/run/nginx.pid;
events {
    worker_connections  1024;
}

http {
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    log_format          main '$remote_addr - $remote_user [$time_local] "$request" '
                             '$status $body_bytes_sent "$http_referer"'
                             '"$http_user_agent" "$http_x_forwarded_for"';
    access_log          /var/log/nginx/access.log main;
    sendfile            on;
    keepalive_timeout   65;
    server {
        listen          80;
        server_name     _ default_server;
        index           index.html;
        location / {
            root        /usr/share/nginx/html;
            index       index.html;
            try_files   $uri $uri/ /index.html;
        }
    }
}

希望能帮助到你。 最好的祝福。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM