Ich habe eine App mit folgenden Diensten:
web/
- hält einen python 3 flask Web-Server auf Port 5000. Verwendet sqlite3.worker/
- hat eine index.js
- Datei, die ein Arbeiter für eine Warteschlange ist. Der Webserver interagiert mit dieser Warteschlange unter Verwendung einer JSON-API über Port 9730
. Der Arbeiter verwendet redis zur Speicherung. Der Worker speichert die Daten auch lokal im Ordner worker/images/
Diese Frage betrifft jetzt nur den 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
Wenn ich docker-compose build
Ausführe, funktioniert alles wie erwartet und alle npm-Module werden wie erwartet in /worker/node_modules
Installiert.
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>
Aber wenn ich docker-compose up
Mache, sehe ich diesen Fehler:
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)
Es stellt sich heraus, dass keines der Module in /worker/node_modules
Vorhanden ist (auf dem Host oder im Container).
Wenn ich auf dem Host npm install
Bin, funktioniert alles einwandfrei. Das will ich aber nicht. Ich möchte, dass der Container Abhängigkeiten verarbeitet.
Was läuft hier falsch?
(Selbstverständlich sind alle Pakete in package.json
.)
Dies geschieht, weil Sie Ihr worker
-Verzeichnis als Volume zu Ihrem docker-compose.yml
Hinzugefügt haben, da das Volume während des Builds nicht bereitgestellt wird.
Wenn Docker das Image erstellt, wird das Verzeichnis node_modules
Im Verzeichnis worker
erstellt, und alle Abhängigkeiten werden dort installiert. Dann wird zur Laufzeit das Verzeichnis worker
von außerhalb des Dockers in die Docker-Instanz eingebunden (auf der node_modules
Nicht installiert ist), wobei das gerade installierte node_modules
Ausgeblendet wird. Sie können dies überprüfen, indem Sie das gemountete Volume aus Ihrem docker-compose.yml
Entfernen.
Eine Problemumgehung besteht darin, ein Daten-Volume zum Speichern des gesamten node_modules
Zu verwenden, während Daten-Volumes in die Daten aus dem erstellten Docker-Image kopiert werden, bevor das Verzeichnis worker
bereitgestellt wird. Dies kann in docker-compose.yml
Folgendermaßen erfolgen:
redis:
image: redis
worker:
build: ./worker
command: npm start
ports:
- "9730:9730"
volumes:
- worker/:/worker/
- /worker/node_modules
links:
- redis
Ich bin nicht ganz sicher, ob dies Probleme für die Portabilität des Images mit sich bringt, aber da Sie anscheinend hauptsächlich Docker verwenden, um eine Laufzeitumgebung bereitzustellen, sollte dies kein Problem sein.
Wenn Sie mehr über Bände lesen möchten, finden Sie hier ein Nizza-Benutzerhandbuch: https://docs.docker.com/userguide/dockervolumes/
Das node_modules
Ordner wird vom Volume überschrieben und ist im Container nicht mehr verfügbar. Ich benutze die native Modul-Ladestrategie , um den Ordner vom Volume zu entfernen:
/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
ist von außerhalb des Containers nicht zugänglich, da es im Bild enthalten ist.
Die von @FrederikNS bereitgestellte Lösung funktioniert, aber ich bevorzuge es, mein node_modules-Volume explizit zu benennen.
Meine project/docker-compose.yml
Datei (Docker-Compose Version 1.6+):
version: '2'
services:
frontend:
....
build: ./worker
volumes:
- ./worker:/worker
- node_modules:/worker/node_modules
....
volumes:
node_modules:
meine dateistruktur ist:
project/
│── worker/
│ └─ Dockerfile
└── docker-compose.yml
Es erstellt ein Volume mit dem Namen project_node_modules
Und verwendet es jedes Mal, wenn ich meine Anwendung öffne.
Mein docker volume ls
Sieht so aus:
DRIVER VOLUME NAME
local project1_mysql
local project1_node_modules
local project2_postgresql
local project2_node_modules
Ich hatte kürzlich ein ähnliches Problem. Sie können node_modules
An anderer Stelle installieren und die Umgebungsvariable NODE_PATH
Festlegen.
Im folgenden Beispiel habe ich node_modules
In /install
Installiert.
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
Es gibt eine elegante Lösung:
Mounten Sie einfach nicht das gesamte Verzeichnis, sondern nur das App-Verzeichnis. Auf diese Weise haben Sie keine Probleme mit npm_modules
.
Beispiel:
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
Ich bin auf dasselbe Problem gestoßen. Wenn der Ordner /worker
In den Container eingebunden wird, wird der gesamte Inhalt synchronisiert (der Ordner node_modules wird also ausgeblendet, wenn Sie ihn nicht lokal haben).
Aufgrund inkompatibler npm-Pakete, die auf dem Betriebssystem basieren, konnte ich die Module nicht einfach lokal installieren - und dann den Container starten.
Meine Lösung für dieses Problem bestand darin, die Quelle in einen src
-Ordner zu packen und dann node_modules
Mit diese index.js-Datei in diesen Ordner zu verlinken. Die Datei index.js
Ist nun der Ausgangspunkt meiner Anwendung.
Beim Ausführen des Containers habe ich den Ordner /app/src
In meinen lokalen Ordner src
geladen.
Der Containerordner sieht also ungefähr so aus:
/app
/node_modules
/src
/node_modules -> ../node_modules
/app.js
/index.js
Es ist hässlich , aber es funktioniert ..
Aufgrund von wie Node.js Module lädt , node_modules
kann sich an einer beliebigen Stelle im Pfad zu Ihrem Quellcode befinden. Setzen Sie Ihre Quelle zum Beispiel auf /worker/src
und dein package.json
im /worker
, damit /worker/node_modules
ist, wo sie installiert sind.
Das Installieren von node_modules im Container in einem anderen als dem Projektordner und das Festlegen von NODE_PATH in Ihrem node_modules-Ordner hilft mir (Sie müssen den Container neu erstellen).
Ich benutze Docker-Compose. Meine Projektdateistruktur:
-/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 im nodejs Ordner:
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
Es gibt auch eine einfache Lösung ohne Zuordnung node_module
Verzeichnis in ein anderes Volume. Es geht darum, die Installation von npm-Paketen in den endgültigen CMD-Befehl zu verschieben.
Nachteil dieses Ansatzes:
- lauf
npm install
jedes Mal, wenn Sie Container ausführen (das Umschalten vonnpm
aufyarn
kann diesen Vorgang auch etwas beschleunigen).
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
Es gibt zwei unterschiedliche Anforderungen für Node-Dev-Umgebungen: Mounten Sie Ihren Quellcode in den Container und mounten Sie die node_modules aus dem Container (für Ihre IDE). Um das erste zu erreichen, machst du das übliche Mount, aber nicht alles ... nur die Dinge, die du brauchst
volumes:
- worker/src:/worker/src
- worker/package.json:/worker/package.json
- etc...
(der Grund, nicht zu tun - /worker/node_modules
liegt daran, dass docker-compose dieses Volume zwischen den Durchläufen beibehält, was bedeutet, dass Sie von dem abweichen können, was sich tatsächlich im Image befindet (um nicht nur die Bereitstellung von Ihrem Host zu binden).
Der zweite ist tatsächlich schwieriger. Meine Lösung ist ein bisschen hackisch, aber es funktioniert. Ich habe ein Skript, um den Ordner node_modules auf meinem Host-Computer zu installieren, und ich muss nur daran denken, es aufzurufen, wenn ich package.json aktualisiere (oder es dem make-Ziel hinzufüge, das docker-compose build lokal ausführt).
install_node_modules:
docker build -t building .
docker run -v `pwd`/node_modules:/app/node_modules building npm install
Meiner Meinung nach sollten wir nicht RUN npm install
in der Dockerfile. Stattdessen können wir einen Container mit bash starten, um die Abhängigkeiten zu installieren, bevor wir den formalen Knotendienst ausführen
docker run -it -v ./app:/usr/src/app your_node_image_name /bin/bash
[email protected]:/usr/src/app# npm install
Sie können so etwas in Ihrem Dockerfile ausprobieren:
FROM node:0.12
WORKDIR /worker
CMD bash ./start.sh
Dann solltest du das Volume so benutzen:
volumes:
- worker/:/worker:rw
Das Startscript sollte Teil Ihres Worker-Repositorys sein und wie folgt aussehen:
#!/bin/sh
npm install
npm start
Die node_modules sind also Teil Ihres Worker-Volumes und werden synchronisiert und die npm-Skripte werden ausgeführt, wenn alles läuft.