Вопрос:
У меня есть приложение со следующими службами:
- web/ – хранит и запускает веб-сервер на флэке python 3 на порту 5000. Использует sqlite3.
- worker/ – имеет файл index.js, который является рабочим для очереди. веб-сервер взаимодействует с этой очередью, используя json API через порт 9730. Рабочий использует redis для хранения. Рабочий также сохраняет данные локально в папке worker/images/
Теперь этот вопрос касается только worker.
Содержание
- worker/Dockerfile
- docker-compose.yml
- Рабочий /Dockerfile
- докер-compose.yml
- ОБНОВЛЕНИЕ: используйте решение, предоставленное @FrederikNS.
- работник /Dockerfile
- докер-compose.yml
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.)
Ответ №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 root@247543a930d6:/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 выполняются, когда все работает.