web-dev-qa-db-fra.com

Installez node_modules dans le conteneur Docker et synchronisez-les avec l'hôte.

J'ai le problème d'installer node_modules Dans le conteneur Docker et de les synchroniser avec l'hôte. La version de mon Docker est 18.03.1-ce, build 9ee9f40 Et celle de Docker Compose est 1.21.2, build a133471.

Mon docker-compose.yml Ressemble à ceci:

# Frontend Container.
frontend:
  build: ./app/frontend
  volumes:
    - ./app/frontend:/usr/src/app
    - frontend-node-modules:/usr/src/app/node_modules
  ports:
    - 3000:3000
  environment:
    NODE_ENV: ${ENV}
  command: npm start

# Define all the external volumes.
volumes:
  frontend-node-modules: ~

Mon Dockerfile:

# Set the base image.
FROM node:10

# Create and define the working directory.
RUN mkdir /usr/src/app
WORKDIR /usr/src/app

# Install the application's dependencies.
COPY package.json ./
COPY package-lock.json ./
RUN npm install

L'astuce avec le volume externe est décrite dans de nombreux articles de blog et réponses Stack Overflow. Par exemple, celui-ci .

L'application fonctionne très bien. Le code source est synchronisé. Le rechargement à chaud fonctionne également très bien.

Le seul problème que j'ai est que le dossier node_modules Est vide sur l'hôte. Est-il possible de synchroniser le dossier node_modules Qui se trouve dans le conteneur Docker avec l'hôte?

J'ai déjà lu ces réponses:

  1. le volume docker-compose sur node_modules mais est vide
  2. Accès à node_modules après l’installation de npm dans Docker

Malheureusement, ils ne m'ont pas beaucoup aidé. Je n'aime pas le premier , car je ne souhaite pas exécuter npm install Sur mon hôte à cause des problèmes multi-plateformes possibles (par exemple, l'hôte est Windows ou Mac et le conteneur Docker est Debian 8 ou Ubuntu 16.04). Le second n'est pas bon pour moi aussi, parce que j'aimerais exécuter npm install Dans mon Dockerfile au lieu de l'exécuter après le démarrage du conteneur Docker.

En outre, j'ai trouvé cet article de blog . L'auteur tente de résoudre le même problème auquel je suis confronté. Le problème est que node_modules Ne sera pas synchronisé car nous les copions simplement du conteneur Docker vers l'hôte.

J'aimerais que mon node_modules À l'intérieur du conteneur Docker soit synchronisé avec l'hôte. S'il vous plaît, prenez en compte que je veux:

  • installer node_modules automatiquement au lieu de manuellement
  • installer node_modules dans le conteneur Docker à la place de l'hôte
  • pour que node_modules soit synchronisé avec l'hôte (si j'installe un nouveau package dans le conteneur Docker, il doit être synchronisé automatiquement avec l'hôte, sans aucune action manuelle)

J'ai besoin de node_modules Sur l'hôte, car:

  • possibilité de lire le code source quand j'en ai besoin
  • le IDE a besoin de node_modules pour être installé localement afin de pouvoir accéder au devDependencies tel que eslint ou prettier. Je ne veux pas installer ces devDependencies globalement.

Merci d'avance.

27
Vladyslav Turak

Au début, je voudrais remercier David Maze et trust512 pour avoir posté leurs réponses. Malheureusement, ils ne m'ont pas aidé à résoudre mon problème.

Je voudrais poster ma réponse à cette question.

Mon docker-compose.yml:

---
# Define Docker Compose version.
version: "3"

# Define all the containers.
services:
  # Frontend Container.
  frontend:
    build: ./app/frontend
    volumes:
      - ./app/frontend:/usr/src/app
    ports:
     - 3000:3000
    environment:
      NODE_ENV: development
    command: /usr/src/app/entrypoint.sh

Mon Dockerfile:

# Set the base image.
FROM node:10

# Create and define the node_modules's cache directory.
RUN mkdir /usr/src/cache
WORKDIR /usr/src/cache

# Install the application's dependencies into the node_modules's cache directory.
COPY package.json ./
COPY package-lock.json ./
RUN npm install

# Create and define the application's working directory.
RUN mkdir /usr/src/app
WORKDIR /usr/src/app

Et le dernier mais non le moindre entrypoint.sh:

#!/bin/bash

cp -r /usr/src/cache/node_modules/. /usr/src/app/node_modules/
exec npm start

La partie la plus délicate ici consiste à installer le node_modules Dans le répertoire de cache de node_module (/usr/src/cache) Défini dans notre Dockerfile. Après cela, entrypoint.sh Déplacera le node_modules Du répertoire de cache (/usr/src/cache) Vers notre répertoire d'application (/usr/src/app). Grâce à cela, tout le répertoire node_modules Apparaîtra sur notre ordinateur hôte.

En regardant ma question ci-dessus je voulais:

  • installer node_modules automatiquement au lieu de manuellement
  • installer node_modules dans le conteneur Docker à la place de l'hôte
  • pour que node_modules soit synchronisé avec l'hôte (si j'installe un nouveau package dans le conteneur Docker, il doit être synchronisé automatiquement avec l'hôte, sans aucune action manuelle.

La première chose est faite: node_modules Sont installés automatiquement. La deuxième chose est faite aussi: node_modules Sont installés à l'intérieur du conteneur Docker (donc, il n'y aura pas de problèmes multi-plateformes). Et la troisième chose est faite aussi: node_modules Qui ont été installés à l'intérieur du conteneur Docker seront visibles sur notre ordinateur hôte et ils seront synchronisé ! Si nous installons un nouveau package dans le conteneur Docker, il sera immédiatement synchronisé avec notre ordinateur hôte.

