Решение, протестированное на MacOS 10.14.3 и Windows 10 Pro.
Есть куда улучшать, например можно добавить multi-stage build и т.д.. Но основная идея думаю останется такой.
Минимальный пример, всё лишнее убрал.
Используем один
Dockerfile
. Согласно best-practices, для кеширования слоёв, сначала ставим зависимости, затем копируем код, задаём не-рутового пользователя. Можно "выпекать" как есть и в продакшн.
DockerfileFROM node:10-alpine
ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}
# Install latest npm
RUN npm i npm@latest -g
WORKDIR /usr/src/app
# Instal dependencies
COPY package*.json ./
RUN npm install
# Copy app code
COPY ./src ./src
# https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#non-root-user
USER node
CMD ["node", "src/index.js"]
В
docker-compose.yaml
для разработки мы:
1. Задаём root пользователя, чтобы не иметь проблем с правами для установки зависимостей (спорный момент, возможно стоит просто разобраться с правами на директорию)
2. Монтируем папку с кодом - теперь можем видеть изменения без пересборки образа. В этот момент установленные в этой папке внутри контейнера зависимости "исчезнут".
3. Устанавливаем зависимости (теперь они видны и на хосте) и запускаем nodemon для отслеживания изменений. Мне понравился вариант объединить эти две команды, чтобы не выполнять дополнительно
docker-compose exec api npm i
после старта контейнера. При первом запуске нам в любом случае нужно поставить зависимости, последующие запуски или будут очень быстрыми, или доставят новые зависимости.
docker-compose.yamlversion: '3'
services:
api:
environment:
- NODE_ENV=development
user: root
build:
context: ./api
args:
- NODE_ENV=development
volumes:
- ./api:/usr/src/app
ports:
- 3000:3000
command: npm i && node_modules/.bin/nodemon src/index.js
А теперь немного магии.
Допустим, я занимаюсь исключительно PHP-частью проекта и мне все эти ваши node_modules на локальной машине не нужны.
Создаём
docker-compose.override.yml
(должен быть в .gitignore) и переопределяем интересующие нас секции:
docker-compose.override.yamlversion: '3'
services:
api:
volumes:
- ./api:/usr/src/app
- /usr/src/app/node_modules
command: node_modules/.bin/nodemon src/index.js
Теперь мы используем установленные в контейнере на этапе билда образа node_modules, а на хосте видим только пустую папку. Так же мы не выполняем установку зависимостей при старте контейнера.
Или допустим я работаю на Windows и nodemon в Docker не реагирует на изменения файлов. Лечим:
docker-compose.override.yamlversion: '3'
services:
api:
command: node_modules/.bin/nodemon --legacy-watch src/index.js
Подробнее:
https://docs.docker.com/compose/extends/
Таким образом, мы получаем возможность быстро запустить проект состоящий из любого количества разных сервисов на разных технологиях, установив только Docker. * Нам доступен автокомлит в IDE, мы можем заглянуть в код подключаемых зависимостей. При этом, мы имеем возможности локальной подстройки проекта - например, пофиксить проблемы нашей операционной системы или не делать видимой папку с зависимостями.
* Конечно, тут могут возникнуть проблемы как с установкой и настройкой самого докера (в том числе поддержкой докера вашей операционной системой), так и работой более сложных конфигов. На мой взгляд, этот путь минимум не сложнее, чем настройка Vagrant'а и явно выигрывает у ручной установки (Python 2, Postgress и ещё чего-то когда твоя зона ответственности только шаблоны и стили, ой, а у тебя же уже стоит Python 3, и кстати по тому старому проекту надо Mongo 2 версии, так что делай что хочешь с установленной 3 версией...и объясни чуваку с WIndows как поставить nvm, чтобы версии Node.js переключать).