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
.)
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/
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.
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
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
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/
redis:
image: redis
worker:
build: ./worker
command: npm start
ports:
- "9730:9730"
volumes:
- worker/:/worker/
links:
- redis
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
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 ..
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.
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
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 denpm
àyarn
pourrait également accélérer un peu ce processus).
FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
COPY . /worker/
CMD /bin/bash -c 'npm install; npm start'
redis:
image: redis
worker:
build: ./worker
ports:
- "9730:9730"
volumes:
- worker/:/worker/
links:
- redis
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
À 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
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.