Установите node_modules внутри контейнера Docker и синхронизируйте их с хостом
У меня проблема с установкой 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 dependencies.
COPY package.json ./
COPY package-lock.json ./
RUN npm install
Уловка с внешним объемом описана во многих постах блога и ответах Stack Overflow. Например, этот.
Приложение прекрасно работает. Исходный код синхронизирован. Горячая перезагрузка тоже отлично работает.
Единственная проблема, которая у меня есть, это то, что папка node_modules
пуста на хосте. Можно ли синхронизировать папку node_modules
которая находится внутри контейнера Docker, с хостом?
Я уже прочитал эти ответы:
- том docker-compose для node_modules, но пустой
- Доступ к node_modules после установки npm в Docker
К сожалению, они мне не очень помогли. Мне не нравится первый, потому что я не хочу запускать npm install
на моем хосте из-за возможных межплатформенных проблем (например, хост - Windows или Mac, а контейнер Docker - Debian 8 или Ubuntu 16.04). Второй Dockerfile
тоже мне не подходит, потому что я хотел бы запустить npm install
в моем Dockerfile
вместо того, чтобы запускать его после запуска контейнера Docker.
Кроме того, я нашел этот пост в блоге. Автор пытается решить ту же проблему, с которой я столкнулся. Проблема в том, что node_modules
не будут синхронизированы, потому что мы просто копируем их из контейнера Docker на хост.
Я хотел бы, чтобы мои node_modules
внутри контейнера Docker были синхронизированы с хостом. Пожалуйста, примите во внимание, что я хочу:
- установить
node_modules
автоматически, а не вручную - установить
node_modules
внутри контейнера Docker вместо хоста - синхронизировать
node_modules
с хостом (если я устанавливаю новый пакет внутри контейнера Docker, он должен синхронизироваться с хостом автоматически без каких-либо действий вручную)
Мне нужно иметь node_modules
на хосте, потому что:
- возможность читать исходный код, когда мне нужно
- интегрированная среда должна
node_modules
быть установлен локально, чтобы он мог иметь доступ к devDependencies
, таких как eslint
или prettier
. Я не хочу устанавливать эти devDependencies
глобально.
Заранее спасибо.
Ответы
Ответ 1
Сначала я хотел бы поблагодарить Дэвида Мазе и доверие512 за отправку их ответов. К сожалению, они не помогли мне решить мою проблему.
Я хотел бы опубликовать свой ответ на этот вопрос.
Мой 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 cache directory.
RUN mkdir /usr/src/cache
WORKDIR /usr/src/cache
# Install the application dependencies into the node_modules cache directory.
COPY package.json ./
COPY package-lock.json ./
RUN npm install
# Create and define the application working directory.
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
И последнее, но не менее entrypoint.sh
: entrypoint.sh
:
#!/bin/bash
cp -r /usr/src/cache/node_modules/. /usr/src/app/node_modules/
exec npm start
Самая сложная часть здесь - установить node_modules
в node_module
кеша 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
(требуется --build
) Docker будет переустанавливать node_modules
(потому что package.json
был изменен), и файл entrypoint.sh
переместит их в наш /usr/src/app/node_modules
.
Вы должны учесть еще одну важную вещь. Если вы git pull
код из удаленного хранилища или git checkout your-teammate-branch
по package.json
git checkout your-teammate-branch
, когда Docker работает, там могут быть некоторые новые пакеты, добавленные в package.json
файл. В этом случае вы должны остановить Docker с помощью CTRL + C
а затем снова с помощью docker-compose up --build
(--build
). Если ваши контейнеры запущены в качестве демона, вы должны просто выполнить docker-compose stop
docker-compose up --build
чтобы остановить контейнеры, а затем снова с помощью docker-compose up --build
(--build
).
Если у вас есть какие-либо вопросы, пожалуйста, дайте мне знать в комментариях.
Надеюсь это поможет.
Ответ 2
Здесь три вещи:
- Когда вы запускаете
docker build
или docker-compose build
, ваш Dockerfile создает новое изображение, содержащее каталог /usr/src/app/node_modules
и установку узла, но ничего больше. В частности, ваше приложение не находится на встроенном изображении. - Когда вы
docker-compose up
, volumes: ['./app/frontend: /usr/src/app']
директива скрывает все, что было в /usr/src/app
и монтирует содержимое хост-системы поверх нее. - Затем директива
volumes: ['frontend-node-modules: /usr/src/app/node_modules']
устанавливает именованный том поверх дерева node_modules
, скрывая соответствующий каталог хост-системы.
Если вы собираетесь запустить другой контейнер и присоединить к нему именованный том, я ожидаю, что вы увидите дерево node_modules
. Для того, что вы описываете, вам просто не нужен именованный том: удалите вторую строку из раздела volumes:
block и volumes:
в конце файла docker-compose.yml
.
Ответ 3
Я бы не предлагал дублирование томов, хотя я не видел, чтобы какие-либо официальные документы запрещали его, у меня были некоторые проблемы с ним в прошлом. Как я это делаю:
- Избавьтесь от внешнего тома, поскольку вы не планируете фактически использовать его, как оно предназначено для использования, - возвращая контейнер с его данными, созданными специально в контейнере, после остановки + удаления.
Вышеупомянутое может быть достигнуто путем сокращения вашего файла компоновки немного:
frontend:
build: ./app/frontend
volumes:
- ./app/frontend:/usr/src/app
ports:
- 3000:3000
environment:
NODE_ENV: ${ENV}
command: npm start
- Избегайте перекрытия данных тома с инструкциями Dockerfile, когда это не необходимо.
Это означает, что вам может понадобиться два Dockerfiles - один для локальной разработки, а один для развертывания жирного изображения со всеми файлами dist dist приложения, расположенными внутри.
Тем не менее, рассмотрим разработку Dockerfile:
FROM node:10
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
RUN npm install
Вышесказанное заставляет приложение создать полную установку node_modules и сопоставить его с местом вашего хоста, в то время как команда, указанная командой docker-compose, запустит ваше приложение.
Ответ 4
Я знаю, что это было разрешено, но как насчет:
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 будут присутствовать в ваших томах, но его содержимое создается внутри контейнера, поэтому проблем с кросс-платформой не возникает.
Ответ 5
Никто не упомянул о решении с использованием функции точки 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 "[email protected]"
fi
exec "[email protected]"
докер-compose.yml:
version: "3.7"
services:
web:
build:
target: dev
context: .
volumes:
- .:/app:delegated
ports:
- "3000:3000"
restart: always
environment:
NODE_ENV: dev
При таком подходе вы получаете все 3 балла, которые вам необходимы, и, тем не менее, это намного более чистый способ - не нужно перемещать файлы.
Ответ 6
Спасибо Vladyslav Turak за ответ с помощью entrypoint.sh
где мы копируем node_modules
из контейнера в хост.
Я реализовал аналогичную вещь, но я столкнулся с проблемой с хриплыми, @commitlint, tslint пакетами npm.
Я не могу ничего вставить в репозиторий.
Причина. Я скопировал node_modules
из Linux в Windows. В моем случае <5% файлов разные (.bin и большинство package.json), а 95% - то же самое. пример: изображение с diff
Поэтому я вернулся к решению с npm install
node_modules
для Windows в первую очередь (для IDE и отладки). И изображение Docker будет содержать версию node_modules
на node_modules
.
Ответ 7
Как вы упоминаете, связывание вашей папки host_modules с вашим контейнером node_modules не является хорошей практикой. Я видел решение создания внутреннего тома для этой папки довольно часто. Невыполнение этого требования вызовет проблемы на этапе строительства.
Я столкнулся с этой проблемой, когда пытался создать среду разработки докеров для angular приложения, которое показывает ошибки tslib при редактировании файлов в папке хоста, поскольку папка host host_modules была пустой (как и ожидалось).
Дешевое решение, которое помогло мне в этом случае, заключалось в использовании расширения кода Visual Studio под названием "Удаленные контейнеры".
Это расширение позволит вам прикрепить код Visual Studio к вашему контейнеру и прозрачно редактировать ваши файлы в папках вашего контейнера. Для этого он установит внутренний сервер vscode в ваш контейнер разработки. Для получения дополнительной информации проверьте эту ссылку.
Однако убедитесь, что ваши тома по-прежнему создаются в файле docker-compose.yml.
Надеюсь, это поможет: D!