我在Docker容器中安装node_modules
并将它们与主机同步时遇到问题。我的Docker的版本为18.03.1-ce, build 9ee9f40
,而Docker Compose的版本为1.21.2, build a133471
。
我的docker-compose.yml
如下:
# Frontend Container.
frontend:
build: ./app/frontend
volumes:
- ./app/frontend:/usr/src/app
- frontend-node-modules:/usr/src/app/node_modules
ports:
- 3000:3000
environment:
NODE_ENV: ${ENV}
command: npm start
# Define all the external volumes.
volumes:
frontend-node-modules: ~
我的Dockerfile
:
# Set the base image.
FROM node:10
# Create and define the working directory.
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
# Install the application's dependencies.
COPY package.json ./
COPY package-lock.json ./
RUN npm install
许多博客文章和Stack Overflow答案中都描述了与外部卷有关的技巧。例如,this one。
该应用程序运行良好。源代码已同步。热装也很好。
我唯一的问题是node_modules
文件夹在主机上为空。可以将Docker容器内的node_modules
文件夹与主机同步吗?
我已经阅读了以下答案:
不幸的是,他们对我没有太大帮助。我不喜欢the first one,因为由于跨平台问题(例如主机是Windows或Mac,而Docker容器是Debian 8),我不想在主机上运行npm install
或Ubuntu 16.04)。 The second one对我也不利,因为我想在npm install
中运行Dockerfile
而不是在启动Docker容器之后运行它。
我还找到了this blog post。作者试图解决我面临的同样问题。问题是node_modules
不会同步,因为我们只是将它们从Docker容器复制到主机。
我希望将Docker容器中的node_modules
与主机同步。请考虑到我想要的:
node_modules
而不是手动安装node_modules
安装在Docker容器而不是主机中node_modules
与主机同步(如果我在Docker容器中安装了一些新软件包,则应自动与主机同步,而无需任何手动操作)我需要在主机上放置node_modules
,因为:
node_modules
才能在本地安装,以便它可以访问devDependencies
或eslint
之类的prettier
。我不想在全球安装这些devDependencies
。谢谢。
答案 0 :(得分:16)
首先,我要感谢David Maze和trust512发布他们的答案。不幸的是,他们没有帮助我解决我的问题。
我想发表这个问题的答案。
我的docker-compose.yml
:
---
# Define Docker Compose version.
version: "3"
# Define all the containers.
services:
# Frontend Container.
frontend:
build: ./app/frontend
volumes:
- ./app/frontend:/usr/src/app
ports:
- 3000:3000
environment:
NODE_ENV: development
command: /usr/src/app/entrypoint.sh
我的Dockerfile
:
# Set the base image.
FROM node:10
# Create and define the node_modules's cache directory.
RUN mkdir /usr/src/cache
WORKDIR /usr/src/cache
# Install the application's dependencies into the node_modules's cache directory.
COPY package.json ./
COPY package-lock.json ./
RUN npm install
# Create and define the application's working directory.
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
最后但并非最不重要的entrypoint.sh
:
#!/bin/bash
cp -r /usr/src/cache/node_modules/. /usr/src/app/node_modules/
exec npm start
这里最棘手的部分是将node_modules
安装到node_module
中定义的/usr/src/cache
的缓存目录(Dockerfile
)中。之后,entrypoint.sh
将node_modules
从缓存目录(/usr/src/cache
)移到我们的应用程序目录(/usr/src/app
)。因此,整个node_modules
目录将出现在我们的主机上。
看着上面我想要的问题:
- 自动安装
node_modules
而不是手动安装- 将
node_modules
安装在Docker容器而不是主机中- 使
node_modules
与主机同步(如果我在Docker容器中安装了一些新软件包,则应该 自动与主机同步,无需任何手动操作
第一件事已完成:node_modules
已自动安装。第二件事也完成了:node_modules
已安装在Docker容器内(因此,不会出现跨平台问题)。第三件事也完成了:安装在Docker容器中的node_modules
在我们的主机上将可见,并且将它们同步!如果我们在Docker容器中安装了一些新软件包,它将立即与我们的主机同步。
需要注意的重要事情:确实,安装在Docker容器中的新软件包将出现在/usr/src/app/node_modules
中。由于此目录已与我们的主机同步,因此此新软件包也将出现在我们主机的node_modules
目录中。但是/usr/src/cache/node_modules
在这一点上将具有旧版本(没有此新包)。无论如何,这对我们来说不是问题。在下一个docker-compose up --build
(需要--build
)期间,Docker将重新安装node_modules
(因为package.json
已更改),并且entrypoint.sh
文件会将它们移至我们的/usr/src/app/node_modules
。
您应该考虑另一件事。如果您git pull
来自远程存储库的代码,或者git checkout your-teammate-branch
来自Docker运行时的代码,则可能有一些新软件包添加到package.json
文件中。在这种情况下,您应该使用CTRL + C
停止Docker,然后使用docker-compose up --build
重新启动Docker(需要--build
)。如果您的容器作为守护程序运行,则只需执行docker-compose stop
即可停止容器,并使用docker-compose up --build
重新启动容器(需要--build
)。
如果您有任何疑问,请在评论中让我知道。
希望这会有所帮助。
答案 1 :(得分:6)
遇到此问题并发现接受的答案非常慢,无法在每次运行的容器中将所有node_modules
复制到主机上,我设法通过在容器中安装依赖项,镜像主机卷和如果存在node_modules
文件夹,请再次跳过安装:
Dockerfile:
FROM node:12-alpine
WORKDIR /usr/src/app
CMD [ -d "node_modules" ] && npm run start || npm ci && npm run start
docker-compose.yml:
version: '3.8'
services:
service-1:
build: ./
volumes:
- ./:/usr/src/app
当您需要重新安装依赖项时,只需删除node_modules
。
答案 2 :(得分:4)
这里发生了三件事:
docker build
或docker-compose build
时,您的Dockerfile会构建一个新映像,其中包含一个/usr/src/app/node_modules
目录和一个Node安装,但没有其他内容。特别是,您的应用程序不在内置映像中。docker-compose up
时,volumes: ['./app/frontend:/usr/src/app']
指令将隐藏/usr/src/app
中的所有内容,并将主机系统内容安装在其上。volumes: ['frontend-node-modules:/usr/src/app/node_modules']
指令将命名卷安装在node_modules
树的顶部,隐藏相应的主机系统目录。如果要启动另一个容器并将其连接到该容器,我希望您会在其中看到node_modules
树。对于您要描述的内容,您只是不想要指定的卷:从volumes:
块和volumes:
文件末尾的docker-compose.yml
部分删除第二行。>
答案 3 :(得分:2)
我不建议重叠的卷,尽管我还没有看到任何官方文档禁止它,但是过去我对此有一些疑问。我是怎么做的:
可以通过稍微缩短撰写文件来实现以上目的:
frontend:
build: ./app/frontend
volumes:
- ./app/frontend:/usr/src/app
ports:
- 3000:3000
environment:
NODE_ENV: ${ENV}
command: npm start
这意味着您可能需要两个Dockerfile-一个用于本地开发,一个用于部署胖映像,其中所有应用程序dist文件都分层放置在其中。
也就是说,考虑开发Dockerfile:
FROM node:10
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN npm install
以上内容使应用程序创建完整的node_modules安装并将其映射到您的主机位置,而docker-compose指定的命令将启动您的应用程序。
答案 4 :(得分:2)
没有人提到实际使用docker的entrypoint
功能的解决方案。
这是我的工作解决方案:
Dockerfile(多阶段构建,因此它既可以生产也可以本地开发):
FROM node:10.15.3 as production
WORKDIR /app
COPY package*.json ./
RUN npm install && npm install --only=dev
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
FROM production as dev
COPY docker/dev-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["dev-entrypoint.sh"]
CMD ["npm", "run", "watch"]
docker / dev-entrypoint.sh:
#!/bin/sh
set -e
npm install && npm install --only=dev ## Note this line, rest is copy+paste from original entrypoint
if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ]; then
set -- node "$@"
fi
exec "$@"
docker-compose.yml:
version: "3.7"
services:
web:
build:
target: dev
context: .
volumes:
- .:/app:delegated
ports:
- "3000:3000"
restart: always
environment:
NODE_ENV: dev
通过这种方法,您可以达到所需的所有3个点,而且恕我直言,这是一种更简洁的方法-无需移动文件。
答案 5 :(得分:1)
感谢Vladyslav Turak对entrypoint.sh
的回答,我们在此将node_modules
从容器复制到主机。
我实现了类似的操作,但是遇到了沙哑的@ commitlint,tslint npm软件包的问题。
我什么都不能推入仓库。
原因:我将node_modules
从Linux复制到Windows。在我的情况下,<5%的文件是不同的(.bin和大多数package.json),而95%的文件是相同的。 example: image with diff
因此,我首先返回了npm install
中的node_modules
的解决方案,用于Windows(用于IDE和调试)。 Docker映像将包含Linux版本的node_modules
。
答案 6 :(得分:1)
我知道这已解决,但是呢:
Dockerfile:
ArrayList
docker-composer.yml:
FROM node
# Create app directory
WORKDIR /usr/src/app
# Your other staffs
EXPOSE 3000
volumes / app / package.json
version: '3.2'
services:
api:
build: ./path/to/folder/with/a/dockerfile
volumes:
- "./volumes/app:/usr/src/app"
command: "npm start"
运行后,node_modules将存在于您的卷中,但是其内容是在容器内生成的,因此不会出现跨平台问题。
答案 7 :(得分:1)
您提到的将主机node_modules文件夹与容器node_modules绑定不是一个好习惯。我已经看到了为此文件夹创建内部卷的解决方案。不这样做会在构建阶段引起问题。
当我尝试为有角度的应用程序构建docker开发环境时遇到了这个问题,当我编辑主机文件夹中的文件时会显示tslib错误,这是因为主机的node_modules文件夹为空(符合预期)。 / p>
在这种情况下,对我有帮助的廉价解决方案是使用名为“ Remote-Containers” 的Visual Studio代码扩展。
此扩展名将使您可以将 Visual Studio代码附加到容器,并透明地编辑容器文件夹中的文件。为此,它将在开发容器内安装内部vscode服务器。有关更多信息,请检查this link。
但是,请确保您的卷仍在docker-compose.yml文件中创建。
我希望它对您有帮助:D!
答案 8 :(得分:1)
您可以使用外部命名卷技巧在容器中安装 node_modules,并通过将卷的存储位置配置为指向主机的 node_modules 目录来将其与主机同步。这可以通过使用 volume 和 local driver 的命名 bind mount 来完成,如下例所示。
无论如何,卷的数据都存储在您的主机上,例如 /var/lib/docker/volumes/,因此我们只是将其存储在您的项目中。
要在 Docker Compose 中执行此操作,只需将您的 node_modules 卷添加到您的前端服务,然后在命名卷部分中配置该卷,其中“设备”是相对路径(来自docker-compose.yml 的位置)到本地(主机)node_modules 目录。
docker-compose.yml
version: '3.9'
services:
ui:
# Your service options...
volumes:
- node_modules:/path/to/node_modules
volumes:
node_modules:
driver: local
driver_opts:
type: none
o: bind
device: ./local/path/to/node_modules
此解决方案的关键是永远不要直接在主机 node_modules 中进行更改,而是始终在容器中安装、更新或删除 Node 包。
文档:
答案 9 :(得分:0)
我不确定您为什么要让源代码驻留在容器中,而 主机和在开发过程中相互绑定安装。通常,您希望源代码驻留在容器中以进行部署,而不是用于开发,因为源代码可在主机上使用并绑定安装。
您的docker-compose.yml
frontend:
volumes:
- ./app/frontend:/usr/src/app
您的Dockerfile
FROM node:10
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
当然,每次package.json更改时,您都必须第一次运行npm install
,但是您必须在容器中运行它,所以不会出现跨平台问题:docker-compose exec frontend npm install
最后启动服务器docker-compose exec frontend npm start
然后,通常在以部署为目标的CI管道中,您将复制完整的源代码并重新安装node_modules来构建最终映像,但是当然,此时您不再需要绑定安装和“同步” ,因此您的设置应如下所示:
docker-compose.yml
frontend:
build:
context: ./app/frontend
target: dev
volumes:
- ./app/frontend:/usr/src/app
Dockerfile
FROM node:10 as dev
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
FROM dev as build
COPY package.json package-lock.json ./
RUN npm install
COPY . ./
CMD ["npm", "start"]
随后,您可以手动或在管道中将Dockerfile的构建阶段作为目标,以构建可部署的映像。
我知道这不是对您问题的确切答案,因为您必须运行npm install并且在开发过程中容器中没有任何内容,但是它解决了您的node_modules问题,并且我觉得您的问题混合了开发和部署方面的考虑,因此也许您以错误的方式想到了这个问题。
答案 10 :(得分:0)
我的解决方法是在容器启动时而不是在构建时安装依赖项。
# We're using a multi-stage build so that we can install dependencies during build-time only for production.
# dev-stage
FROM node:14-alpine AS dev-stage
WORKDIR /usr/src/app
COPY package.json ./
COPY . .
# `yarn install` will run every time we start the container. We're using yarn because it's much faster than npm when there's nothing new to install
CMD ["sh", "-c", "yarn install && yarn run start"]
# production-stage
FROM node:14-alpine AS production-stage
WORKDIR /usr/src/app
COPY package.json ./
RUN yarn install
COPY . .
在node_modules
运行.dockerignore
时将Dockerfile
添加到COPY . .
以防止其被复制。我们使用卷来引入node_modules
。
**/node_modules
node_app:
container_name: node_app
build:
context: ./node_app
target: dev-stage # `production-stage` for production
volumes:
# For development:
# If node_modules already exists on the host, they will be copied
# into the container here. Since `yarn install` runs after the
# container starts, this volume won't override the node_modules.
- ./node_app:/usr/src/app
# For production:
#
- ./node_app:/usr/src/app
- /usr/src/app/node_modules