La chose importante à noter: à vrai dire, le nouveau paquet installé dans le conteneur Docker apparaîtra dans /usr/src/app/node_modules. Comme ce répertoire est synchronisé avec notre ordinateur hôte, ce nouveau package apparaîtra également dans le répertoire node_modules De notre ordinateur hôte. Mais le /usr/src/cache/node_modules Aura l'ancienne construction à ce stade (sans ce nouveau paquet). Quoi qu'il en soit, ce n'est pas un problème pour nous. Au cours de la prochaine opération docker-compose up --build (--build Est requis), Docker réinstallera le node_modules (Car package.json A été modifié) et le entrypoint.sh Le fichier les déplacera dans notre /usr/src/app/node_modules.

Vous devriez prendre en compte une autre chose importante. Si vous git pull Le code du référentiel distant ou git checkout your-teammate-branch Lorsque Docker est en cours d'exécution, de nouveaux packages peuvent être ajoutés au fichier package.json. Dans ce cas, vous devez arrêter Docker avec CTRL + C Et le relancer avec docker-compose up --build (--build Est requis). Si vos conteneurs fonctionnent en tant que démon, vous devez simplement exécuter docker-compose stop Pour arrêter les conteneurs et le relancer avec docker-compose up --build (--build Est requis).

Si vous avez des questions, s'il vous plaît laissez-moi savoir dans les commentaires.

J'espère que cela t'aides.

24
Vladyslav Turak

Il y a trois choses qui se passent ici:

  1. Lorsque vous exécutez docker build Ou docker-compose build, Votre fichier Dockerfile crée une nouvelle image contenant un répertoire /usr/src/app/node_modules Et une installation Node, mais rien d'autre. En particulier, votre application ne figure pas dans l'image construite.
  2. Lorsque vous docker-compose up, La directive volumes: ['./app/frontend:/usr/src/app'] Masque tout ce qui se trouvait dans /usr/src/app Et monte le contenu du système hôte par dessus.
  3. Ensuite, la directive volumes: ['frontend-node-modules:/usr/src/app/node_modules'] Monte le volume nommé au-dessus de l’arborescence node_modules, En masquant le répertoire du système hôte correspondant.

Si vous deviez lancer un autre conteneur et y attacher le volume nommé, vous vous attendriez à voir l'arborescence node_modules. Pour ce que vous décrivez, vous ne voulez simplement pas le volume nommé: supprimez la deuxième ligne du bloc volumes: Et de la section volumes: À la fin du fichier docker-compose.yml .

5
David Maze

Je sais que cela a été résolu, mais qu'en est-il:

Dockerfile:

FROM node

# Create app directory
WORKDIR /usr/src/app

# Your other staffs

EXPOSE 3000

docker-composer.yml:

version: '3.2'
services:
    api:
        build: ./path/to/folder/with/a/dockerfile
        volumes:
            - "./volumes/app:/usr/src/app"
        command: "npm start"

volumes/app/package.json

{
    ... ,
    "scripts": {
        "start": "npm install && node server.js"
    },
    "dependencies": {
        ....
    }
 }

Après exécution, node_modules sera présent dans vos volumes, mais son contenu est généré dans le conteneur afin d'éviter tout problème de multiplateforme.

2
Ignacio

