Как обеспечить обновление Docker-контейнера по мере обновления файлов собранного приложения?
Новичок в Docker. Основной мой язык - TypeScript, потому мои FullStack-приложения разделены на исходный код и выходной JavaScript.
Я владею такими инструментами, как gulp и webpack, которые могут пересобрать проекты по мере внесения изменений в код (т. н. инкрементальная сборка), но мне непонятно, как заставить Docker видеть эти изменения.
В комментарии к вопросу Какие существуют походы для интеграции Docker-контейнера и инкрементальной сборки FullStack-проекта с серверной частью на NodeJS? мне порекомендовали такой подход:
Примонтировать папку с собираемым проектом в докер и дергать в докере команду на перезапуск сервера последним шагом при сборке.
Я не совсем понял, идёт ли речь о volum-ах, но в ответе на вопрос Какова правильная интерпретация сообщения “Creating network ”XXX“ with the default driver” при запуске Docker-а? мне порекомендовали их не использовать:
Cкорее все что указано в volume не нужно монтировать в контейнер, а указываться в Dockerfile через COPY.
(Оригинал)
Для того, чтобы попросить Вас показать реализацию одной из выше приведённых концепций, я взял такую структуру:
- 00-Source - исходные файлы
- 01-DevelopmentBuild - локальная сборка
Пока не знаю, как будет выглядеть окончательная структура проекта (консультируюсь по этому поводу в вопросе Являются ли папки с исходным и выходным кодом “приложением” в проектах использованием Docker?), но для конкретики начну с этой.
Начальный docker-compose.yaml:
version: "3"
services:
webpack:
build: ./
volumes:
- ./00-Source:/Application/00-Source
- ./01-DevelopmentBuild:/Application/01-DevelopmentBuild
- ./.eslintignore:/Application/.eslintignore
- ./.eslintrc.yaml:/Application/.eslintrc.yaml
- ./.mocharc.yaml:/Application/.mocharc.yaml
- ./package.json:/Application/package.json
- ./tsconfig.json:/Application/tsconfig.json
ports:
- 5000:5000
Пока что единственный Dockerfile
FROM node:12.4
WORKDIR /Application
RUN npm i
CMD [ "npm", "run", "'Incremental development build'" ]
Как в данном примере можно организовать инкрементальную сборку?
Важный момент
Не знаю, сколько времени нужно на создание нового контейнера и его запуск, но я не думаю, что это займёт пару секунд. Поскольку уже второй раз вижу в ответах предложение создавать новый контейнер при обновлении кода и ещё какие-то манипуляции с системой контроля версий проводить, то на конкретном примере поясню, что мне надо:
- Добавил новое CSS-свойство -> сохранил файл -> стили тут же пересобрались -> изображение в браузере обновилось
- Удалил это CSS-свойство -> сохранил файл -> стили тут же пересобрались -> изображение в браузере вернулось к предыдущему
Без Dockera с помощью Gulp или Webpack и соответствующих плагинов каждый из этих процессов занимает 2-5 секунд для маленьких проектов, а при хорошей настройке время цикла будет порядка 10 секунд для крупных проектов. Не соврем понял, зачем мне предлагают создание новых контейнеров и манипуляции с системой контроля версий, но я так думаю, там уже этот цикл будет занимать уже минуты. С точки зрения высокотехнологической веб-разработки ждать по несколько минут, чтобы увидеть в браузере результат добавления нового CSS-свойства неприемлемо.
И да, конечно я говорю про режим локальной разработки, а что касается деплоя тестовой и продакшен-сборки, то там сделаю всё что потребуется: и управление системой контроля версий, и создание контейнеров, и CI.
Запрошенное уточнение
В этом вопросе полагается, что CI/CD собирается на стороне разработчика, а заказчик может только пользоваться готовым продуктом.
Ответы (2 шт):
Следует уточнить, что ключевым в ответе будет понимание с чьей стороны происходят процессы CI/CD, на стороне Заказчика или на стороне Разработчика. Также важно понимать как поставляется образ - путем предоставления Dockerfile или же образа в registry.
В любом случае, каждый раз, когда обновляется код нужно формировать новый образ (контейнер). Из Вашего примера - код содержится в монтируемых каталогах, что не всегда удобно. Можно попробовать добавить в Dockerfile команды git pull из Вашего репозитория, чтобы код при сборке был всегда актуальным.
Сразу оговорюсь, что я слабо знаком с backend разработкой на js, по-этому привожу пример для клиентской части. Главная ведь суть вопроса в том, как в докер контейнере увидеть изменения, и пример с клиентской частью, все же лучше чем без примера. В общем, надеюсь, что ответ будет полезен.
Наиболее практичный вариант (очень подробно описан тут, правда серверная часть там на php, но все равно очень рекомендую почитать. Ниже я привожу сильно упрощенный, но рабочий код) выглядит следующим образом.
Нужно создать отдельный контейнер (назовем его buildchain-контейнер), который будет использоваться чисто для сборки проекта как с нуля так и в инкрементальном режиме. Т.е. для запуска приложения нужно использовать отдельный контейнер (назовем его app). В примере ниже в нем запущен nginx и он использует результат работы buildchain контейнера.
Исходники в buildchain нужно добавлять именно через volume для того, чтобы не нужно было в режиме разработки пересобирать контейнер при каждом изменении файлов. Так конечно будет работать медленней чем на локальном хосте, но это неизбежная плата (для большого проекта она может быть довольно существенной, по крайней мере для MacOS, про другие ОС не скажу) за то, что не нужно засорять хост средствами разработки, у всех одинаковые версии и т.д.
Он может находится в ./docker-config/buildchain/Dockerfile и выглядит так:
FROM node:12.4
# тут инсталируем yarn
WORKDIR /project
RUN cd /project && yarn install
CMD yarn run build
./docker-config/buildchain/package.json:
{
...
"scripts": {
"watch": "npm-watch",
"build": "yarn run build-css && yarn run build-js",
"build-css": "./node_modules/.bin/node-sass --output-style compressed ./src/assets/css/app.scss > ./src/web/assets/css/app.css",
"build-js": "./node_modules/.bin/babel ./src/assets/js/app.js --presets es2015 --out-file ./src/web/assets/js/app.js"
},
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.2.0",
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"node-sass": "^4.10.0",
"npm-watch": "^0.6.0"
},
"watch": {
"build-css": {
"patterns": [
"src/assets/css"
],
"extensions": "scss",
"quiet": false
},
"build-js": {
"patterns": [
"src/assets/js"
],
"extensions": "js",
"quiet": false
}
}
}
Запускать контейнер удобно из docker-compose. Для этого в docker-compose.yml нужно добавить:
services:
nginx:
build:
context: .
dockerfile: ./docker-config/nginx/Dockerfile
ports:
- 8080:80
volumes:
- ./src/web:/var/www/html/web
buildchain:
build:
context: .
dockerfile: ./docker-config/buildchain/Dockerfile
volumes:
- node-modules:/project/node_modules
- ./docker-config/buildchain/package.json:/project/package.json
- ./src:/project/src
command: yarn run watch
volumes:
node-modules:
Общая структура проекта:
▾ docker-config/
▾ buildchain/
Dockerfile
package.json
▾ nginx/
Dockerfile
default.conf
▾ src/
▾ assets/
▸ css/
▸ js/
▾ web/
▾ assets/
▸ css/
▸ js/
index.html
docker-compose.yml
Теперь при запуске docker-compose up поднимается и buildchain и 'nginx. Изменения в файлах в srcчерезvolumeпопадают вbuildchainи автоматически собираются.nginxполучает их опять же черезvolume` и сразу же видит.
