在 Dockerized Node.js 开发环境中处理 node_modules 的干净实用的方法?

Posted

技术标签:

【中文标题】在 Dockerized Node.js 开发环境中处理 node_modules 的干净实用的方法?【英文标题】:Clean AND practical way to handle node_modules in a Dockerized Node.js dev environment? 【发布时间】:2020-04-20 17:13:53 【问题描述】:

我最近一直在尝试对一个 MERN 堆栈应用程序进行 dockerize(用于开发和后来的生产),而 Node.js(尤其是 node_modules)和 Docker 之间的交互让我望而却步。在整个问题中,我将指定用于开发的计算机作为“主机”。

TL;DR

在开发时,有没有一种方法可以在不将主机的 node_modules 文件夹挂载到容器的情况下对 Node.js 应用进行 Dockerize 处理?

我尝试过的方法(以及为什么没有一个让我满意)

我可以想到使用 Docker(及其 docker-compose 实用程序)进行开发的 3 大优势(我将这些称为第 1、2 和 3 点):

    它使得在新机器上设置开发环境或在项目中集成新成员变得容易,因为除了 Docker 本身之外,您无需手动安装任何东西。 该应用程序易于在开发时运行以进行调试(只需快速docker-compose up 并且数据库、后端和前端都已启动并运行)。 运行时环境在所有机器和应用的最终生产环境中都是一致的,因为 Docker 容器是一种自己的小型 Linux 虚拟机。

在对 Node.js 应用程序进行 docker 化时,前 2 点没有问题;但是我觉得第三个在开发环境中更难实现,因为依赖项在 Node 中的工作方式及其 node_modules 的功能。让我解释一下:

这是我简化的项目文件夹结构:

project
│   docker-compose.yml
│
└───node-backend
│   │   Dockerfile
│   │   package.json
│   │   server.js
│   │
│   └───src
│   │   │   ...
│   │
│   └───node_modules
│       │   ...
│
└───react-frontend
    │   ...

根据我的尝试以及在互联网上的文章和教程中看到的内容,基本上有 3 种使用 Docker 开发 Node.js 的方法。 在所有 3 种方法中,我假设我使用以下 Dockerfile 来描述我的应用程序的图像:

# node-backend/Dockerfile
FROM node:lts-alpine
WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install

COPY . ./

EXPOSE 8000
CMD [ "npm", "start" ]

方法 1:快速而肮脏

开发时,将主机的整个代码文件夹(包括 node_modules)安装到容器中。 docker-compose.yml 文件通常如下所示(为清楚起见,不包括数据库或 React 应用配置):

# ./docker-compose.yml
version: "3"
services:
    backend:
        build: ./node-backend/
        ports:
            - 8000:8000
        volumes:
            - ./node-backend:/usr/src/app

这种方法在开发中最容易设置和使用:代码在主机和容器之间同步,热重载(例如使用 nodemon)有效,依赖关系在主机和容器之间同步(无需在主机上的每个npm install some-module 处重建容器)。

但是,它不遵守 第 3 点:由于主机的 node_modules 也安装到容器中,它们可能包含一些特定于平台的部分(例如 node-gyp 插件)是为您的主机操作系统(在我的情况下是 macOS 或 Windows)而不是为容器的操作系统(Alpine Linux)编译的。

方法 2:安装新的依赖项更简洁但烦人

挂载主机的源代码文件夹,但这次创建一个卷(命名或匿名)来保存容器的 node_modules,防止它们被主机的 node_modules 隐藏。

# ./docker-compose.yml
version: "3"
services:
    backend:
        build: ./node-backend/
        ports:
            - 8000:8000
        volumes:
            - ./node-backend:/usr/src/app
            - /usr/src/app/node_modules

通过这种方法,第 3 点现在得到尊重:我们确保容器在开发中使用的 node_modules 文件夹是专门为容器创建的,因此包含适当的平台特定代码。

但是,安装新的依赖项很痛苦:

要么你直接在容器中运行你的npm installs(使用docker exec),但是除非你每次都手动安装,否则依赖项不会安装在你的主机上。重要的是还要为 IDE(在我的情况下为 VSCode)在本地安装它们以提供自动完成、linting、避免“缺少模块”警告..​​.... 要么在主机上运行 npm installs,但每次都必须重新构建 Docker 映像,以便容器的 node_modules 是最新的,这非常耗时。

方法 3:可能是最干净的,但更难设置

最后的方法是直接容器内开发,这意味着不需要挂载主机,你只需要创建一个卷(这次我们命名它,但我认为匿名也可以工作?)以便对代码和 node_modules 的更改是持久的。我还没有尝试过这种方法,所以我不确定docker-compose.yml 文件会是什么样子,但可能是其中的一些内容:

# ./docker-compose.yml
version: "3"
services:
    backend:
        build: ./node-backend/
        ports:
            - 8000:8000
        volumes:
            - backend-data:/usr/src/app
volumes:
    backend-data:

这种方法也尊重第 3 点,但在容器内进行远程开发比在主机上进行常规开发更难设置(尽管 VSCode 显然是simplifies the process)。此外,源代码版本控制(ie 使用 Git)似乎有点烦人,因为您必须将主机的 SSH 标识传递到您的容器上才能允许它访问您的远程仓库.

结论

如您所见,我还没有找到一种结合了我正在寻找的所有优势的方法。我需要一种易于设置和在开发中使用的方法;尊重第 3 点,因为它是容器化理念和目的的一个重要方面;这不会使容器和主机之间的 node_modules 同步变得令人头疼,同时保留了 IDE 的所有功能(Intellisense、linting 等)。

有什么我完全想念的吗?对于这个问题,你们有什么解决办法?

【问题讨论】:

NodeJS + npm_modules + Docker 选项由 Bret Fisher 在 2019 年 Docker 博客中解释:docker.com/blog/keep-nodejs-rockin-in-docker 【参考方案1】:

我建议看看这个项目https://github.com/BretFisher/node-docker-good-defaults,它通过使用技巧同时支持本地和容器node_modules,但与某些框架(例如strapi)不兼容:

container node_modules 位于应用程序的上一级文件夹(用于解析 deps 的节点算法,递归查找应用程序的文件夹以查找 node_modules),并将它们从应用程序文件夹中删除 host node_modules 被放置在 app 文件夹中(那里没有变化) 你额外的bind-mount the package.json + lock文件专门在容器(记住从应用程序的一个文件夹)到主机(应用程序文件夹)之间,所以它们总是保持同步=>所以当你docker exec npm install dep时,你只需要npm install dep在主机上并最终重建您的 docker 映像。

【讨论】:

网址已更改为github.com/BretFisher/node-docker-good-defaults

以上是关于在 Dockerized Node.js 开发环境中处理 node_modules 的干净实用的方法?的主要内容,如果未能解决你的问题,请参考以下文章

《Node.js入门》Windows 7下Node.js Web开发环境搭建笔记

node.js + webstorm 开发个人博客:配置开发环境

node.js + webstorm :配置开发环境

Nodejs学习笔记--- 简介及安装Node.js开发环境

Nodejs学习笔记--- 简介及安装Node.js开发环境

vue.js:搭建开发环境及构建项目