node.js Docker-compose:npm 安装成功后卷中不存在 node_modules

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30043872/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 18:43:40  来源:igfitidea点击:

Docker-compose: node_modules not present in a volume after npm install succeeds

node.jsdockerubuntu-14.04docker-compose

提问by KGo

I have an app with the following services:

我有一个提供以下服务的应用程序:

  • web/- holds and runs a python 3 flask web server on port 5000. Uses sqlite3.
  • worker/- has an index.jsfile which is a worker for a queue. the web server interacts with this queue using a json API over port 9730. The worker uses redis for storage. The worker also stores data locally in the folder worker/images/
  • web/- 在端口 5000 上持有并运行 python 3 flask web 服务器。使用 sqlite3。
  • worker/- 有一个index.js文件,它是队列的工作人员。Web 服务器使用 json API 通过端口与此队列交互9730。worker 使用 redis 进行存储。工作人员还将数据本地存储在文件夹中worker/images/

Now this question only concerns the worker.

现在这个问题只涉及worker.

worker/Dockerfile

worker/Dockerfile

FROM node:0.12

WORKDIR /worker

COPY package.json /worker/
RUN npm install

COPY . /worker/

docker-compose.yml

docker-compose.yml

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

When I run docker-compose build, everything works as expected and all npm modules are installed in /worker/node_modulesas I'd expect.

当我运行时docker-compose build,一切都按预期工作,并且所有 npm 模块都/worker/node_modules按照我的预期安装。

npm WARN package.json [email protected] No README data

> [email protected] install /worker/node_modules/pageres/node_modules/screenshot-stream/node_modules/phantom-bridge/node_modules/phantomjs
> node install.js

<snip>

But when I do docker-compose up, I see this error:

但是当我这样做时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)

Turns out none of the modules are present in /worker/node_modules(on host or in the container).

结果发现/worker/node_modules(在主机或容器中)没有任何模块存在。

If on the host, I npm install, then everything works just fine. But I don't want to do that. I want the container to handle dependencies.

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

What's going wrong here?

这里出了什么问题?

(Needless to say, all packages are in package.json.)

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

回答by FrederikNS

This happens because you have added your workerdirectory as a volume to your docker-compose.yml, as the volume is not mounted during the build.

发生这种情况是因为您已将worker目录作为卷添加到docker-compose.yml.

When docker builds the image, the node_modulesdirectory is created within the workerdirectory, and all the dependencies are installed there. Then on runtime the workerdirectory from outside docker is mounted into the docker instance (which does not have the installed node_modules), hiding the node_modulesyou just installed. You can verify this by removing the mounted volume from your docker-compose.yml.

docker 构建镜像时,会在node_modules目录中创建worker目录,所有依赖都安装在那里。然后在运行时,worker来自外部 docker的目录被挂载到 docker 实例(没有安装node_modules),隐藏node_modules你刚刚安装的。您可以通过从docker-compose.yml.

A workaround is to use a data volume to store all the node_modules, as data volumes copy in the data from the built docker image before the workerdirectory is mounted. This can be done in the docker-compose.ymllike this:

一种解决方法是使用数据卷来存储所有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

I'm not entirely certain whether this imposes any issues for the portability of the image, but as it seems you are primarily using docker to provide a runtime environment, this should not be an issue.

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

If you want to read more about volumes, there is a nice user guide available here: https://docs.docker.com/userguide/dockervolumes/

如果您想阅读有关卷的更多信息,这里有一个很好的用户指南:https: //docs.docker.com/userguide/dockervolumes/

EDIT: Docker has since changed it's syntax to require a leading ./for mounting in files relative to the docker-compose.yml file.

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

回答by jsan

The node_modulesfolder is overwritten by the volume and no more accessible in the container. I'm using the native module loading strategyto take out the folder from the volume:

node_modules文件夹被卷覆盖,并且无法在容器中访问。我正在使用本机模块加载策略从卷中取出文件夹:

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

