Environnement de développement Node.js dans Docker sous Windows

J’ai essayé tout ce que je peux penser. J’ai lu les docs, les blogs et essayé les échantillons suivants sur github.

Mais je n’arrive pas à le faire fonctionner.

Ce que je veux faire est simple. Je veux écrire mon code node.js sur ma machine Windows 8.1, et je veux également exécuter le code depuis un conteneur Docker sans avoir à reconstruire le conteneur tout le temps. Je veux donc mapper un répertoire sur mon hôte Windows vers un répertoire à l’intérieur du conteneur.

J’ai créé ce fichier Dockerfile

FROM node:0.10.38 RUN apt-get update -qq && apt-get install -y build-essential ENV ZMQ_VERSION 4.1.3 ENV LIBSODIUM_VERSION 1.0.3 RUN curl -SLO "https://download.libsodium.org/libsodium/releases/libsodium-$LIBSODIUM_VERSION.tar.gz" \ && tar xvf libsodium-$LIBSODIUM_VERSION.tar.gz \ && cd libsodium-$LIBSODIUM_VERSION \ && ./configure \ && make \ && make install \ && cd .. \ && rm -r libsodium-$LIBSODIUM_VERSION \ && rm libsodium-$LIBSODIUM_VERSION.tar.gz RUN curl -SLO "http://download.zeromq.org/zeromq-$ZMQ_VERSION.tar.gz" \ && tar xvf zeromq-$ZMQ_VERSION.tar.gz \ && cd zeromq-$ZMQ_VERSION \ && ./configure \ && make \ && make install \ && cd .. \ && rm -r zeromq-$ZMQ_VERSION \ && rm zeromq-$ZMQ_VERSION.tar.gz RUN ldconfig COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] RUN mkdir -p /usr/src/app ADD . /usr/src/app WORKDIR /usr/src/app RUN npm install EXPOSE 3000 EXPOSE 35729 CMD ["npm", "start"] 

J’ai ce simple fichier server.js

 var express = require('express'); var app = express(); var zmq = require('zmq'); app.get('/', function (req, res) { res.send('ZMQ: ' + zmq.version); }); var server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port); }); 

Et ce simple package.json

 { "name": "docker-node-hello-world", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "start": "node server.js" }, "author": "", "license": "ISC", "dependencies": { "express": "^4.13.3", "zmq": "^2.14.0" } } 

J’ai installé la dernière boîte à outils Docker, et je peux exécuter l’exemple de Docker Hello World. J’essaie de construire mon image de docker comme ça quand je suis dans le répertoire où se trouve mon fichier Dockerfile.

docker build -t dockernodetest:dockerfile .

J’essaie alors de l’exécuter, également à partir du même emplacement à l’intérieur du terminal Docker Quickstart. J’utilise le hachage car le tag ne prend pas pour une raison quelconque:

docker run -v //c/Bitbucket/docker-node-hello-world/:/usr/src/app -p 3000:3000 -i -t 9cfd34e046a5 ls ./usr/src/app

Le résultat est un répertoire vide. J’espérais pouvoir invoquer

docker run -v //c/Bitbucket/docker-node-hello-world/:/usr/src/app -p 3000:3000 -i -t 9cfd34e046a5 npm start

Mais comme le répertoire hôte n’est pas disponible, il échoue. J’ai l’impression d’avoir mal compris quelque chose de très basique. Je ne sais tout simplement pas quoi.

Commençons par le Dockerfile

 FROM node:0.10.38-onbuild RUN apt-get update -qq && apt-get install -y build-essential ENV ZMQ_VERSION 4.1.3 ENV LIBSODIUM_VERSION 1.0.3 RUN curl -SLO "https://download.libsodium.org/libsodium/releases/libsodium-$LIBSODIUM_VERSION.tar.gz" \ && tar xvf libsodium-$LIBSODIUM_VERSION.tar.gz \ && cd libsodium-$LIBSODIUM_VERSION \ && ./configure \ && make \ && make install \ && cd .. \ && rm -r libsodium-$LIBSODIUM_VERSION \ && rm libsodium-$LIBSODIUM_VERSION.tar.gz RUN curl -SLO "http://download.zeromq.org/zeromq-$ZMQ_VERSION.tar.gz" \ && tar xvf zeromq-$ZMQ_VERSION.tar.gz \ && cd zeromq-$ZMQ_VERSION \ && ./configure \ && make \ && make install \ && cd .. \ && rm -r zeromq-$ZMQ_VERSION \ && rm zeromq-$ZMQ_VERSION.tar.gz RUN ldconfig EXPOSE 3000 35729 

A partir de la ligne 1, j’ai utilisé la 0.10.38-onbuild car je veux tirer parti des scripts onbuild qui vont exécuter le /usr/src/app et exécuter npm install

