Docker-compose: node_modules відсутні в томі після успішної установки npm


184

У мене є додаток із такими послугами:

  • web/ - тримає і запускає веб-сервер flask python 3 на порт 5000. Використовує sqlite3.
  • worker/- має index.jsфайл, який є працівником для черги. веб-сервер взаємодіє з цією чергою за допомогою API json через порт 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 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.)


Ви нарешті знайшли рішення?
Джастін Стейтон

Я думаю, вам слід скористатися інструкцією ONBUILD ... Ось так
Лукас Поттерський,

1
Як би ви зробили розробку для хоста, коли IDE не знає залежності node_module?
Андре

2
Спробуйте видалити volumes: - worker/:/worker/блок з docker-compose.ymlфайлу. Цей рядок перезаписує папку, яку ви створюєте за допомогою команди 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.- Як ти це перевірив?
Валлі

Відповіді:


272

Це трапляється тому, що ви додали до workerкаталогу свій каталог docker-compose.yml, тому що том не встановлений під час збирання.

Коли докер створює зображення, в 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/

EDIT: з того часу Docker змінив свій синтаксис, щоб вимагати провідних ./файлів для монтажу у файлах відносно файлу docker-compose.yml.


7
чудова відповідь, найменш настирливе і чудове пояснення!
kkemple

41
Я спробував цей метод і потрапив у стіну, коли залежності змінилися. Я відновив зображення, запустив новий контейнер і об'єм /worker/node_modulesзалишився таким же, як і раніше (зі старими залежностями). Чи є хитрість, як використовувати новий об'єм при перебудові зображення?
Ondrej Slinták

11
Здається, докер-композиція не видаляє томи, якщо інші контейнери використовують їх (навіть якщо вони мертві). Отже, якщо є якісь мертві контейнери одного типу (з будь-якої причини), слід сценарій, який я описав у попередньому коментарі. З того, що я спробував, використовуючи, docker-compose rmздається, вирішити цю проблему, але я вважаю, що повинно бути краще і простіше рішення.
Ondrej Slinták

15
Чи є рішення у 2018 році без необхідності rebuild --no-cacheщоразу змінювати депа?
Eelke

7
тепер ви можете використовувати `--renew-anon-volumes`, який відтворить анонімні томи замість даних з попередніх контейнерів.
Мухаммед Ессеемій

37

node_modulesПапка перезаписується за обсягом і не більше доступними в контейнері. Я використовую стратегію завантаження нативного модуля, щоб витягнути папку з гучності:

/data/node_modules/ # dependencies installed here
/data/app/ # code base

Докерфайл:

COPY package.json /data/
WORKDIR /data/
RUN npm install
ENV PATH /data/node_modules/.bin:$PATH

COPY . /data/app/
WORKDIR /data/app/

node_modulesКаталог недоступний зовні контейнера , так як він входить в образ.


1
чи є якісь недоліки цього підходу? Здається, для мене це добре працює.
Брет Фішер

1
node_modulesнедоступний ззовні контейнера, але насправді не є недоліком;)
jsan

і кожного разу, коли ви міняєте package.json, вам потрібно буде переробляти весь контейнер за допомогою --no-cache?
Люк

Вам потрібно відновити зображення при зміні пакета.json, так, але --no-cache не потрібен. Якщо ви запустите, docker-compose run app npm installви створите node_modules в поточному каталозі, і вам більше не потрібно буде перебудовувати зображення.
jsan

9
Мінусом є: більше немає автодоповнення IDE, немає допомоги, немає хорошого досвіду розробки. Все також потрібно встановити на хості зараз, але чи не причина для використання докера тут, що dev-хосту нічого не потрібно, щоб мати можливість працювати з проектом?
Майкл Б.

31

Рішення, яке надає @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
в той час як це "працює", ви обминаєте реальну концепцію в docker: всі залежності повинні бути випечені для максимальної мобільності. Ви не можете перемістити це зображення, не виконуючи інші команди, які ніби дмуть
Хав'єр Буцці

4
годинами намагався вирішити ту саму проблему і придумав таке саме рішення, як і я. Ваша відповідь має бути найвищою, але я думаю, що ppl не отримає вашої відповіді, оскільки ви назвали свій том "node_modules", а всі читачі не вистачають факту, що це створює новий том. Читаючи ваш код, я подумав, що ви просто "перевстановіть" папку локальних вузлів_модулів і відкинули цю ідею. Можливо, вам слід відредагувати назву тома на щось на кшталт "container_node_modules", щоб зробити це зрозумілим. :)
Фабіан

1
Я також виявив, що це найелегантніше рішення, яке дозволяє легко посилатися на один і той же об'єм для модулів вузлів на декількох етапах збирання. Велика стаття Уроки створення програм Node в Docker також використовує той самий підхід.
kf06925

1
@ kf06925 Чоловік, ти мене справді врятував, я витратив години, намагаючись вирішити це, і завдяки статті я міг !! Я б купив тобі пиво, якби міг так подякувати
Хеладо

21

Нещодавно у мене була подібна проблема. Ви можете встановити в node_modulesіншому місці та встановити NODE_PATHзмінну середовища.

У наведеному нижче прикладі я встановив node_modulesу/install

працівник / Докерфайл

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/

docker-compose.yml

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

6
Головне рішення, яке проголосувало @FrederikNS, є корисним, як я вирішив іншу проблему мого локального тома, що перезаписав контейнер на node_modulesоснові цієї статті . Але це змусило мене пережити це питання . Це рішення тут створити окремий каталог для його копіювання package.json, запустіть npm installйого, а потім вкажіть NODE_PATHзмінну середовища в, docker-compose.ymlщоб вказати на node_modulesпапку цього каталогу, працює і вважає себе правильним.
cwnewhouse

