#docker #lerna #monorepo #yarn-workspaces
#docker #lerna #monorepo #yarn-рабочие пространства
Вопрос:
По сути, обе службы foo
и bar
зависят от common
библиотеки.
Предположим, что common
пакет уже опубликован в реестре npm.
|
├── packages
| ├── common
| | ├── src
| | ├── package.json
| | ├── tsconfig.build.json
| | ├── tsconfig.json
| ├── foo
| | ├── src
| | ├── Dockerfile
| | ├── package.json
| | ├── tsconfig.build.json
| | ├── tsconfig.json
| ├── bar
| | ├── src
| | ├── Dockerfile
| | ├── package.json
| | ├── tsconfig.build.json
| | ├── tsconfig.json
├── tsconfig.json
├── package.json
├── yarn.lock
├── docker-compose.init.yml
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.init
├── .dockerignore
Я добавил все devDependencies, которые являются общими для всех пакетов в корневом package.json, следующим образом:
"scripts": {
"build": "lerna run build --stream",
"setup": "yarn amp;amp; yarn build",
"docker:bootstrap": "docker-compose --file=docker-compose.init.yml build",
"docker:up": "docker-compose up --build"
},
"devDependencies": {
"@nestjs/cli": "^7.5.1",
"@nestjs/common": "^7.4.4",
"@nestjs/core": "^7.4.4",
"@nestjs/platform-express": "^7.4.4",
"@nestjs/schematics": "^7.1.2",
"@nestjs/testing": "^7.4.4",
"@types/express": "^4.17.8",
"@types/jest": "^26.0.13",
"@types/node": "^14.10.2",
"@types/supertest": "^2.0.10",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"eslint": "^7.9.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"express": "^4.17.1",
"husky": "^4.3.0",
"jest": "^26.4.2",
"lerna": "^3.22.1",
"lint-staged": "^10.4.0",
"prettier": "^2.1.2",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^6.6.3",
"supertest": "^4.0.2",
"ts-jest": "^26.3.0",
"ts-loader": "^8.0.3",
"ts-node": "^9.0.0",
"typescript": "3.9.5"
}
foo
Пакет должен использовать реляционную базу данных, поэтому я установил следующие пакеты независимо.
$ yarn workspace foo add @nestjs/typeorm mysql typeorm
Чтобы устранить сообщение об ошибке "<package> has unmet peer dependency <package>"
, я нажал следующую команду.
$ yarn workspace foo add @nestjs/common @nestjs/core @nestjs/platform-express rxjs
Я здесь немного потерялся. Какой смысл организовывать несколько приложений в режиме monorepo, если я продолжаю повторять установку одних и тех же пакетов из одного пакета в другой? В конце концов, мне еще сложнее писать Dockerfile.
Мой первый вопрос: когда разработчик работает над монолитной кодовой базой, является ли это нормальным поведением для установки библиотек в определенный пакет, если это необходимо?
This is how my Dockerfile looks like:
// docker-compose.init.yml
# This file triggers the initial build
version: "3.8"
services:
pkg_builder:
image: pkg-builder
build:
context: .
dockerfile: Dockerfile.init
First, execute the command below.
$ yarn docker:bootstrap
Dockerfile.init
creates an initial builder image from which the “real” builder image can copy the build directory.
// Dockerfile.init
FROM scratch
# Copy files from the root to build directory
COPY package.json lerna.json yarn.lock tsconfig.json /build/
# This line is required to install dependencies from foo's package.json
COPY ./packages/foo/package.json /build/packages/foo/package.json
С этого момента создавайте изображения с помощью команды:
$ yarn docker:up
// docker-compose.yml
version: "3.8"
services:
pkg_builder:
image: pkg-builder
build: .
mariadb:
image: mariadb:10.3
ports:
- "3306:3306"
environment:
- MYSQL_USER=root
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=tutorial
restart: always
foo:
container_name: foo
build: ./packages/foo
ports:
- "8000:8000"
depends_on:
- mariadb
restart: always
// Dockerfile
FROM node:12-alpine
COPY --from=pkg-builder /build /build
WORKDIR /build
RUN rm -rf node_modules
RUN yarn
CMD ["true"]
Проблема в том, что размер изображения слишком большой. Это связано с тем, что все зависимости разработчика были скопированы из pkg-builder
.
// foo's Dockerfile
FROM node:12-alpine
WORKDIR /app/current
COPY --from=pkg-builder /build/node_modules /app/current/packages/foo/node_modules
COPY --from=pkg-builder /build/tsconfig.json ./tsconfig.json
WORKDIR /app/current/packages/foo
COPY . .
RUN yarn build
EXPOSE 8000
CMD [ "node", "./dist/main" ]
Наконец, как я должен уменьшить изображение?В этом случае я считаю, что многоступенчатая сборка не является правильной стратегией для уменьшения размера. Чего мне здесь не хватает?
Ответ №1:
В каждом package.json
файле должен быть указан полный список непосредственных зависимостей его приложения.Я должен иметь возможность проверить ваш исходный репозиторий, запустить yarn install
и иметь рабочее дерево приложений. Там, где в вашем вопросе говорится: «и, кстати, эти другие зависимости установлены в среде, и я просто предполагаю их», это проблема для всех, кто не работает именно с той системой, в которой вы находитесь, и, в частности, это проблема для Docker и других инструментов автоматической сборки.
Ваши библиотечные зависимости могут иметь свои собственные библиотечные зависимости. Они будут перечислены в yarn.lock
файле, но их необязательно указывать непосредственно в package.json
файле.
В качестве примера возьмем библиотеки доступа к базе данных: если ваше основное приложение использует их, они должны быть включены в ваше dependencies
. Но если весь доступ к базе данных инкапсулирован в вашей common
общей библиотеке, вашим приложениям нужно только ссылаться на эту библиотеку (in foo/package.json
), а библиотека должна включать зависимости базы данных (in common/package.json
).
Вы должны отделиться dependencies
от devDependencies
.В списке должны быть перечислены вещи, необходимые для запуска вашего приложения ( express
) ; должны быть перечислены dependencies
вещи, которые вам нужны только для сборки вашего приложения ( eslint
) devDependencies
. Вы обсуждаете размер изображения; это дает вам возможность установить гораздо меньшую группу пакетов в контейнер при фактическом запуске.
(Обратите внимание, что Yarn на самом деле не поддерживает неустановку devDependencies
; npm поддерживает, хотя в противном случае он намного медленнее в использовании.)
Затем многоступенчатая сборка может привести к созданию изображения меньшего размера.Идея здесь в том, что на первом этапе устанавливаются все зависимости разработчика и создается приложение; второй этап включает только зависимости времени выполнения и встроенный код. Это более или менее выглядит как:
ARG node_version=12-current
FROM node:${node_version} AS build
WORKDIR /app
COPY package.json yarn.lock .
RUN yarn install --immutable
COPY . .
RUN yarn build
FROM node:${node_version}
WORKDIR /app
ENV NODE_ENV=production
COPY package.json yarn.lock .
RUN yarn install --immutable
# RUN npm ci # in production mode, skips devDependencies
COPY --from=build /app/dist dist
CMD ["node", "/app/dist/main.js"]
Вам никогда не понадобится «контейнер builder»; показанная вами настройка в основном идентична многоступенчатой сборке, за исключением распределения по трем отдельным файлам Dockerfile. В частности, если у вас есть образ, который не запускает команду, а просто содержит файлы, это хороший кандидат на раннюю стадию многоступенчатой сборки.
Комментарии:
1. Спасибо за ваш ответ, Дэвид. Вы не возражаете, если мы посмотрим на исходный репозиторий? К сожалению, я не совсем понимаю ваш пост. Таким образом, каждый пакет не должен иметь собственных зависимостей lib от своего файла package.json? Вот ссылка на репозиторий. github.com/jeffminsungkim/jmsk-tutorials/tree/master/… Мы могли бы поговорить и о живом ресурсе VSCode.
2. В этом ответе отсутствует контекст re: monorepos.
3. Как сказал Эдвард, я не думаю, что полностью понял ответ Дэвида.