Docker-compose: node_modules не присутствует в томе после завершения установки npm
У меня есть приложение со следующими службами:
-
web/
- хранит и запускает веб-сервер на флэке python 3 на порту 5000. Использует sqlite3.
-
worker/
- имеет файл index.js
, который является рабочим для очереди. веб-сервер взаимодействует с этой очередью, используя 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 [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>
Но когда я делаю 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
.)
Ответы
Ответ 1
Это происходит потому, что вы добавили каталог worker
в качестве тома в ваш docker-compose.yml
, поскольку том не монтируется во время сборки.
Когда docker создает изображение, каталог node_modules
создается в каталоге worker
, и там устанавливаются все зависимости. Затем во время выполнения каталог worker
из внешнего докера монтируется в экземпляр докера (который не имеет установленного node_modules
), скрывая только что установленный node_modules
. Вы можете проверить это, удалив установленный том из docker-compose.yml
.
Обходной путь заключается в использовании тома данных для хранения всего node_modules
, поскольку копии томов данных в данных из встроенного изображения докеров перед установкой каталога worker
. Это можно сделать в docker-compose.yml
следующим образом:
redis:
image: redis
worker:
build: ./worker
command: npm start
ports:
- "9730:9730"
volumes:
- worker/:/worker/
- /worker/node_modules
links:
- redis
Я не совсем уверен, что это накладывает какие-либо проблемы на переносимость изображения, но, как кажется, в первую очередь вы используете докер для обеспечения среды выполнения, это не должно быть проблемой.
Если вы хотите больше узнать о томах, здесь доступно хорошее руководство пользователя: https://docs.docker.com/userguide/dockervolumes/
Ответ 2
Папка 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
недоступен извне контейнера, потому что он включен в изображение.
Ответ 3
Решение, предоставляемое @FrederikNS, работает, но я предпочитаю явно указывать свой том node_modules.
Мой project/docker-compose.yml
файл (docker-compose version 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
Ответ 4
У меня недавно была аналогичная проблема. Вы можете установить 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
Ответ 5
Там элегантное решение:
Просто установите не весь каталог, а только каталог приложения. Таким образом, у вас не будет проблем с 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
Ответ 6
ОБНОВЛЕНИЕ: используйте решение, предоставленное @FrederikNS.
Я столкнулся с той же проблемой. Когда папка /worker
монтируется в контейнер - все его содержимое будет синхронизировано (поэтому папка node_modules исчезнет, если у вас ее нет локально).
Из-за несовместимых пакетов npm, основанных на ОС, я не мог просто установить модули локально - затем запустить контейнер, так что..
Мое решение для этого было node_modules
в исходный код в папку src
, а затем связать node_modules
в эту папку, используя этот файл index.js. Итак, файл index.js
теперь является отправной точкой моего приложения.
Когда я запустил контейнер, я /app/src
папку /app/src
к своей локальной папке src
.
Таким образом, папка контейнера выглядит примерно так:
/app
/node_modules
/src
/node_modules -> ../node_modules
/app.js
/index.js
Это некрасиво, но это работает..
Ответ 7
Благодаря тому, что Node.js загружает модули, node_modules
могут находиться где угодно на пути к вашему исходному коду. Например, поместите ваш источник в /worker/src
а ваш package.json
в /worker
, чтобы они были установлены в /worker/node_modules
.
Ответ 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
Dockerfile в папке nodejs:
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
Ответ 9
Существует также некоторое простое решение без отображения каталога 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
Ответ 10
Есть два отдельных требования, которые я вижу для сред node dev... смонтируйте исходный код в контейнере и смонтируйте node_modules из контейнера (для вашей IDE). Чтобы выполнить первое, вы делаете обычное монтирование, но не все... только то, что вам нужно
volumes:
- worker/src:/worker/src
- worker/package.json:/worker/package.json
- etc...
(причина не делать - /worker/node_modules
заключается в том, что docker-compose будет сохранять этот том между прогонами, то есть вы можете расходиться с тем, что на самом деле находится на изображении (побеждая цель не просто привязать установку с вашего хоста)).
Второй - на самом деле сложнее. Мое решение немного хакерское, но оно работает. У меня есть script, чтобы установить папку node_modules на моем хост-компьютере, и я просто должен помнить, чтобы вызывать ее всякий раз, когда я обновляю package.json(или добавляю его в цель make, которая запускает сборку docker-compose).
install_node_modules:
docker build -t building .
docker run -v `pwd`/node_modules:/app/node_modules building npm install
Ответ 11
На мой взгляд, мы не должны RUN npm install
в Dockerfile. Вместо этого мы можем запустить контейнер, используя bash для установки зависимостей перед запуском службы формального узла
docker run -it -v ./app:/usr/src/app your_node_image_name /bin/bash
[email protected]:/usr/src/app# npm install
Ответ 12
Вы можете попробовать что-то вроде этого в вашем Dockerfile:
FROM node:0.12
WORKDIR /worker
CMD bash ./start.sh
Тогда вы должны использовать объем следующим образом:
volumes:
- worker/:/worker:rw
Начальный скрипт должен быть частью вашего рабочего репозитория и выглядит следующим образом:
#!/bin/sh
npm install
npm start
Таким образом, node_modules являются частью вашего рабочего тома и синхронизируются, а сценарии npm выполняются, когда все работает.