Dockerfile:

Dockerfile:

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

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

The node_modulesdirectory is not accessible from outside the container because it is included in the image.

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

回答by Guillaume Vincent

The solution provided by @FrederikNS works, but I prefer to explicitly name my node_modules volume.

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

My project/docker-compose.ymlfile (docker-compose version 1.6+) :

我的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:

my file structure is :

我的文件结构是:

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

It creates a volume named project_node_modulesand re-use it every time I up my application.

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

My docker volume lslooks like this :

docker volume ls看起来像这样:

DRIVER              VOLUME NAME
local               project1_mysql
local               project1_node_modules
local               project2_postgresql
local               project2_node_modules

回答by ericstolten

I recently had a similar problem. You can install node_moduleselsewhere and set the NODE_PATHenvironment variable.

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

In the example below I installed node_modulesinto /install

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

worker/Dockerfile

工人/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

docker-compose.yml

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

回答by holms

There's elegant solution:

有一个优雅的解决方案:

Just mount not whole directory, but only app directory. This way you'll you won't have troubles with npm_modules.

只是挂载不是整个目录,而只是应用程序目录。这样你就不会遇到麻烦了npm_modules

Example:

例子:

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

Dockerfile.dev:

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

回答by JAM

UPDATE: Use the solutionprovided by @FrederikNS.

更新:使用@FrederikNS 提供的解决方案

I encountered the same problem. When the folder /workeris mounted to the container - all of it's content will be syncronized (so the node_modules folder will disappear if you don't have it locally.)

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

Due to incompatible npm packages based on OS, I could not just install the modules locally - then launch the container, so..

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

My solution to this, was to wrap the source in a srcfolder, then link node_modulesinto that folder, using this index.js file. So, the index.jsfile is now the starting point of my application.

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

When I run the container, I mounted the /app/srcfolder to my local srcfolder.

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

So the container folder looks something like this:

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

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

It is ugly, but it works..

这是丑陋的,但它的工作原理..

回答by Justin Stayton

Due to the way Node.js loads modules, node_modulescan be anywhere in the path to your source code. For example, put your source at /worker/srcand your package.jsonin /worker, so /worker/node_modulesis where they're installed.

由于Node.js 加载模块的方式node_modules可以位于源代码路径中的任何位置。例如,将您的源放在/worker/srcpackage.jsonin 中/worker/worker/node_modules它们的安装位置也是如此。

回答by sergeysynergy

Installing node_modules in container to different from project folder, and setting NODE_PATH to your node_modules folder helps me (u need to rebuild container).

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

I'm using docker-compose. My project file structure:

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

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

docker-compose.yml:

docker-compose.yml:

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

Dockerfile in nodejs folder:

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

回答by Egel

There is also some simple solution without mapping node_moduledirectory into another volume. It's about to move installing npm packages into final CMD command.

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

Disadvantage of this approach:

  • run npm installeach time you run container (switching from npmto yarnmight also speed up this process a bit).

这种方法的缺点:

  • 运行npm install每次运行容器时间(从开关npmyarn也可能加快这一进程中位)。

worker/Dockerfile

工人/Dockerfile

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

docker-compose.yml

docker-compose.yml

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

回答by Paul Becotte

There are two seperate requirements I see for node dev environments... mount your source code INTO the container, and mount the node_modules FROM the container (for your IDE). To accomplish the first, you do the usual mount, but not everything... just the things you need

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

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

( the reason to not do - /worker/node_modulesis because docker-compose will persist that volume between runs, meaning you can diverge from what is actually in the image (defeating the purpose of not just bind mounting from your host)).

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

The second one is actually harder. My solution is a bit hackish, but it works. I have a script to install the node_modules folder on my host machine, and I just have to remember to call it whenever I update package.json (or, add it to the make target that runs docker-compose build locally).

第二个其实更难。我的解决方案有点hackish,但它有效。我有一个脚本来在我的主机上安装 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