Docker-compose:npm install 成功后,node_modules 不存在于卷中

Posted

技术标签:

【中文标题】Docker-compose:npm install 成功后,node_modules 不存在于卷中【英文标题】:Docker-compose: node_modules not present in a volume after npm install succeeds 【发布时间】:2015-07-14 15:35:55 【问题描述】:

我有一个包含以下服务的应用:

web/ - 在端口 5000 上保存并运行 python 3 flask web 服务器。使用 sqlite3。 worker/ - 有一个 index.js 文件,它是队列的工作人员。 Web 服务器使用 json API 通过端口 9730 与此队列交互。 worker 使用 redis 进行存储。工作人员还将数据本地存储在文件夹worker/images/

现在这个问题只涉及worker

worker/Dockerfile

FROM node:0.12

WORKDIR /worker

COPY package.json /worker/
RUN npm install

COPY . /worker/

docker-compose.yml

redis:
    image: redis
worker:
    build: ./worker
    command: npm start
    ports:
        - "9730:9730"
    volumes:
        - worker/:/worker/
    links:
        - redis

当我运行 docker-compose build 时,一切都按预期运行,并且所有 npm 模块都安装在 /worker/node_modules 中,正如我所期望的那样。

npm WARN package.json unfold@1.0.0 No README data

> phantomjs@1.9.2-6 install /worker/node_modules/pageres/node_modules/screenshot-stream/node_modules/phantom-bridge/node_modules/phantomjs
> node install.js

<snip>

但是当我执行docker-compose up 时,我看到了这个错误:

worker_1 | Error: Cannot find module 'async'
worker_1 |     at Function.Module._resolveFilename (module.js:336:15)
worker_1 |     at Function.Module._load (module.js:278:25)
worker_1 |     at Module.require (module.js:365:17)
worker_1 |     at require (module.js:384:17)
worker_1 |     at Object.<anonymous> (/worker/index.js:1:75)
worker_1 |     at Module._compile (module.js:460:26)
worker_1 |     at Object.Module._extensions..js (module.js:478:10)
worker_1 |     at Module.load (module.js:355:32)
worker_1 |     at Function.Module._load (module.js:310:12)
worker_1 |     at Function.Module.runMain (module.js:501:10)

原来/worker/node_modules(在主机或容器中)中不存在任何模块。

如果在主机上,我npm install,那么一切正常。但我不想那样做。我希望容器处理依赖关系。

这里出了什么问题?

(不用说,所有的包都在package.json。)

【问题讨论】:

我认为你应该使用 ONBUILD 指令...像这样:github.com/nodejs/docker-node/blob/master/0.12/onbuild/… 当IDE不知道node_module依赖的情况下,如何在宿主机上进行开发? 尝试从volumes: - worker/:/worker/ 文件中删除volumes: - worker/:/worker/ 块。此行会覆盖您使用 COPY 命令创建的文件夹。 When I run docker-compose build, everything works as expected and all npm modules are installed in /worker/node_modules as I'd expect. - 你是怎么检查的? @Vallie 您可以使用“docker run -it image_name sh”查看您使用“docker build”构建的镜像内容 【参考方案1】:

更新:使用@FrederikNS 提供的solution。

我遇到了同样的问题。当文件夹/worker 挂载到容器时 - 它的所有内容都将被同步(因此如果您在本地没有 node_modules 文件夹,它将消失。)

由于基于操作系统的 npm 包不兼容,我不能只在本地安装模块 - 然后启动容器,所以..

我对此的解决方案是将源代码包装在 src 文件夹中,然后使用 this index.js file 将 node_modules 链接到该文件夹​​中。所以,index.js 文件现在是我的应用程序的起点。

当我运行容器时,我将/app/src 文件夹挂载到我的本地src 文件夹中。

所以容器文件夹看起来像这样:

/app
  /node_modules
  /src
    /node_modules -> ../node_modules
    /app.js
  /index.js

,但它有效..

【讨论】:

【参考方案2】:

由于the way Node.js loads modules,node_modules 可以位于源代码路径中的任何位置。例如,将您的源代码放在/worker/src,将您的package.json 放在/worker,这样/worker/node_modules 就是它们的安装位置。

【讨论】:

【参考方案3】:

发生这种情况是因为您已将 worker 目录作为一个卷添加到您的 docker-compose.yml,因为该卷在构建期间未安装。

docker 构建镜像时,node_modules 目录会在worker 目录中创建,所有依赖项都安装在那里。然后在运行时将来自 docker 外部的 worker 目录挂载到 docker 实例中(它没有安装 node_modules),隐藏刚刚安装的 node_modules。您可以通过从 docker-compose.yml 中删除已安装的卷来验证这一点。