Je ne suggérerais pas que des volumes se chevauchent, bien que je n'ai jamais vu de documents officiels l'interdire, j'ai déjà eu des problèmes avec cela par le passé. Comment je le fais est:

  1. Supprimez le volume externe, car vous ne prévoyez pas de l'utiliser tel qu'il est censé être utilisé - en renouant avec le conteneur avec ses données créées spécifiquement dans le conteneur après l'avoir arrêté + supprimé.

Ce qui précède peut être obtenu en raccourcissant un peu votre fichier de composition:

frontend:
  build: ./app/frontend
  volumes:
    - ./app/frontend:/usr/src/app
  ports:
    - 3000:3000
  environment:
    NODE_ENV: ${ENV}
  command: npm start
  1. Évitez le chevauchement des données de volume avec les instructions Dockerfile lorsque cela n'est pas nécessaire.

Cela signifie que vous aurez peut-être besoin de deux fichiers Dockerfiles: un pour le développement local et un pour le déploiement d'une image grasse avec tous les fichiers dist de l'application superposés.

Cela dit, considérons un développement Dockerfile:

FROM node:10
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN npm install

Ce qui précède permet à l'application de créer une installation complète de node_modules et de la mapper sur votre emplacement hôte, tandis que la commande spécifiée par docker composerait le démarrage de votre application.

2
trust512

Personne n'a mentionné de solution avec l'utilisation de la fonctionnalité entrypoint de docker.

Voici ma solution de travail:

Dockerfile (construction à plusieurs étages, donc production et développement local prêt):

FROM node:10.15.3 as production
WORKDIR /app

COPY package*.json ./
RUN npm install && npm install --only=dev

COPY . .

RUN npm run build

EXPOSE 3000

CMD ["npm", "start"]


FROM production as dev

COPY docker/dev-entrypoint.sh /usr/local/bin/

ENTRYPOINT ["dev-entrypoint.sh"]
CMD ["npm", "run", "watch"]

docker/dev-entrypoint.sh:

#!/bin/sh
set -e

npm install && npm install --only=dev ## Note this line, rest is copy+paste from original entrypoint

if [ "${1#-}" != "${1}" ] || [ -z "$(command -v "${1}")" ]; then
  set -- node "$@"
fi

exec "$@"

docker-compose.yml:

version: "3.7"

services:
    web:
        build:
            target: dev
            context: .
        volumes:
            - .:/app:delegated
        ports:
            - "3000:3000"
        restart: always
        environment:
            NODE_ENV: dev

Avec cette approche, vous atteignez les 3 points que vous souhaitez et à mon humble avis, c’est beaucoup plus propre - il n’est pas nécessaire de déplacer des fichiers.

2
Jan Mikeš

Merci Vladyslav Turak pour la réponse avec entrypoint.sh où on copie node_modules de conteneur à hôte.

J'ai mis en œuvre la même chose, mais je rencontre le problème avec les packages husky, @commitlint, tslint npm.
Je ne peux rien insérer dans le référentiel.
Raison: j'ai copié node_modules de Linux à Windows. Dans mon cas, moins de 5% des fichiers sont différents (.bin et la plupart des fichiers package.json) et 95% sont identiques. exemple: image avec diff

Je suis donc retourné à la solution avec npm install de node_modules pour Windows d’abord (pour IDE et débogage). Et l’image Docker contiendra la version Linux de node_modules.

1
user5367575

Associer votre dossier Host node_modules à votre conteneur node_modules n'est pas une bonne pratique, comme vous le mentionnez. J'ai souvent vu la solution consistant à créer un volume interne pour ce dossier. Ne pas le faire va causer des problèmes pendant la phase de construction.

J'ai rencontré ce problème lorsque j'essayais de créer un environnement de développement de menu fixe pour une angular), qui affiche des erreurs tslib lorsque j'édiais les fichiers de mon dossier Host car le dossier node_modules de mon hôte était vide. (comme prévu).

La solution peu coûteuse qui m'aidait, dans ce cas, était d'utiliser l'extension de code Visual Studio appelée "Conteneurs distants" .

Cette extension vous permettra d’attacher votre code Visual Studio à votre conteneur et d’éditer de manière transparente vos fichiers dans vos dossiers de conteneurs. Pour ce faire, il installera un serveur vscode interne dans votre conteneur de développement. Pour plus d'informations, consultez ce lien .

Assurez-vous cependant que vos volumes sont toujours créés dans votre fichier docker-compose.yml.

J'espère que ça aide: D!

0
Chris Deligan