Установите node_modules внутри контейнера Docker и синхронизируйте их с хостом - PullRequest
0 голосов
/ 29 июня 2018

У меня проблема с установкой node_modules внутри контейнера Docker и синхронизацией их с хостом. Версия моего Docker - 18.03.1-ce, build 9ee9f40, а версия Docker Compose - 1.21.2, build a133471.

Мой docker-compose.yml выглядит так:

# 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: ~

Мой 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

Трюк с внешним томом описан во многих постах блога и ответах Stack Overflow. Например, этот .

Приложение прекрасно работает. Исходный код синхронизирован. Горячая перезагрузка тоже отлично работает.

Единственная проблема, которая у меня возникает, это то, что папка node_modules на хосте пуста. Можно ли синхронизировать папку node_modules, которая находится внутри контейнера Docker, с хостом?

Я уже прочитал эти ответы:

  1. том docker-compose для node_modules, но пустой
  2. Доступ к node_modules после установки npm в Docker

К сожалению, они мне мало помогли. Мне не нравится первый , потому что я не хочу запускать npm install на своем хосте из-за возможных кросс-платформенных проблем (например, хост - Windows или Mac, а контейнер Docker - Debian 8 или Ubuntu 16.04). Второй мне тоже не подходит, потому что я хотел бы запустить npm install в моем Dockerfile вместо того, чтобы запускать его после запуска контейнера Docker.

Кроме того, я нашел это сообщение в блоге . Автор пытается решить ту же проблему, с которой я столкнулся. Проблема в том, что node_modules не будет синхронизирован, потому что мы просто копируем их из контейнера Docker на хост.

Я бы хотел, чтобы мои node_modules внутри контейнера Docker были синхронизированы с хостом. Пожалуйста, примите во внимание, что я хочу:

  • для установки node_modules автоматически вместо ручного
  • для установки node_modules внутри контейнера Docker вместо хоста
  • для синхронизации node_modules с хостом (если я устанавливаю какой-либо новый пакет внутри контейнера Docker, он должен синхронизироваться с хостом автоматически без каких-либо действий вручную)

Мне нужно иметь node_modules на хосте, потому что:

  • возможность читать исходный код, когда мне нужно
  • для IDE требуется локальная установка node_modules, чтобы он мог иметь доступ к devDependencies, например eslint или prettier. Я не хочу устанавливать эти devDependencies глобально.

Заранее спасибо.

Ответы [ 6 ]

0 голосов
/ 25 июня 2019

Никто не упомянул о решении с использованием функции докера entrypoint.

Вот мое рабочее решение:

Dockerfile (многоступенчатая сборка, так что она готова как для рабочей, так и для локальной разработки):

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"]

докер / 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 "$@"

докер-compose.yml:

version: "3.7"

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

При таком подходе вы набираете все 3 балла, которые вам необходимы, и, тем не менее, это намного более чистый способ - не нужно перемещать файлы.

0 голосов
/ 07 декабря 2018

Я знаю, что это было решено, но как насчет:

Dockerfile:

FROM node

# Create app directory
WORKDIR /usr/src/app

# Your other staffs

EXPOSE 3000

докер-composer.yml:

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

объем / приложение / package.json

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

После запуска node_modules будут присутствовать в ваших томах, но его содержимое генерируется внутри контейнера, поэтому кроссплатформенных проблем нет.

0 голосов
/ 29 ноября 2018

Спасибо Владиславу Тураку за ответ с entrypoint.sh, куда мы копируем node_modules из контейнера в хост.

Я реализовал аналогичную вещь, но столкнулся с проблемой с пакетами husky, @commitlint, tslint npm.
Я ничего не могу вставить в хранилище.
Причина: я скопировал node_modules из Linux в Windows. В моем случае <5% файлов разные (.bin и большинство package.json) и 95% одинаковые. <a href="https://i.stack.imgur.com/xyw2j.png" rel="nofollow noreferrer"> пример: изображение с различий

Итак, я сначала вернулся к решению с npm install из node_modules для Windows (для IDE и отладки). И образ Docker будет содержать Linux-версию node_modules.

0 голосов
/ 30 августа 2018

Сначала я хотел бы поблагодарить David Maze и trust512 за размещение их ответов. К сожалению, они не помогли мне решить мою проблему.

Я хотел бы опубликовать свой ответ на этот вопрос.

Мой 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

Мой 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

И последнее, но не менее важное. entrypoint.sh:

#!/bin/bash

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