Ensuite, server.js et package.json sont tels que vous les avez écrits. Celles-ci se trouvent toutes deux dans le même répertoire de travail que le Dockerfile .

Ensuite, nous construisons l’image

 docker build -t dockernodetest . 

J’ai omis la balise dockerfile car cela semblait inutile. Le client appenda automatiquement un latest tag de toute façon. Pour voir quelles images vous avez localement exécutez des docker images .

A ce stade, nous devrions avoir une image prête à fonctionner mais vérifions d’abord que les fichiers que nous voulions charger sont alors et que npm install créé le répertoire node_modules

 $ docker run dockernodetest ls /usr/src/app Dockerfile node_modules package.json server.js 

Nous sums prêts à utiliser notre petite application nodejs

 $ docker run -it -p 8080:3000 dockernodetest > [email protected] start /usr/src/app > node server.js Example app listening at http://0.0.0.0:3000 

Dans ce cas, j’ai utilisé l’ -p 8080:3000 pour mapper le port 3000 du conteneur sur le port 8080 de ma machine hôte. Notez que je n’avais pas d’autres commandes à la fin car l’image -onbuild j’ai tiré le formulaire a un CMD [ "npm", "start" ] et l’action par défaut est d’exécuter le script du package de start .

Donc, pour accélérer le cycle de développement, vous devez monter votre répertoire de travail sur le conteneur via l’option -v

 $ docker run -it -p 8080:3000 -v "$PWD":/usr/src/app dockernodetest > [email protected] start /usr/src/app > node server.js module.js:340 throw err; ^ Error: Cannot find module 'express' at Function.Module._resolveFilename (module.js:338:15) at Function.Module._load (module.js:280:25) at Module.require (module.js:364:17) at require (module.js:380:17) at Object. (/usr/src/app/server.js:1:77) at Module._comstack (module.js:456:26) at Object.Module._extensions..js (module.js:474:10) at Module.load (module.js:356:32) at Function.Module._load (module.js:312:12) at Function.Module.runMain (module.js:497:10) npm ERR! Linux 4.1.7-15.23.amzn1.x86_64 npm ERR! argv "node" "/usr/local/bin/npm" "start" npm ERR! node v0.10.38 npm ERR! npm v2.11.1 npm ERR! code ELIFECYCLE npm ERR! [email protected] start: `node server.js` npm ERR! Exit status 8 

Mais qu’est-ce qui s’est passé ici? Comme nous avons monté le répertoire de travail actuel, il a écrasé ce qui existait auparavant dans /usr/src/app y compris notre répertoire node_modules .

La solution rapide consiste maintenant à exécuter une npm install dans notre répertoire de travail actuel et à réexécuter la commande docker run .

 $ npm install [email protected] /home/ec2-user/dockernode └─┬ [email protected] ├─┬ [email protected] │ ├─┬ [email protected] ... $ docker run -it -p 8080:3000 -v "$PWD":/usr/src/app dockernodetest > [email protected] start /usr/src/app > node server.js Example app listening at http://0.0.0.0:3000 

Maintenant, si vous faites une mise à jour de votre fichier server.js , appuyez simplement sur Ctrl + c et redémarrez l’image de votre docker, mais vous voudrez peut-être utiliser quelque chose comme nodemon pour le rendre encore plus transparent.

Merci beaucoup pour votre réponse. Cela m’a définitivement conduit sur le bon chemin. Cela m’a pris plus de temps que je ne l’avais pensé, mais je l’ai fait fonctionner.

Il y avait quelques petites choses qui ne marchaient pas, mais j’ai enfin réussi. Voici un aperçu de ce que j’ai vécu.

  1. Je n’ai pas pu utiliser les scripts onbuild comme vous l’avez suggéré. La raison en est que lorsque je base mon image sur le script onbuild, il s’exécute npm install avant d’installer ZeroMQ.

  2. L’exécution de npm install dehors de l’image de mon menu fixe n’était pas une option non plus, comme vous l’avez suggéré lorsque j’ai mappé un lecteur. L’une des raisons pour lesquelles je veux l’exécuter dans Docker est que je veux un environnement où toutes les dépendances (telles que ZeroMQ) sont installées et disponibles. C’est quelque chose qui est difficile sur certains environnements et je veux l’exécuter à la fois sur les hôtes Windows et Linux.

  3. Je veux vraiment utiliser nodemon pour le développement, aussi j’ai dû l’installer globalement dans l’image et aussi appeler nodemon au démarrage du conteneur. J’ai donc dû étendre votre exemple comme vous l’avez suggéré.

  4. J’ai eu beaucoup de mal à faire fonctionner les volumes mappés. Il s’avère que sur un hôte Windows, vous devez être dans le contexte de C:\users\ pour pouvoir mapper des volumes sur l’hôte à partir du conteneur. Une fois que j’ai compris cela et que j’ai défait toutes les expériences étranges que j’avais traversées pour que tout fonctionne, j’ai réussi. Utiliser "$PWD" comme vous l’avez suggéré lors de l’appel de docker run est également étrange sur un hôte Windows. Vous devez préfixer avec une barre oblique comme ceci: /"$PWD" .

  5. Quand j’ai tout compris, j’ai commencé à regarder docker composer, en partie parce que je ne voulais pas continuer à taper les longues commandes de docker (que je me trompais) mais aussi parce que dans mes projets réels je voulais plusieurs conteneurs pour ma firebase database et d’autres services dont j’ai besoin.

