node.js - docker compose nodejs
Docker-compose: node_modules no está presente en un volumen después de que la instalación de npm se realice correctamente (12)
Tengo una aplicación con los siguientes servicios:
-
web/
- mantiene y ejecuta un servidor web de matraz python 3 en el puerto 5000. Utiliza sqlite3. -
worker/
- tiene un archivoindex.js
que es un trabajador para una cola. el servidor web interactúa con esta cola utilizando una API json a través del puerto9730
. El trabajador usa redis para el almacenamiento. El trabajador también almacena datos localmente en la carpetaworker/images/
Ahora esta pregunta solo concierne al
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
Cuando ejecuto
docker-compose build
, todo funciona como se esperaba y todos los módulos npm se instalan en
/worker/node_modules
como esperaba.
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>
Pero cuando
docker-compose up
, veo este error:
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)
Resulta que ninguno de los módulos está presente en
/worker/node_modules
(en el host o en el contenedor).
Si en el host,
npm install
, entonces todo funciona bien.
Pero no quiero hacer eso.
Quiero que el contenedor maneje las dependencias.
¿Qué está pasando mal aquí?
(No hace falta decir que todos los paquetes están en
package.json
).
ACTUALIZACIÓN: utilice la solution proporcionada por @FrederikNS.
Encontré el mismo problema.
Cuando la carpeta
/worker
se monta en el contenedor, todo su contenido se sincronizará (por lo que la carpeta node_modules desaparecerá si no lo tiene localmente).
Debido a los paquetes npm incompatibles basados en el sistema operativo, no pude simplemente instalar los módulos localmente, luego iniciar el contenedor, así que ...
Mi solución a esto fue envolver la fuente en una carpeta
src
, luego vincular
node_modules
a esa carpeta, usando
este archivo index.js
.
Entonces, el archivo
index.js
es ahora el punto de partida de mi aplicación.
Cuando ejecuto el contenedor, monté la carpeta
/app/src
en mi carpeta
src
local.
Entonces la carpeta del contenedor se ve así:
/app
/node_modules
/src
/node_modules -> ../node_modules
/app.js
/index.js
Es feo , pero funciona.
Debido a
la forma en que Node.js carga los módulos
,
node_modules
puede estar en cualquier parte de la ruta a su código fuente.
Por ejemplo, coloque su fuente en
/worker/src
y su
package.json
en
/worker
, entonces
/worker/node_modules
es donde están instalados.
El volumen sobrescribe la carpeta
node_modules
y ya no se puede acceder a ella en el contenedor.
Estoy usando la
estrategia de carga del módulo nativo
para extraer la carpeta del volumen:
/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
se puede acceder a
node_modules
desde fuera del contenedor porque está incluido en la imagen.
En mi opinión, no deberíamos
RUN npm install
en el Dockerfile.
En cambio, podemos iniciar un contenedor usando bash para instalar las dependencias antes de ejecutar el servicio de nodo formal
docker run -it -v ./app:/usr/src/app your_node_image_name /bin/bash
root@247543a930d6:/usr/src/app# npm install
Esto sucede porque ha agregado su directorio de
worker
como un volumen a su
docker-compose.yml
, ya que el volumen no se monta durante la compilación.
Cuando Docker construye la imagen, el directorio
node_modules
se crea dentro del directorio de
worker
y todas las dependencias se instalan allí.
Luego, en tiempo de ejecución, el directorio de
worker
desde la ventana acoplable externa se monta en la instancia de la ventana acoplable (que no tiene los
node_modules
instalados), ocultando los
node_modules
que acaba de instalar.
Puede verificar esto eliminando el volumen montado de su
docker-compose.yml
.
Una solución alternativa es usar un volumen de datos para almacenar todos los
node_modules
, ya que los volúmenes de datos copian los datos de la imagen acoplada construida antes de montar el directorio de
worker
.
Esto se puede hacer en el
docker-compose.yml
esta manera:
redis:
image: redis
worker:
build: ./worker
command: npm start
ports:
- "9730:9730"
volumes:
- worker/:/worker/
- /worker/node_modules
links:
- redis
No estoy completamente seguro de si esto impone algún problema para la portabilidad de la imagen, pero como parece que está utilizando principalmente Docker para proporcionar un entorno de tiempo de ejecución, esto no debería ser un problema.
Si desea leer más sobre los volúmenes, hay una buena guía de usuario disponible aquí: https://docs.docker.com/userguide/dockervolumes/
Hay dos requisitos separados que veo para los entornos de desarrollo de nodos ... monte su código fuente EN el contenedor y monte los node_modules DESDE el contenedor (para su IDE). Para lograr lo primero, haces el montaje habitual, pero no todo ... solo las cosas que necesitas
volumes:
- worker/src:/worker/src
- worker/package.json:/worker/package.json
- etc...
(la razón para no hacerlo
- /worker/node_modules
es porque docker-compose persistirá ese volumen entre ejecuciones, lo que significa que puede divergir de lo que realmente está en la imagen (lo que frustra el propósito de no solo vincular el montaje desde su host)).
El segundo es realmente más difícil. Mi solución es un poco hack, pero funciona. Tengo un script para instalar la carpeta node_modules en mi máquina host, y solo tengo que recordar llamarlo cada vez que actualizo package.json (o lo agrego al destino make que ejecuta la compilación docker-compose localmente).
install_node_modules:
docker build -t building .
docker run -v `pwd`/node_modules:/app/node_modules building npm install
Hay una solución elegante:
Simplemente monte no todo el directorio, sino solo el directorio de la aplicación.
De esta manera no tendrás problemas con
npm_modules
.
Ejemplo:
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
Instalar node_modules en el contenedor para que sea diferente de la carpeta del proyecto, y configurar NODE_PATH en su carpeta node_modules me ayuda (necesita reconstruir el contenedor).
Estoy usando docker-compose. Estructura del archivo de mi proyecto:
-/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 en la carpeta 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
La solución proporcionada por @FrederikNS funciona, pero prefiero nombrar explícitamente mi volumen node_modules.
Mi
project/docker-compose.yml
(docker-compose versión 1.6+):
version: ''2''
services:
frontend:
....
build: ./worker
volumes:
- ./worker:/worker
- node_modules:/worker/node_modules
....
volumes:
node_modules:
mi estructura de archivos es:
project/
│── worker/
│ └─ Dockerfile
└── docker-compose.yml
Crea un volumen llamado
project_node_modules
y lo reutiliza cada vez que
project_node_modules
mi aplicación.
Mi
docker volume ls
ve así:
DRIVER VOLUME NAME
local project1_mysql
local project1_node_modules
local project2_postgresql
local project2_node_modules
Puedes probar algo como esto en tu Dockerfile:
FROM node:0.12
WORKDIR /worker
CMD bash ./start.sh
Entonces deberías usar el Volumen así:
volumes:
- worker/:/worker:rw
El script de inicio debe ser parte de su repositorio de trabajo y se ve así:
#!/bin/sh
npm install
npm start
Entonces, los node_modules son parte de su volumen de trabajo y se sincronizan y los scripts npm se ejecutan cuando todo está activo.
Recientemente tuve un problema similar.
Puede instalar
node_modules
otro lugar y establecer la variable de entorno
NODE_PATH
.
En el ejemplo a continuación, instalé
node_modules
en
/install
trabajador / 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
También hay una solución simple sin asignar el directorio
node_module
a otro volumen.
Está a punto de mover la instalación de paquetes npm al comando CMD final.
Desventaja de este enfoque:
- ejecute
npm install
cada vez que ejecute el contenedor (cambiar denpm
ayarn
también podría acelerar un poco este proceso).
trabajador / 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