在Docker容器中安装node_modules并将它们与host同步

Vla*_*rak 34 npm node-modules docker docker-compose

我有在node_modulesDocker容器内安装并与主机同步的问题.我的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: ~
Run Code Online (Sandbox Code Playgroud)

我的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
Run Code Online (Sandbox Code Playgroud)

许多博客文章和Stack Overflow答案都描述了外部卷的技巧.例如,这一个.

该应用程序运行良好.源代码是同步的.热重装也很有效.

我唯一的问题是该node_modules文件夹在主机上是空的.是否可以将node_modulesDocker容器内的文件夹与主机同步?

我已经读过这些答案:

  1. docker-compose在node_modules上的卷但是为空
  2. 在Docker中安装npm之后访问node_modules

不幸的是,他们并没有帮助我很多.我不喜欢第一个,因为我不想npm install在我的主机上运行,因为可能存在跨平台问题(例如主机是Windows或Mac,Docker容器是Debian 8或Ubuntu 16.04).第二个是对我不好过,因为我想运行npm install在我Dockerfile,而不是泊坞容器运行后它已启动.

另外,我发现了这篇博文.作者试图解决我面临的同样问题.问题是node_modules不会同步,因为我们只是将它们从Docker容器复制到主机.

我希望我node_modules的Docker容器内部与主机同步.请考虑我想要:

  • node_modules自动安装而不是手动安装
  • 安装node_modules在Docker容器内而不是主机中
  • 已经node_modules与主机同步(如果我安装多克尔容器内的一些新的包装,应自动与主机,而无需任何手动操作同步)

我需要node_modules在主机上,因为:

  • 在我需要的时候阅读源代码的可能性
  • IDE需要node_modules在本地安装,以便它可以访问devDependencies诸如eslintprettier.我不想devDependencies全局安装这些.

提前致谢.

Vla*_*rak 32

首先,我要感谢David Mazetrust512发布他们的答案.不幸的是,他们没有帮助我解决我的问题.

我想发表我对这个问题的回答.

我的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
Run Code Online (Sandbox Code Playgroud)

我的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
Run Code Online (Sandbox Code Playgroud)

最后但并非最不重要entrypoint.sh:

#!/bin/bash

cp -r /usr/src/cache/node_modules/. /usr/src/app/node_modules/
exec npm start
Run Code Online (Sandbox Code Playgroud)