Самая сложная часть здесь - установить node_modules в каталог кэша node_module (/usr/src/cache), который определен в нашем Dockerfile. После этого entrypoint.sh переместит node_modules из каталога кэша (/usr/src/cache) в каталог нашего приложения (/usr/src/app). Благодаря этому на нашем хост-компьютере появится весь каталог node_modules.

Глядя на мой вопрос выше, я хотел:

  • для установки node_modules автоматически вместо ручного
  • для установки node_modules внутри контейнера Docker вместо хоста
  • для синхронизации node_modules с хостом (если я устанавливаю какой-то новый пакет внутри контейнера Docker, он должен быть синхронизируется с хостом автоматически без каких-либо ручных действий

Первое, что сделано: node_modules устанавливаются автоматически. Второе тоже сделано: node_modules установлены внутри контейнера Docker (так что кроссплатформенных проблем не будет). И третье тоже сделано: node_modules, которые были установлены внутри контейнера Docker, будут видимыми на нашей хост-машине, и они будут синхронизированы ! Если мы установим новый пакет внутри контейнера Docker, он будет сразу синхронизирован с нашим хост-компьютером.

Важное замечание: по правде говоря, новый пакет, установленный внутри контейнера Docker, появится в /usr/src/app/node_modules. Поскольку этот каталог синхронизирован с нашим хостом, этот новый пакет появится и в каталоге node_modules нашего хоста. Но /usr/src/cache/node_modules будет иметь старую сборку на этом этапе (без этого нового пакета). Во всяком случае, это не проблема для нас. В течение следующего docker-compose up --build (требуется --build) Docker переустановит node_modules (так как package.json был изменен), а файл entrypoint.sh переместит их в наш /usr/src/app/node_modules.

Вы должны принять во внимание еще одну важную вещь. Если вы git pull код из удаленного репозитория или git checkout your-teammate-branch, когда Docker работает, возможно, в файл package.json будут добавлены некоторые новые пакеты. В этом случае вы должны остановить Docker с помощью CTRL + C и снова запустить его с помощью docker-compose up --build (требуется --build). Если ваши контейнеры работают как демон, вам нужно просто выполнить docker-compose stop, чтобы остановить контейнеры и снова запустить его с помощью docker-compose up --build (требуется --build).

Если у вас есть какие-либо вопросы, пожалуйста, дайте мне знать в комментариях.

Надеюсь, это поможет.

0 голосов
/ 29 июня 2018

Я бы не советовал перекрывать тома, хотя я не видел ни одного официального документа, запрещающего это, у меня были некоторые проблемы с ним в прошлом. Как мне это сделать:

  1. Избавьтесь от внешнего тома, поскольку вы фактически не планируете использовать его так, как он предназначен для использования - перезапускать контейнер с его данными, созданными специально в контейнере, после его остановки + удаления.

Выше может быть достигнуто путем небольшого сокращения вашего композитного файла:

frontend:
  build: ./app/frontend
  volumes:
    - ./app/frontend:/usr/src/app
  ports:
    - 3000:3000
  environment:
    NODE_ENV: ${ENV}
  command: npm start
  1. Избегайте наложения данных тома с инструкциями Dockerfile, когда в этом нет необходимости.

Это означает, что вам могут понадобиться два файла Docker - один для локальной разработки и один для развертывания жирного образа со всеми файлами dist приложения, размещенными внутри.

Тем не менее рассмотрим Dockerfile для разработки:

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

Вышеприведенное заставляет приложение создать полную установку node_modules и сопоставить ее с местоположением вашего хоста, в то время как указанная команда docker-compose запустит ваше приложение.

0 голосов
/ 29 июня 2018

Здесь происходит три вещи:

  1. Когда вы запускаете docker build или docker-compose build, ваш Dockerfile создает новый образ, содержащий каталог /usr/src/app/node_modules и установку Node, но больше ничего. В частности, ваше приложение не имеет встроенного образа.
  2. Когда вы docker-compose up, директива volumes: ['./app/frontend:/usr/src/app'] скрывает все, что было в /usr/src/app и монтирует содержимое хост-системы поверх него.
  3. Затем директива volumes: ['frontend-node-modules:/usr/src/app/node_modules'] монтирует именованный том поверх дерева node_modules, скрывая соответствующий каталог системы хоста.

Если бы вы запустили другой контейнер и подключили к нему именованный том, я ожидаю, что вы увидите там дерево node_modules. Для того, что вы описываете, вам просто не нужен именованный том: удалите вторую строку из блока volumes: и раздел volumes: в конце файла docker-compose.yml.

...