一种解决方法是使用数据卷来存储所有node_modules,因为在挂载worker 目录之前,数据卷会从构建的docker 映像中复制数据。这可以像这样在docker-compose.yml 中完成:

redis:
    image: redis
worker:
    build: ./worker
    command: npm start
    ports:
        - "9730:9730"
    volumes:
        - ./worker/:/worker/
        - /worker/node_modules
    links:
        - redis

我不完全确定这是否会给映像的可移植性带来任何问题,但您似乎主要使用 docker 来提供运行时环境,这应该不是问题。

如果您想了解有关卷的更多信息,这里有一个不错的用户指南:https://docs.docker.com/userguide/dockervolumes/

编辑:Docker 已经更改了它的语法,要求在与 docker-compose.yml 文件相关的文件中安装前导 ./

【讨论】:

我已经尝试过这种方法并在依赖项发生变化时碰壁了。我已经重建了映像,启动了新的容器和卷 /worker/node_modules 与以前一样(具有旧的依赖项)。重建映像时如何使用新卷有什么技巧吗? 如果其他容器使用它们,docker compose 似乎不会删除它们(即使它们已经死了)。因此,如果有一些相同类型的死容器(无论出于何种原因),我在之前的评论中描述的场景如下。从我的尝试来看,使用docker-compose rm 似乎可以解决这个问题,但我相信一定有更好更简单的解决方案。 2018年有没有解决方案,不用每次deps变化都rebuild --no-cache 这种方法的问题是你的开发机器(容器外)无法访问node_modules文件夹 您现在可以使用 ` --renew-anon-volumes` 重新创建匿名卷,而不是从以前的容器中创建数据。【参考方案4】:

node_modules 文件夹被卷覆盖,无法在容器中访问。我正在使用native module loading strategy 从卷中取出文件夹:

/data/node_modules/ # dependencies installed here
/data/app/ # code base

Dockerfile:

COPY package.json /data/
WORKDIR /data/
RUN npm install
ENV PATH /data/node_modules/.bin:$PATH

COPY . /data/app/
WORKDIR /data/app/

node_modules 目录无法从容器外部访问,因为它包含在映像中。

【讨论】:

node_modules 无法从容器外部访问,但并不是真正的缺点;) 而且每次更改 package.json 时都需要使用 --no-cache 重建整个容器,对吗? 修改package.json的时候需要重建镜像,是的,但是--no-cache不是必须的。如果您运行docker-compose run app npm install,您将在当前目录中创建一个node_modules,您不再需要重新构建映像。 缺点是:没有更多的 IDE 自动完成功能,没有帮助,没有良好的开发体验。现在还需要在主机上安装所有东西,但是在这里使用 docker 的原因不就是 dev-host 不需要任何东西就可以处理项目吗? @jsan 非常感谢,你是救命稻草。【参考方案5】:

我最近遇到了类似的问题。您可以在别处安装node_modules 并设置NODE_PATH 环境变量。

在下面的示例中,我将node_modules 安装到/install

工人/Dockerfile

FROM node:0.12

RUN ["mkdir", "/install"]

ADD ["./package.json", "/install"]
WORKDIR /install
RUN npm install --verbose
ENV NODE_PATH=/install/node_modules

WORKDIR /worker

COPY . /worker/

docker-compose.yml

redis:
    image: redis
worker:
    build: ./worker
    command: npm start
    ports:
        - "9730:9730"
    volumes:
        - worker/:/worker/
    links:
        - redis

【讨论】:

@FrederikNS 投票得最多的解决方案很有用,我是如何根据this article 解决本地卷覆盖容器的node_modules 的另一个问题。但它让我体验了this issue。这个解决方案在这里创建一个单独的目录以将您的package.json 复制到,在那里运行npm install,然后在docker-compose.yml 中指定NODE_PATH 环境变量以指向该目录的node_modules 文件夹工作并且感觉正确。 将节点模块安装到不同的路径 (/install) 然后设置 NODE_PATH (ENV NODE_PATH=/install/node_modules) 可以解决问题,因为在卷安装后路径不会被覆盖。 【参考方案6】:

@FrederikNS 提供的解决方案有效,但我更喜欢明确命名我的 node_modules 卷。

我的project/docker-compose.yml 文件(docker-compose 1.6+ 版):

version: '2'
services:
  frontend:
    ....
    build: ./worker
    volumes:
      - ./worker:/worker
      - node_modules:/worker/node_modules
    ....
