Как создать образ докера из проекта nodejs в монорепозитории с рабочими пространствами yarn

В настоящее время мы изучаем CI / CD с нашей командой для нашего веб-сайта. Недавно мы также адаптировались к структуре монорепозитория, так как это значительно упрощает наши зависимости и обзор. В настоящее время тестирование и т. Д. Готово для CI, но сейчас я приступаю к развертыванию. Я хотел бы создать docker-образы необходимых пакетов.

Я подумал:

1) Вытяните полный монорепозиторий в проект докера, но запуск пряжи в нашем проекте приводит к общему размеру проекта около 700 МБ, и это в основном из-за нашего собственного приложения для реагирования, у которого даже не должно быть образа докера. Также это должно приводить к длительному извлечению образа каждый раз, когда нам нужно развернуть новую версию.

2) Каким-то образом объединять мои проекты. С нашим интерфейсом у нас есть рабочая настройка, так что все должно быть в порядке. Но я просто попытался добавить веб-пакет в наш экспресс-API, и из-за этой проблемы в моем пакете возникла ошибка: https://github.com/mapbox/node-pre-gyp/issues/308

3) Я попытался запустить yarn install только внутри необходимого проекта, но это все равно установит мои node_modules для всех моих проектов.

4) Запустите пакет npm: pkg. В результате получается один файл, готовый к запуску в определенной системе с определенной версией узла. Это ДЕЙСТВИТЕЛЬНО работает, но я не уверен, насколько хорошо он будет обрабатывать ошибки и сбои.

5) Другим решением может быть копирование проекта из рабочей области и запуск на нем пряжи. Проблема в том, что использование рабочих пространств пряжи (неявно связанных зависимостей) практически исчезло. Мне пришлось бы явно добавить другие мои зависимости рабочей области. Возможна ссылка на них из определенного хэша коммита, который я собираюсь протестировать прямо сейчас. (РЕДАКТИРОВАТЬ: вы не можете ссылаться на подкаталог как на пакет пряжи)

6) ???

Я хотел бы знать, не хватает ли мне возможности иметь только необходимые node_modules для определенного проекта, чтобы я мог сохранить свои образы докеров небольшими.


person 33Fraise33    schedule 07.05.2018    source источник
comment
вы нашли решение этого? Я работаю над подобным проектом.   -  person Peter    schedule 05.09.2018
comment
Это не будет проблемой, если вы публикуете свои пакеты в npm, вы не должны напрямую зависеть от пакета на диске во время развертывания, а от того, который был отправлен в реестр. Автоматическое связывание пряжи следует использовать только во время разработки. Если вы помните об этом, у вас не будет проблем с обычным развертыванием, если вы просто скопируете каталог службы в образ докера и установите там deps.   -  person jonathancardoso    schedule 04.12.2019


Ответы (3)


Я работал над проектом по структуре, подобной вашей, он выглядел так:

project
├── package.json
├── packages
│   ├── package1
│   │   ├── package.json
│   │   └── src
│   ├── package2
│   │   ├── package.json
│   │   └── src
│   └── package3
│       ├── package.json
│       └── src
├── services
│   ├── service1
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── src
│   └── service2
│       ├── Dockerfile
│       ├── package.json
│       └── src
└── yarn.lock

Папка services/ содержит по одной службе на каждую подпапку. Каждая служба написана на node.js и имеет свой собственный package.json и Dockerfile. Обычно это веб-сервер или REST API на основе Express.

Папка packages/ содержит все пакеты, не являющиеся службами, обычно это внутренние библиотеки.

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

Основной package.json (тот, что находится в корневой папке проекта) содержит только некоторые devDependencies, такие как eslint, средство запуска тестов и т. Д.

Индивидуум Dockerfile выглядит так, если предположить, что service1 зависит как от package1, так и package3:

FROM node:8.12.0-alpine AS base

WORKDIR /project

FROM base AS dependencies

# We only copy the dependencies we need
COPY packages/package1 packages/package1
COPY packages/package3 packages/package3

COPY services/services1 services/services1

# The global package.json only contains build dependencies
COPY package.json .

COPY yarn.lock .

RUN yarn install --production --pure-lockfile --non-interactive --cache-folder ./ycache; rm -rf ./ycache

Фактические Dockerfile, которые я использовал, были более сложными, так как им приходилось собирать подпакеты, запускать тесты и т. Д. Но вы должны получить представление об этом примере.

Как видите, хитрость заключалась в том, чтобы скопировать только те пакеты, которые необходимы для конкретной службы. Файл yarn.lock содержит список package @ version с точной версией и разрешенными зависимостями. Скопировать его без всех подпакетов не проблема, yarn будет использовать версию, разрешенную там, при установке зависимостей включенных пакетов.

