245

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

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

4

18 回答 18

371

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

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

一种解决方法是使用数据卷来存储所有 . ,因为在挂载目录node_modules之前,数据卷会从构建的 docker 映像中复制数据。worker这可以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 文件的文件中安装前导。

于 2015-09-25T14:56:39.217 回答
55

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

于 2015-10-13T15:25:04.013 回答
50

@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
于 2016-04-01T16:06:45.860 回答
35

我最近遇到了类似的问题。您可以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
于 2016-02-10T14:09:13.973 回答
22

有优雅的解决方案:

只需挂载不是整个目录,而是仅挂载 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
于 2016-12-03T20:51:35.453 回答
16

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

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

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

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

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

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

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

它很丑陋,但它有效..

于 2015-05-15T14:08:41.630 回答
11

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

于 2015-06-18T21:51:04.503 回答
8

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

我正在使用 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
于 2016-11-30T10:36:33.697 回答
7

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

这种方法的缺点:

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

工人/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
于 2017-05-29T10:55:18.383 回答
5

我看到节点开发环境有两个单独的要求......将您的源代码安装到容器中,并从容器中安装 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
于 2016-06-28T22:01:56.800 回答
4

在我看来,我们不应该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
于 2018-03-14T09:21:28.493 回答
3

您也可以放弃 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

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

于 2020-01-29T11:02:19.993 回答
2

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

于 2019-03-21T13:36:24.877 回答
2

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

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

于 2021-02-26T20:35:26.677 回答
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

码头工人-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
于 2020-09-21T00:58:02.993 回答
1

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

这个怎么运作

FROM node:0.12

WORKDIR /worker

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

COPY . /worker/
于 2021-02-11T11:41:17.277 回答
1

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

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

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

于 2021-05-11T06:30:01.450 回答
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

于 2021-01-06T23:05:46.660 回答