Voilà à quoi ça ressemble maintenant. Cela fonctionne exactement comme je le veux. J’ai maintenant un conteneur où toutes les dépendances sont installées à l’intérieur du conteneur et chaque fois que nodemon server.js mon conteneur, il appelle d’abord npm install et nodemon server.js . Tous les fichiers, y compris les modules npm installés, sont sur l’hôte mais mappés dans le conteneur à partir duquel tout est exécuté.

Fichier 1 – docker-compose.yml (notez que je n’ai pas besoin de la variable $ PWD, mais que je peux simplement utiliser un chemin relatif pour le chemin de l’hôte)

 web: build: . volumes: - ".:/usr/src/app" ports: - "3000:3000" 

Fichier 2 – Dockerfile

 FROM node:0.10.40 RUN mkdir /usr/src/app RUN mkdir /usr/src/node_modules RUN apt-get update -qq && apt-get install -y build-essential ENV ZMQ_VERSION 4.1.3 ENV LIBSODIUM_VERSION 1.0.3 RUN curl -SLO "https://download.libsodium.org/libsodium/releases/libsodium-$LIBSODIUM_VERSION.tar.gz" \ && tar xvf libsodium-$LIBSODIUM_VERSION.tar.gz \ && cd libsodium-$LIBSODIUM_VERSION \ && ./configure \ && make \ && make install \ && cd .. \ && rm -r libsodium-$LIBSODIUM_VERSION \ && rm libsodium-$LIBSODIUM_VERSION.tar.gz RUN curl -SLO "http://download.zeromq.org/zeromq-$ZMQ_VERSION.tar.gz" \ && tar xvf zeromq-$ZMQ_VERSION.tar.gz \ && cd zeromq-$ZMQ_VERSION \ && ./configure \ && make \ && make install \ && cd .. \ && rm -r zeromq-$ZMQ_VERSION \ && rm zeromq-$ZMQ_VERSION.tar.gz RUN ldconfig RUN npm install -g nodemon WORKDIR /usr/src/app CMD export NODE_PATH=/usr/src/node_modules && cp package.json /usr/src && npm install --prefix /usr/src && npm start EXPOSE 3000 35729 

Fichier 3 – package.json (notez que j’utilise l’option -L lorsque j’appelle nodemon. Cela est nécessaire lors de l’exécution dans un conteneur)

 { "name": "docker-node-hello-world", "version": "1.0.0", "description": "node hello world", "main": "server.js", "scripts": { "start": "nodemon -L server.js" }, "author": "Author", "license": "ISC", "dependencies": { "express": "^4.13.3", "zmq": "^2.14.0" } } 

Fichier 4 – server.js

 var express = require('express'); var app = express(); var zmq = require('zmq'); app.get('/', function (req, res) { res.send('ZMQ Version: ' + zmq.version); }); var server = app.listen(3000, function () { var host = server.address().address; var port = server.address().port; console.log('Example app listening at http://%s:%s', host, port); }); 

Pour construire, utilisez la commande suivante

 docker-compose build 

Pour exécuter, utilisez la commande suivante

 docker-compose up 

À ce stade, je peux développer mon application sur mon hôte et chaque fois que je change de fichier, mon serveur de nœud est redémarré. Lorsque j’ajoute une nouvelle dépendance à mon package.json, je dois redémarrer mon conteneur pour exécuter npm install .

La seule chose que je n’ai pas encore de travail, c’est comment faire de ma reconstruction un bon fonctionnement. Qu’est-ce qui se passe est que, après avoir créé une nouvelle image, je dois supprimer mon ancien conteneur avant de pouvoir exécuter un nouveau conteneur. Le mappage est verrouillé par l’ancien conteneur.

Je voudrais vraiment vous donner le crédit @jeedo. Je n’aurais pas atteint cette solution sans votre aide. Je ne sais pas comment faire cela et marque également cela comme la solution.

Edit: Je viens d’éditer ceci pour append quelque chose à mon fichier Docker. J’ai déplacé le dossier node_modules du système de fichiers hôte. J’ai eu des problèmes avec les chemins devenant trop longs sous Windows. Ces modifications permettent de s’assurer que les modules node_modules sont toujours installés à l’intérieur du conteneur.