Docker-compose: node_modules n’est pas présent dans un volume après l’installation de npm

J’ai une application avec les services suivants:

  • web/ – contient et exécute un serveur Web de flask python 3 sur le port 5000. Utilise sqlite3.
  • worker/ – possède un fichier index.js qui est un travailleur pour une queue. le serveur Web interagit avec cette queue en utilisant une API json sur le port 9730 . Le travailleur utilise redis pour le stockage. L’agent stocke également des données localement dans le dossier worker/images/

Maintenant, cette question ne concerne que le 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 docker-compose build , tout fonctionne comme prévu et tous les modules npm sont installés dans /worker/node_modules comme prévu.

 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  

Mais quand je 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. (/worker/index.js:1:75) worker_1 | at Module._comstack (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 bien. 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 .)

Cela se produit parce que vous avez ajouté votre répertoire de worker tant que volume à votre docker-compose.yml , car le volume n’est pas monté pendant la génération.

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, à l’exécution, le répertoire de worker de l’extérieur de docker est monté dans l’instance de docker (qui n’a pas les node_modules installés), en masquant les node_modules vous venez d’installer. Vous pouvez le vérifier en retirant le volume monté de votre docker-compose.yml .

Une solution consiste à utiliser un volume de données pour stocker tous les node_modules , car les volumes de données copient dans les données de l’image du docker intégrée avant le assembly du répertoire de 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 certain si cela pose des problèmes pour la portabilité de l’image, mais comme il semble que vous utilisiez principalement docker pour fournir un environnement d’exécution, cela ne devrait pas poser de problème.

Si vous souhaitez en savoir plus sur les volumes, vous trouverez un guide de l’utilisateur très utile à l’ adresse suivante : https://docs.docker.com/userguide/dockervolumes/

Le dossier node_modules est remplacé par le volume et n’est plus accessible dans le conteneur. J’utilise la stratégie de chargement du 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 depuis l’extérieur du conteneur car il est inclus dans l’image.

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 la suivante:

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

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

Le docker volume ls mon docker volume ls ressemble à ceci:

 DRIVER VOLUME NAME local project1_mysql local project1_node_modules local project2_postgresql local project2_node_modules 

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

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

MISE À JOUR: Utilisez la solution fournie 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 donc si vous ne l’avez pas en local).

En raison de paquets npm incompatibles basés sur un système d’exploitation, je ne pouvais pas installer les modules localement – puis lancer le conteneur, donc ..

Ma solution à ce problème était d’emballer la source dans un dossier src , puis de lier node_modules dans ce dossier, en utilisant ce fichier index.js . Donc, le fichier index.js est maintenant le sharepoint départ de mon application.

Lorsque je lance le conteneur, j’ai monté le dossier /app/src dans mon dossier src local.

Ainsi, le dossier conteneur ressemble à ceci:

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

C’est moche , mais ça marche ..

En raison de la façon dont Node.js charge les modules , node_modules peut se trouver n’importe où dans le chemin d’access à votre code source. Par exemple, placez votre source dans /worker/src et votre package.json dans /worker , donc /worker/node_modules est l’endroit où ils sont installés.

Il y a une solution élégante:

Il suffit de monter pas tout le répertoire, mais seulement 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 

L’installation de node_modules dans un conteneur différent du dossier du projet, et la définition de NODE_PATH dans votre dossier node_modules m’aide (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 

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

Inconvénient de cette approche:

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

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

Il y a deux exigences distinctes que je vois pour les environnements de développement de nœud … montez votre code source dans le conteneur et montez les node_modules FROM le conteneur (pour votre IDE). Pour accomplir le premier, vous faites la monte 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 entre les exécutions, ce qui signifie que vous pouvez diverger de ce qui se trouve réellement dans l’image (ne pas simplement lier le assembly à votre hôte)).

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

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

A mon avis, il ne faut pas RUN npm install NPM dans le Dockerfile. 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