volumes:
  node_modules:

我的文件结构是:

project/
   │── worker/
   │     └─ Dockerfile
   └── docker-compose.yml

它会创建一个名为 project_node_modules 的卷,并在我每次启动应用程序时重新使用它。

我的docker volume ls 看起来像这样:

DRIVER              VOLUME NAME
local               project_mysql
local               project_node_modules
local               project2_postgresql
local               project2_node_modules

【讨论】:

虽然这个“有效”,但你正在绕过 docker 中的一个真实概念:所有依赖项都应该被烘焙以实现最大的可移植性。你不能在不运行其他命令的情况下移动这个图像 @JavierBuzzi 问题在于 npm/yarn 的 node_modules 与该理念不一致。如果您的应用程序中没有设置即插即用,那么您需要这样做。还有 $NODE_PATH 但我无法让它适用于我的用例。这对我来说很好。除了使用命名卷与匿名卷之外,它也与投票最多的答案没有什么不同。【参考方案7】:

我看到节点开发环境有两个单独的要求...将您的源代码安装到容器中,然后从容器中安装 node_modules(对于您的 IDE)。要完成第一个,您需要进行通常的安装,但不是所有的...只是您需要的东西

volumes:
    - worker/src:/worker/src
    - worker/package.json:/worker/package.json
    - etc...

(不这样做的原因- /worker/node_modules 是因为 docker-compose 将在运行之间保持该卷,这意味着您可以偏离图像中的实际内容(违背了不仅仅是从主机绑定安装的目的))。

第二个实际上更难。我的解决方案有点骇人听闻,但它确实有效。我有一个脚本可以在我的主机上安装 node_modules 文件夹,我只需要记住在更新 package.json 时调用它(或者,将它添加到在本地运行 docker-compose build 的 make 目标中)。

install_node_modules:
    docker build -t building .
    docker run -v `pwd`/node_modules:/app/node_modules building npm install

【讨论】:

【参考方案8】:

将容器中的 node_modules 安装到与项目文件夹不同的位置,并将 NODE_PATH 设置为您的 node_modules 文件夹对我有帮助(您需要重建容器)。

我正在使用 docker-compose。我的项目文件结构:

-/myproject
--docker-compose.yml
--nodejs/
----Dockerfile

docker-compose.yml:

version: '2'
services:
  nodejs:
    image: myproject/nodejs
    build: ./nodejs/.
    volumes:
      - ./nodejs:/workdir
    ports:
      - "23005:3000"
    command: npm run server

nodejs 文件夹中的 Dockerfile:

FROM node:argon
RUN mkdir /workdir
COPY ./package.json /workdir/.
RUN mkdir /data
RUN ln -s /workdir/package.json /data/.
WORKDIR /data
RUN npm install
ENV NODE_PATH /data/node_modules/
WORKDIR /workdir

【讨论】:

【参考方案9】:

有一个优雅的解决方案:

只是挂载不是整个目录,而只是 app 目录。这样你就不会遇到npm_modules 的麻烦。

例子:

  frontend:
    build:
      context: ./ui_frontend
      dockerfile: Dockerfile.dev
    ports:
    - 3000:3000
    volumes:
    - ./ui_frontend/src:/frontend/src

Dockerfile.dev:

FROM node:7.2.0

#Show colors in docker terminal
ENV COMPOSE_HTTP_TIMEOUT=50000
ENV TERM="xterm-256color"

COPY . /frontend
WORKDIR /frontend
RUN npm install update
RUN npm install --global typescript
RUN npm install --global webpack
RUN npm install --global webpack-dev-server
RUN npm install --global karma protractor
RUN npm install
CMD npm run server:dev

【讨论】:

这是一个很好且快速的解决方案,但它需要在安装新的依赖项后重新构建和修剪。【参考方案10】:

还有一些简单的解决方案,无需将node_module 目录映射到另一个卷。它即将将安装 npm 包转移到最终的 CMD 命令中。

这种方法的缺点:

每次运行容器时运行npm install(从npm 切换到yarn 也可能会加快这个过程)。

工人/Dockerfile

FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
COPY . /worker/
CMD /bin/bash -c 'npm install; npm start'

docker-compose.yml

redis:
    image: redis
worker:
    build: ./worker
    ports:
        - "9730:9730"
    volumes:
        - worker/:/worker/
    links:
        - redis

【讨论】:

【参考方案11】:

在我看来,我们不应该在 Dockerfile 中使用RUN npm install。相反,我们可以在运行正式的节点服务之前使用 bash 启动一个容器来安装依赖项