这里最棘手的部分是安装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与主机(同步,如果我安装多克尔容器内的一些新的包装,应自动与主机无需任何手动操作同步

第一件事就是完成:node_modules自动安装.第二件事也是这样做的:node_modules安装在Docker容器内(因此,不存在跨平台问题).第三件事也是这样做的:node_modules安装在Docker容器中的东西将在我们的主机上可见,它们将被同步!如果我们在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-branchDocker运行时,可能会有一些新的软件包添加到该package.json文件中.在这种情况下,您应该CTRL + C使用docker-compose up --build(--build需要)停止Docker 并再次使用它.如果您的容器作为守护程序运行,您应该执行docker-compose stop以停止容器并再次使用docker-compose up --build(--build需要).

如果您有任何疑问,请在评论中告诉我.

希望这可以帮助.

  • 使用 rsync 代替 cp 可以节省时间。如果您之前已经复制了node_modules,则该命令几乎会立即运行。例如,在我的脚本中运行:`rsync -arv /home/app/node_modules /tmp/node_modules` (4认同)
  • `RUN mkdir /usr/src/cache` 是不必要的,因为 `WORKDIR /usr/src/cache` 创建了所需的目录 (4认同)
  • 这个解决方案对我来说效果很好。非常感谢,我已经搜索了很长时间。谢谢! (3认同)
  • 谢谢你这对我来说效果很好!小提示,由于我尚未生成“package-lock.json”,因此在构建过程中生成了错误。无需在“Dockerfile”中使用两行单独的行来复制“package-lock.json”和“package.json”,而是可以轻松地使用一行“COPY package*.json ./”来覆盖两者或其中之一。 (3认同)
  • 对于这个“问题”,官方有什么办法吗?我正在寻找一种干净的方法,将所有工作留给容器,并具有从容器到主机的一些同步机制,以便能够从本地(主机)使用 prettier、git、eslint。 (2认同)
  • 您好,我尝试了您的解决方案,我得到 `/usr/local/bin/docker-entrypoint.sh: exec: line 8: /usr/src/app/entrypoint.sh: not find`。 (2认同)

Jer*_*M4n 14

简单、完整的解决方案

您可以使用外部命名卷技巧在容器中安装node_modules ,并通过将卷的存储位置配置为指向主机的node_modules目录来将其与主机同步。这可以通过使用本地驱动程序绑定安装的命名来完成,如下面的示例所示。

卷的数据无论如何都存储在您的主机上,例如 /var/lib/docker/volumes/ 中,因此我们只是将其存储在您的项目中。

要在 Docker Compose 中执行此操作,只需将 node_modules 卷添加到前端服务,然后在命名卷部分中配置该卷,其中“device”是相对路径来自 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
Run Code Online (Sandbox Code Playgroud)

此解决方案的关键是永远不要直接在主机的 node_modules 中进行更改,而是始终在容器中安装、更新或删除 Node 包。

版本控制提示: 当您的 Node package.json/package-lock.json 文件发生更改时,无论是在拉取或切换分支时,除了重建 Image 之外,您还必须删除 Volume 并删除其内容:

docker volume rm example_node_modules
rm -rf local/path/to/node_modules
mkdir local/path/to/node_modules
Run Code Online (Sandbox Code Playgroud)

文档:


lew*_*lbr 11

遇到这个问题并发现接受的答案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
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml:

version: '3.8'

services:
  service-1:
    build: ./
    volumes:
      - ./:/usr/src/app
Run Code Online (Sandbox Code Playgroud)

当您需要重新安装依赖项时,只需删除node_modules.

  • 这是如何工作的,安装卷时 Dockerfile 中的 node_modules 文件夹会被覆盖。 (4认同)
  • @丹尼尔W。我有相同的第一印象,但它可能有效,因为 CMD 是在容器启动时执行的(安装卷之后) (2认同)
  • 我见过的最优雅的解决方案只需转义 CMD 行的 `\[` `\]` 特殊字符,如下所示: `CMD \[ -d "node_modules" \] && npm run start || npm ci && npm run start` (2认同)

小智 6

没有人提到实际使用 dockerentrypoint功能的解决方案。

这是我的工作解决方案:

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"]
Run Code Online (Sandbox Code Playgroud)

码头工人/开发入口点.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 "$@"
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml:

version: "3.7"

services:
    web:
        build:
            target: dev
            context: .
        volumes:
            - .:/app:delegated
        ports:
            - "3000:3000"
        restart: always
        environment:
            NODE_ENV: dev
Run Code Online (Sandbox Code Playgroud)

通过这种方法,您可以实现所需的所有 3 点,恕我直言,这是一种更简洁的方法 - 无需四处移动文件。


Dav*_*aze 5

这里发生了三件事:

  1. 当您运行docker build或时docker-compose build,您的Dockerfile将构建一个新映像,其中包含/usr/src/app/node_modules目录和Node安装,但仅此而已。特别是,您的应用程序不在生成的映像中。
  2. 当您使用时docker-compose up,该volumes: ['./app/frontend:/usr/src/app']指令将隐藏其中的任何/usr/src/app内容,并将主机系统内容装载到该指令之上。
  3. 然后,该volumes: ['frontend-node-modules:/usr/src/app/node_modules']指令将命名卷安装在node_modules树的顶部,隐藏相应的主机系统目录。

如果您要启动另一个容器并将其附加到其上,那么我希望您会在其中看到node_modules树。对于您要描述的内容,您只是不需要指定的卷:从代码volumes:块和文件volumes:末尾的部分删除第二行docker-compose.yml

  • 是的,这是正确的,这实际上就是它的工作原理:安装的卷/主机目录隐藏了映像中的内容。Docker 永远不会自动将图像中的内容复制到主机路径卷。您可以在启动容器之前在主机目录上运行“npm install”或“yarn install”,或者将命令更改为“npm install && npm start”之类的命令。 (2认同)
  • 我知道了。问题是由于可能存在跨平台问题,我不想在主机上运行`npm install`。安装在主机上的`node_modules`在Docker容器中并不总是能正常工作。可能会有很多问题,例如不同的环境,不同的NPM或Node.js版本(例如,我们在Mac上使用Node.js 8.11.3和NPM 5.6.0运行npm install和使用Node.js 10.5.0和NPM 6.1.0将卷安装到Ubuntu 16.04 Docker容器)。该应用程序无法正常工作。据我了解,不可能实现我想要的吗? (2认同)
  • 例如,[node-sass](https://github.com/sass/node-sass)是二进制文件,它在哪里编译确实很重要。如果环境不同,我们将收到错误消息。在[此处](/sf/ask/2935993861/)了解更多信息。 (2认同)