我有一个具有以下服务的应用程序:
web/ - 在端口 5000 上保存并运行 python 3 flask web 服务器。使用 sqlite3。
worker/ - 有一个 index.js 文件,它是队列的工作者。 Web 服务器使用 json API 通过端口 9730 与此队列交互。worker 使用 redis 进行存储。 worker 还将数据本地存储在文件夹 worker/images/ 中
现在这个问题只涉及 worker
。
工人/Dockerfile
FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
RUN npm install
COPY . /worker/
码头工人-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
中。)
docker-compose.yml
文件中删除 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.
- 您是如何检查的?
发生这种情况是因为您已将 worker
目录作为卷添加到 docker-compose.yml
,因为在构建期间未安装该卷。
当 docker 构建映像时,会在 worker
目录中创建 node_modules
目录,并在其中安装所有依赖项。然后在运行时将来自 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 文件的文件中安装前导 ./
。
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
无法从容器外部访问,但并不是真正的缺点;)
docker-compose run app npm install
,您将在当前目录中创建一个 node_modules,并且您不再需要重新构建映像。
@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
我最近遇到了类似的问题。您可以在别处安装 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
node_modules
的不同问题。但它让我体验了this issue。这个解决方案在这里创建一个单独的目录以将您的 package.json
复制到,在那里运行 npm install
,然后在 docker-compose.yml
中指定 NODE_PATH
环境变量以指向该目录的 node_modules
文件夹,并且感觉不错。
有优雅的解决方案:
只需挂载不是整个目录,而是仅挂载 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
更新:使用@FrederikNS 提供的解决方案。
我遇到了同样的问题。当文件夹 /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
它很丑陋,但它有效..
由于 the way Node.js loads modules,node_modules
可以位于源代码路径中的任何位置。例如,将您的源放在 /worker/src
中,将您的 package.json
放在 /worker
中,因此 /worker/node_modules
是它们的安装位置。
将容器中的 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
还有一些简单的解决方案,无需将 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
我看到节点开发环境有两个单独的要求......将您的源代码安装到容器中,并从容器中安装 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
在我看来,我们不应该在 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 建议在每个图像构建上都这样做。你可以这样做,但你不需要为此也使用卷。在每次构建时,模块都将是最新的。
您也可以放弃 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
这对我来说特别有用,因为我只需要图像的环境,但是在容器外对我的文件进行操作,我认为这也是你想要做的。
你可以在你的 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 脚本。
我尝试了此页面上最流行的答案,但遇到了一个问题:我的 Docker 实例中的 node_modules
目录将缓存在命名或未命名的挂载点中,然后会覆盖作为一部分构建的 node_modules
目录Docker 构建过程。因此,我添加到 package.json
的新模块不会出现在 Docker 实例中。
幸运的是,我找到了这个出色的页面,它解释了发生的事情并提供了至少 3 种解决方法:https://burnedikt.com/dockerized-node-development-and-mounting-node-volumes/
如果您希望在开发期间主机可以使用 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
如果你不使用 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
您只需将 node_modules
移动到 /
文件夹即可。
FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
RUN npm install \
&& mv node_modules /node_modules
COPY . /worker/
使用 Yarn,您可以通过设置将 node_modules 移动到卷外
# ./.yarnrc
--modules-folder /opt/myproject/node_modules
请参阅https://www.caxy.com/blog/how-set-custom-location-nodemodules-path-yarn
不定期副业成功案例分享
/worker/node_modules
与以前保持一致(具有旧依赖项)。重建映像时如何使用新卷有什么技巧吗?docker-compose rm
似乎可以解决此问题,但我相信必须有更好、更简单的解决方案。rebuild --no-cache
?