docker run -it -v ./app:/usr/src/app  your_node_image_name  /bin/bash
root@247543a930d6:/usr/src/app# npm install

【讨论】:

我实际上同意你的观点。当您想在容器和主机之间共享数据时,可以使用卷。当您决定让您的node_modules 即使在容器移除后仍然存在时,您还应该知道何时或何时不手动执行npm install。 OP 建议在每个图像构建上都这样做。你可以这样做,但你不需要为此也使用卷。在每次构建时,模块都会是最新的。 @Blauhirn 在执行例如操作时将本地主机卷挂载到容器中会很有帮助。 gulp watch(或类似的命令) - 您希望 node_modules 保持不变,同时仍允许更改其他源(js、css 等)。 npm 坚持使用本地 gulp,所以它必须持久化(或者在启动时通过其他方法安装)【参考方案12】:

你可以在你的 Dockerfile 中尝试这样的事情:

FROM node:0.12
WORKDIR /worker
CMD bash ./start.sh

那么你应该像这样使用 Volume:

volumes:
  - worker/:/worker:rw

startscript 应该是您的工作存储库的一部分,如下所示:

#!/bin/sh
npm install
npm start

因此,node_modules 是您的工作卷的一部分,并且会同步,并且在一切就绪时执行 npm 脚本。

【讨论】:

这会增加启动容器的大量开销。 但只是第一次,因为node_modules会被持久化在本地机器上。 或者直到图像被重建,或者卷被移除:)。也就是说,我自己还没有找到更好的解决方案。【参考方案13】:

您也可以放弃 Dockerfile,因为它很简单,只需使用基本图像并在 compose 文件中指定命令:

version: '3.2'

services:
  frontend:
    image: node:12-alpine
    volumes:
      - ./frontend/:/app/
    command: sh -c "cd /app/ && yarn && yarn run start"
    expose: [8080]
    ports:
      - 8080:4200

这对我来说特别有用,因为我只需要镜像的环境,但是在容器外对我的文件进行操作,我想这也是你想做的。

【讨论】:

【参考方案14】:

如果您希望在开发期间主机可以使用node_modules 文件夹,您可以在启动容器时而不是在构建时安装依赖项。我这样做是为了让语法高亮在我的编辑器中工作。

Dockerfile

# 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 . .

.dockerignore

node_modules 添加到.dockerignore 以防止在Dockerfile 运行COPY . . 时复制它。我们使用卷来引入node_modules

**/node_modules

docker-compose.yml

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

【讨论】:

【参考方案15】:

如果你不使用 docker-compose,你可以这样做:

FROM node:10

WORKDIR /usr/src/app

RUN npm install -g @angular/cli

COPY package.json ./
RUN npm install

EXPOSE 5000

CMD ng serve --port 5000 --host 0.0.0.0

然后构建它:docker build -t myname .,然后通过添加两个卷运行它,第二个没有源:docker run --rm -it -p 5000:5000 -v "$PWD":/usr/src/app/ -v /usr/src/app/node_modules myname

【讨论】:

【参考方案16】:

您可以将node_modules 移动到/ 文件夹中。

How it works

FROM node:0.12

WORKDIR /worker

COPY package.json /worker/
RUN npm install \
  && mv node_modules /node_modules

COPY . /worker/

【讨论】:

【参考方案17】:

我尝试了此页面上最受欢迎的答案,但遇到了一个问题:我的 Docker 实例中的 node_modules 目录将缓存在命名或未命名的挂载点中,然后会覆盖之前的 node_modules 目录作为 Docker 构建过程的一部分构建。因此,我添加到 package.json 的新模块不会出现在 Docker 实例中。

幸运的是,我找到了这个出色的页面,它解释了发生的事情并提供了至少 3 种解决方法: https://burnedikt.com/dockerized-node-development-and-mounting-node-volumes/

【讨论】:

【参考方案18】:

使用 Yarn,您可以通过设置将 node_modules 移到卷外

# ./.yarnrc
--modules-folder /opt/myproject/node_modules

见https://www.caxy.com/blog/how-set-custom-location-nodemodules-path-yarn

【讨论】:

以上是关于Docker-compose:npm install 成功后,node_modules 不存在于卷中的主要内容,如果未能解决你的问题,请参考以下文章

docker-compose Install Rancher

docker-compose Install Elasticsearch 7.16

docker-compose Install Yearing

docker-compose Install loki+promtail

docker-compose Install NextCloud

docker-compose Install Rancher