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

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

  • web/ -在端口5000上保存并运行python 3 flask网络服务器。使用sqlite3。
  • worker/-有一个index.js文件,它是队列的工作程序。Web服务器使用json API通过端口与此队列进行交互9730工作人员使用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(在主机上或在容器中)。

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

What's going wrong here?

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

番长樱梅2020/03/24 14:09:29

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

docker run -it -v ./app:/usr/src/app  your_node_image_name  /bin/bash
root@247543a930d6:/usr/src/app# npm install
番长樱梅2020/03/24 14:09:29

对于节点开发环境,我看到两个单独的要求...将源代码装入容器,并从容器装入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
古一2020/03/24 14:09:29

您可以在Dockerfile中尝试以下操作:

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

然后,您应该像这样使用Volume:

volumes:
  - worker/:/worker:rw

起始脚本应该是您的工作程序存储库的一部分,如下所示:

#!/bin/sh
npm install
npm start

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

斯丁2020/03/24 14:09:29

由于Node.js加载modules的方式node_modules可以位于源代码路径中的任何位置。例如,将您的源代码放在/worker/srcpackage.json/worker您的源代码/worker/node_modules安装在哪里。

樱小胖Mandy2020/03/24 14:09:29

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

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

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

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

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

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

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

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

达蒙JinJin2020/03/24 14:09:29

有一个优雅的解决方案:

只是不挂载整个目录,而是挂载应用程序目录。这样,您就不会遇到麻烦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
Stafan路易2020/03/24 14:09:29

我最近有一个类似的问题。您可以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
猴子JinJin2020/03/24 14:09:29

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

当docker构建映像时,将在node_modules目录中创建worker目录,并将所有依赖项安装在该目录中。然后在运行时,将worker来自外部docker 目录挂载到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文件相关的文件中进行挂载。

达蒙2020/03/24 14:09:29

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

/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无法从容器外部访问目录,因为目录已包含在映像中。