web-dev-qa-db-fra.com

Docker-compose: node_modules non présent dans un volume après la réussite de l'installation de npm

J'ai une application avec les services suivants:

  • web/ - contient et exécute un serveur Web python 3 flask sur le port 5000. Utilise sqlite3.
  • worker/ - contient un fichier index.js qui est un ouvrier pour une file d'attente. le serveur Web interagit avec cette file d'attente à l'aide d'une API json sur le port 9730. Le travailleur utilise redis pour le stockage. Le travailleur stocke également les données localement dans le dossier worker/images/

Maintenant, cette question ne concerne que la 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

Lorsque je lance docker-compose build, tout fonctionne comme prévu et tous les modules npm sont installés dans /worker/node_modules comme je l’attendais.

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>

Mais quand je fais docker-compose up, je vois cette erreur:

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)

Il s'avère qu'aucun des modules n'est présent dans /worker/node_modules (sur l'hôte ou dans le conteneur).

Si sur l'hôte, je npm install, alors tout fonctionne correctement. Mais je ne veux pas faire ça. Je veux que le conteneur gère les dépendances.

Qu'est-ce qui ne va pas ici?

(Inutile de dire que tous les paquets sont dans package.json.)

144
Karan Goel

Cela est dû au fait que vous avez ajouté votre répertoire worker en tant que volume à votre docker-compose.yml, car le volume n’est pas monté pendant la construction.

Lorsque docker construit l'image, le répertoire node_modules est créé dans le répertoire worker et toutes les dépendances y sont installées. Ensuite, lors de l'exécution, le répertoire worker du menu fixe extérieur est monté dans l'instance de menu fixe (qui n'a pas installé le node_modules), masquant le node_modules que vous venez d'installer. Vous pouvez le vérifier en supprimant le volume monté de votre docker-compose.yml.

Une solution de contournement consiste à utiliser un volume de données pour stocker tous les node_modules, car les volumes de données sont copiés dans les données à partir de l'image du menu fixe avant le montage du répertoire worker. Cela peut être fait dans le docker-compose.yml comme ceci:

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

Je ne suis pas tout à fait sûr que cela pose des problèmes de portabilité de l'image, mais comme il semble que vous utilisez principalement docker pour fournir un environnement d'exécution, cela ne devrait pas être un problème.

Si vous souhaitez en savoir plus sur les volumes, un guide de l'utilisateur Nice est disponible ici: https://docs.docker.com/userguide/dockervolumes/

217
FrederikNS

Le dossier node_modules est écrasé par le volume et n'est plus accessible dans le conteneur. J'utilise la stratégie de chargement de module natif pour extraire le dossier du volume:

/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 n'est pas accessible de l'extérieur du conteneur car il est inclus dans l'image.

28
jsan

La solution fournie par @FrederikNS fonctionne, mais je préfère nommer explicitement mon volume node_modules.

Mon fichier 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:

ma structure de fichier est:

project/
   │── worker/
   │     └─ Dockerfile
   └── docker-compose.yml

Il crée un volume nommé project_node_modules et le réutilise chaque fois que je lance mon application.

Mon docker volume ls ressemble à ceci:

DRIVER              VOLUME NAME
local               project1_mysql
local               project1_node_modules
local               project2_postgresql
local               project2_node_modules
26

J'ai récemment eu un problème similaire. Vous pouvez installer node_modules ailleurs et définir la variable d'environnement NODE_PATH.

Dans l'exemple ci-dessous, j'ai installé node_modules dans /install

worker/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/

docker-compose.yml

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

Il y a une solution élégante:

Il suffit de ne pas monter le répertoire complet, mais uniquement le répertoire de l'application. De cette façon, vous n'aurez pas de problèmes avec npm_modules.

Exemple:

  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
16
holms

UPDATE: Utilisez le solution fourni par @FrederikNS.

J'ai rencontré le même problème. Lorsque le dossier /worker est monté sur le conteneur, tout son contenu sera synchronisé (le dossier node_modules disparaîtra si vous ne l'avez pas localement.)

En raison des packages npm incompatibles basés sur le système d'exploitation, je ne pouvais pas simplement installer les modules localement - puis lancer le conteneur, donc ..

Ma solution à cela consistait à envelopper le source dans un dossier src, puis à lier node_modules dans ce dossier, en utilisant ce fichier index.js . Ainsi, le fichier index.js est maintenant le point de départ de mon application.

Lorsque j'exécute le conteneur, j'ai monté le dossier /app/src dans mon dossier local src.

Donc, le dossier conteneur ressemble à ceci:

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

C'est moche , mais ça marche ..

12
JAM

En raison de façon dont Node.js charge les modules , node_modules peut être n'importe où dans le chemin d'accès à votre code source. Par exemple, placez votre source sous /worker/src et votre package.json dans /worker, de sorte que /worker/node_modules se trouve à l'emplacement d'installation.

9
Justin Stayton

Installer node_modules dans un conteneur différent du dossier de projet et définir NODE_PATH dans votre dossier node_modules m'aident bien (vous devez reconstruire le conteneur).

J'utilise docker-compose. Ma structure de fichier de projet:

-/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 dans le dossier 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
6
sergeysynergy

Il existe également une solution simple sans mapper le répertoire node_module dans un autre volume. Il est sur le point de déplacer les packages d’installation de npm dans la commande finale de CMD.

Inconvénient de cette approche:

  • lancez npm install à chaque fois que vous exécutez un conteneur (passer de npm à yarn pourrait également accélérer un peu ce processus).

worker/Dockerfile

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
5
Egel

Il existe deux exigences distinctes que je vois pour les environnements de développement de noeud: montez votre code source DANS le conteneur et montez le noeud node_modules à partir du conteneur (pour votre IDE). Pour accomplir le premier, vous faites la monture habituelle, mais pas tout ... juste les choses dont vous avez besoin

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

(La raison pour ne pas faire - /worker/node_modules] est parce que docker-compose persistera ce volume entre les exécutions, ce qui signifie que vous pouvez diverger de ce qui est réellement dans l'image (ce qui irait à l'encontre du but de ne pas simplement lier le montage à votre hôte)).

Le second est en réalité plus difficile. Ma solution est un peu féroce, mais ça marche. J'ai un script pour installer le dossier node_modules sur ma machine hôte et je ne dois pas oublier de l'appeler chaque fois que je mets à jour package.json (ou l'ajoutez à la cible make qui exécute docker-compose build localement).

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

À mon avis, nous ne devrions pas RUN npm install dans le fichier Docker. Au lieu de cela, nous pouvons démarrer un conteneur en utilisant bash pour installer les dépendances avant d'exécuter le service de noeud formel

docker run -it -v ./app:/usr/src/app  your_node_image_name  /bin/bash
root@247543a930d6:/usr/src/app# npm install
2
salamander

Vous pouvez essayer quelque chose comme ceci dans votre Dockerfile:

FROM node:0.12
WORKDIR /worker
CMD bash ./start.sh

Ensuite, vous devriez utiliser le volume comme ceci:

volumes:
  - worker/:/worker:rw

Le startscript devrait faire partie de votre référentiel de travail et se présenter comme suit:

#!/bin/sh
npm install
npm start

Ainsi, les node_modules font partie de votre volume de travail. Ils sont synchronisés et les scripts npm sont exécutés lorsque tout est en place.

1
Parav01d