Включення ENV NODE_PATH = / install / node_modules в Dockerfile було нарешті рішенням для мене після годин спроб різних підходів. Дякую вам сер.
Бенні Мід

Що робити, якщо ти працюєш npm installна хості? Здається, node_modulesвін з’явиться на хості та буде відображений у контейнері, переймаючи пріоритет NODE_PATH. Отже контейнер буде використовувати node_modules від хоста.
vitalets

19

Є елегантне рішення:

Просто змонтуйте не весь каталог, а лише каталог додатків. Таким чином у вас не виникне проблем 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

Блискуча. Не можу зрозуміти, чому це не прийнята відповідь.
Jivan

2
Це хороше і швидке рішення, але воно потребує відновлення та чорносливу після встановлення нової залежності.
Кунок

Я зараз роблю це по-іншому, має бути том, спеціально для node_modules, і том, який ви змонтуєте з хоста. Тоді немає ніяких проблем
Holms

14

ОНОВЛЕННЯ: Використовуйте рішення, надане @FrederikNS.

Я зіткнувся з тією ж проблемою. Коли папка /workerвстановлена ​​до контейнера - весь її вміст буде синхронізований (тому папка node_modules зникне, якщо у вас її немає локально.)

Через несумісні пакети npm на базі ОС я не міг просто встановити модулі локально - тоді запустити контейнер, так що ..

Моїм рішенням цього було загортати джерело в srcпапку, а потім посилатись node_modulesна цю папку, використовуючи цей файл index.js . Отже, index.jsфайл тепер є відправною точкою моєї заявки.

Коли я запускаю контейнер, я встановив /app/srcпапку в свою локальну srcпапку.

Отже, папка контейнера виглядає приблизно так:

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

Це некрасиво , але це працює ..


3
о, шановний пане ... я не можу повірити, що я також затримався з цим!
Лукас Поттерський


7

Встановлення node_modules в контейнер, який відрізняється від папки проекту, і встановлення NODE_PATH у папку node_modules допомагає мені (вам потрібно відновити контейнер).

Я використовую докер-композит. Моя структура файлу проекту:

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

docker-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

1
Це найкраще рішення, яке я знайшов. NODE_PATHбула ключовою для мене.
cdignam

Я думаю, що це має сенс, але, встановивши NODE_PATH, під час запуску зображення CMD npm start не використовує вказаний NODE_PATH.
Acton

6

Існує також просте рішення без зіставлення node_moduleкаталогу в інший том. Йдеться про переміщення установки npm-пакетів у остаточну команду CMD.

Недолік такого підходу:

  • запускати npm installкожен раз, коли ви запускаєте контейнер (перемикання на npmдо, yarnтакож може трохи прискорити цей процес).

працівник / Докерфайл

FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
COPY . /worker/
CMD /bin/bash -c 'npm install; npm start'

docker-compose.yml

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

3

Є два окремі вимоги, які я бачу для середовищ розробки вузла ... монтуйте свій вихідний код у контейнер і монтуйте node_modules З контейнера (для вашого IDE). Щоб виконати перше, ви робите звичайне кріплення, але не все ... лише те, що вам потрібно

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

(причина цього не робити - /worker/node_modules- це те, що docker-compose збережеться, що гучність між прогонами, це означає, що ви можете розходитись із тим, що є насправді на зображенні (перемагаючи мету не просто прив’язувати монтаж від вашого хоста)).

Другий насправді складніше. Моє рішення трохи хакітське, але воно працює. У мене є сценарій для встановлення папки node_modules на моїй хост-машині, і я просто мушу пам'ятати, щоб викликати її щоразу, коли я оновлюю package.json (або додаю його до make target, яка виконує docker-compose build локально).

install_node_modules:
    docker build -t building .
    docker run -v `pwd`/node_modules:/app/node_modules building npm install

2

На мою думку, ми не повинні 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

Я насправді з цим згоден. Томи призначені для використання, коли потрібно обмінюватися даними між контейнером і хостом. Коли ви вирішите node_modulesбути наполегливими навіть після видалення контейнера, ви також повинні знати, коли або коли не робити npm installвручну. ОП пропонує зробити це для кожного побудови образу . Ви можете це зробити, але для цього також не потрібно використовувати гучність. При кожній збірці модулі будуть актуальні будь-коли.
phil294

@Blauhirn корисно монтувати локальний об'єм хоста в контейнер, наприклад, виконуючи, наприклад, глоткові годинники (або аналогічні команди) - ви хочете, щоб node_modules зберігалися, але все ще дозволяли змінити інші джерела (js, css тощо). НПМ наполягає на використанні місцевого глотка, тому він повинен зберігатися (або бути встановлені з допомогою інших методів , при запуску)
TBM

2

Ви можете спробувати щось подібне у своєму 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 виконуються, коли все працює.


Це додасть великі накладні витрати для запуску контейнера.
TBM

2
Але лише перший раз, тому що node_modules будуть зберігатися на локальній машині.
Parav01d

Або до того моменту, поки зображення не буде відновлено, або не буде видалено гучність :). Це сказало, що я не знайшов собі кращого рішення.
TBM

0

Ви також можете викопати 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

Це особливо корисно для мене, тому що мені просто потрібне оточення зображення, але я працюю над своїми файлами поза контейнером, і я думаю, що це теж потрібно зробити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.