В вашем случае проект, поддерживающий реакцию, никогда не будет частью какого-либо Dockerfile, поскольку он не зависит ни от одной из служб, что позволяет сэкономить много места.

Для краткости я опустил много деталей в этом ответе, не стесняйтесь спрашивать точность в комментарии, если что-то не совсем понятно.

person Anthony Garcia-Labiad    schedule 14.10.2018
comment
Как COPY packages/package1 packages/package1 работает, если файл Dockerfile находится внутри каталога service1? Разве это не COPY ../../packages/package1 packages/package1? - person HenningCash; 20.11.2018
comment
Это потому, что я использовал команду сборки, такую ​​как docker build -f ./services/service1/Dockerfile ., которая устанавливает контекст для текущего каталога (в данном случае корня проекта) с файлом Docker для service1. - person Anthony Garcia-Labiad; 21.11.2018
comment
Я действительно хотел бы, чтобы был способ не копировать пакеты и просто позволить webpack обрабатывать установку зависимостей. Это возможно? - person Travis Tubbs; 12.02.2019
comment
Обратной стороной этого подхода является то, что вам придется определять свои зависимости дважды; один раз в package.json вашей службы и один раз в Dockerfile. - person Nepoxx; 04.11.2020
comment
Вы можете автоматически генерировать части Dockerfiles в precommit hook / ci с информацией из package.json файлов. - person hexagoncode; 30.01.2021
comment
Как использование машинописного текста меняет ситуацию? При компиляции создается каталог с файлами js ... - person Nathan H; 25.04.2021

После множества проб и ошибок я обнаружил, что осторожное использование файла .dockerignore - отличный способ контролировать ваше окончательное изображение. Это отлично работает при работе в монорепозитории для исключения «других» пакетов.

Для каждого пакета у нас есть файл dockerignore с похожим именем, который заменяет действующий файл .dockerignore непосредственно перед сборкой.

e.g., cp admin.dockerignore .dockerignore

Ниже приведен пример admin.dockerignore. Обратите внимание на * в верхней части этого файла, что означает «игнорировать все». Префикс ! означает «не игнорировать», т. Е. Сохранять. Комбинация означает игнорировать все, кроме указанных файлов.

*
# Build specific keep
!packages/admin

# Common Keep
!*.json
!yarn.lock
!.yarnrc
!packages/common

**/.circleci
**/.editorconfig
**/.dockerignore
**/.git
**/.DS_Store
**/.vscode
**/node_modules
person Simeon    schedule 30.07.2019

Недавно мы поместили наши серверные службы в монорепозиторий, и это был один из немногих вопросов, которые нам нужно было решить. В Yarn нет ничего, что могло бы помочь нам в этом отношении, поэтому нам пришлось искать в другом месте.

Сначала мы попробовали @ zeit / ncc, были некоторые проблемы, но в конце концов нам удалось получить финальные сборки. Он создает один большой файл, который включает весь ваш код, а также весь код ваших зависимостей. Выглядело великолепно. Мне пришлось скопировать в образ докера только несколько файлов (js, исходные карты, статические ресурсы). Изображения были намного меньше, и приложение работало. НО потребление оперативной памяти сильно выросло. Вместо ~ 70 МБ работающий контейнер потреблял ~ 250 МБ. Не уверен, что мы сделали что-то не так, но я не нашел никакого решения, и есть только одна проблема упомянув об этом. Я предполагаю, что Node.js load анализирует и загружает весь код из пакета, хотя большая его часть никогда не используется.

Все, что нам нужно, - это разделить производственные зависимости каждого пакета для создания тонкого образа докера. Вроде не так-то просто сделать, но инструмент все-таки нашли.

Сейчас мы используем fleggal / monopack. Он связывает наш код с Webpack и переносит его в Babel. Таким образом, он также создает один пакет файлов, но он не содержит всех зависимостей, только наш код. Этот шаг нам не нужен, но мы не против, чтобы он там был. Для нас важная часть - Monopack копирует только производственное дерево зависимостей пакета в dist / bundled node_modules. Это именно то, что нам нужно. Образы Docker теперь имеют размер 100–150 МБ вместо 700 МБ.

Есть один способ попроще. Если у вас всего несколько действительно больших модулей npm в вашем node_modules, вы можете использовать nohoist в корне package.json. Таким образом, yarn сохраняет эти модули в локальном node_modules пакета, и его не нужно копировать в образы Docker всех других служб.

eg.:

"nohoist": [
  "**/puppeteer",
  "**/puppeteer/**",
  "**/aws-sdk",
  "**/aws-sdk/**"
]
person Pavel    schedule 04.03.2019