Docker-compose:在npm安装成功

时间:2015-05-05 03:40:00

标签: node.js docker ubuntu-14.04 docker-compose

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

  • web/ - 在端口5000上保存并运行python 3 flask Web服务器。使用sqlite3。
  • worker/ - 有一个index.js文件,它是队列的工作者。 Web服务器使用端口9730上的json API与此队列交互。工人使用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。)

17 个答案:

答案 0 :(得分:205)

这是因为您已将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/

答案 1 :(得分:27)

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无法从容器外部访问,因为它包含在图像中。

答案 2 :(得分:24)

@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               project1_mysql
local               project1_node_modules
local               project2_postgresql
local               project2_node_modules

答案 3 :(得分:17)

我最近遇到了类似的问题。您可以在其他位置安装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/

搬运工-compose.yml

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

答案 4 :(得分:11)

更新:使用@FrederikNS提供的solution

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

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

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

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

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

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

丑陋,但它有效..

答案 5 :(得分:9)

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

答案 6 :(得分:5)

从项目文件夹安装在容器node_modules不同,并设置NODE_PATH您node_modules文件夹可以帮助我(U需要重建容器)。

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

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

搬运工-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

答案 7 :(得分:4)

还有一些简单的解决方案,而不将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'

搬运工-compose.yml

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

答案 8 :(得分:3)

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

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

(不做- /worker/node_modules的原因是因为docker-compose会在运行之间保留该卷,这意味着您可以与图像中的实际内容不同(这不仅仅是绑定从主机绑定的目的) )。

第二个实际上更难。我的解决方案有点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

答案 9 :(得分:1)

在我看来,我们不应该在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

答案 10 :(得分:1)

由于其简单性,您也可以抛弃Dockerfile,只需使用基本映像并在撰写文件中指定命令即可。

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

这对我来说特别有用,因为我只需要图像的环境,但是可以在容器外部对我的文件进行操作,所以我认为这也是您想要做的。

答案 11 :(得分:1)

如果希望开发期间主机可以使用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

答案 12 :(得分:1)

您可以将 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/

答案 13 :(得分:0)

您可以在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脚本。

答案 14 :(得分:0)

如果你不使用 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

答案 15 :(得分:0)

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

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

答案 16 :(得分:0)

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

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

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