最近,我一直在尝试将MERN堆栈应用程序(用于开发和稍后用于生产)进行dockerize,而Node.js(尤其是node_modules)与Docker之间的交互让我望而却步。在整个问题中,我将用于开发的计算机指定为“主机”。
TL; DR
在开发时是否有一种在不将主机的node_modules文件夹安装到容器的情况下对Node.js应用程序进行Docker打包的方法不太可行?
我尝试的方法(以及为什么没有一个方法使我满意)
我可以想到使用Docker(及其docker-compose实用程序)进行开发的三大优势(我将这些优势称为 1、2和3点):
- 它使您可以轻松地在新机器上设置开发环境或将新成员集成到项目中,因为除了Docker本身之外,您无需手动安装任何东西。
- 该应用易于开发时调试运行(只需快速运行
docker-compose up
,数据库,后端和前端就可以启动并运行)。 - 由于Docker容器属于其自己的小型Linux虚拟机,因此运行时环境在所有计算机和应用程序的最终生产环境之间是一致的。
在对Node.js应用程序进行docker化时,前两点没有问题;但是我觉得在第三个环境中,由于依赖项在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 app配置):# ./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 install
(使用docker exec
),但是除非您每次手动进行手动操作,否则都不会在主机上安装依赖项。同样重要的是,还要在IDE本地安装它们(在我的情况下为VSCode),以提供自动补全,掉毛,避免“缺少模块”警告... - 您可以在主机上运行
npm install
,但是每次都必须重建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)。此外,源代码版本控制(使用Git的 ie )似乎有点烦人,因为您必须将主机的SSH标识传递到容器上,才能允许它访问远程仓库
结论
如您所见,我尚未找到一种结合了我所寻找的所有优点的方法。我需要一种易于设置和在开发中使用的方法;尊重第3点,因为这是集装箱化理念和宗旨的重要方面;在保留所有IDE功能(Intellisense,linting等)的同时,这不会使容器和主机之间的node_modules同步变得头疼。
我完全缺少什么东西吗?你们对这个问题有什么解决办法?