Посмотрите, как упаковать основные библиотеки DotNet в базовый образ Docker для динамической ссылки на него.
В своем стремлении узнать больше о Docker и контейнеризации я спросил себя, можно ли упаковать библиотеку основных классов dotnet в контейнер и использовать ее из проектов в других контейнерах.
В этой статье более подробно рассматривается, как это можно сделать с помощью:
- Библиотека основных классов dotnet, которую я хочу поместить в контейнер — mylib
- Консольное приложение dotnet core, включающее mylib в качестве эталонной сборки — myconsole
- Dotnet Core AspNet Api, который включает mylib в качестве эталонной сборки — myapi
Я использую Visual Studio Code, Docker Desktop Community Edition 2.4.0.0 на Mac OS X 10.15.
MyLib - Джилл
Mylib предоставляет класс ClassLib:
После создания библиотеки она публикуется в папке lib
в корневой папке, где находится мое решение. Для простоты у меня есть одна корневая папка, содержащая mylib, myconsole
и myapi
. На самом деле это могут быть отдельные решения.
Для упаковки библиотеки в базовый образ используется следующий Dockerfile:
Чтобы создать базовый образ докера, который не зависит ни от каких других образов докера, он запускает FROM scratch
. Затем он создает каталог /app
в образе и устанавливает его в качестве рабочего каталога. Команда COPY /lib .
копирует все файлы из папки /lib
, в которой была опубликована библиотека mylib
, в рабочий каталог /app
образа.
Сборка образа Docker для образа s/lib
выглядит следующим образом:
Результат проекта mylib
— это все, что содержит этот образ. Он не имеет зависимостей от каких-либо других изображений.
MyConsole — Джек
Первым проектом, использующим mylib
, является myconsole
:
Он использует mylib.dll
в качестве эталонной сборки из-под папки lib
:
HintPath указывает, где искать mylib.dll
во время выполнения. Это настроено на поиск эталонной сборки в той же папке, из которой запускается myconsole
. Я явно закомментировал тег <Private>false</Private>
, поскольку он нужен нам для правильного создания файла зависимостей myconsole.deps.json. Параметр <Private>true</Private>
создает файл зависимости без mylib
в качестве зависимости, что приводит к сбою во время выполнения, даже если mylib.dll
присутствует в той же папке, что и myconsole
:
Другой способ решить эту проблему — вручную изменить файл myconsole.deps.json
, чтобы включить зависимость, которую я решил не делать, чтобы сделать эту статью проще.
Поскольку весь смысл этой статьи заключается в повторном использовании эталонной библиотеки из контейнера докеров, во время создания образа console
мы будем явно копировать только элементы myconsole* в образ и игнорировать элементы mylib*.
Элементы myconsole
публикуются в папке console
:
Файл образа Docker для создания образа s/console
выглядит следующим образом:
Здесь мы начинаем со ссылки на изображение s/lib
как library
. Поскольку console
является основным приложением dotnet, для его запуска требуется dotnet/core/runtime
, который является основой этого образа. Все файлы, опубликованные в папке /app/
базового образа s/lib
, затем копируются непосредственно в рабочую папку /app
этого образа. Только файлы, начинающиеся с myconsole*
, опубликованные сборкой myconsole
, затем копируются в каталог /app
. Это позволяет myconsole
подобрать эталонную сборку mylib.dll
(скопированную на предыдущем шаге) во время выполнения. Чтобы показать, что мы копируем только файлы, начинающиеся с myconsole*
, мы копируем тот же пакет в каталог /app/consoleonly/
. Мы собираемся проверить эту папку позже в статье.
Давайте создадим изображение s/console
:
и запустите его:
Мой мой!! Джилл и Джек влюблены 💏 .
Давайте снова запустим контейнер и заглянем внутрь:
и, как и ожидалось, только элементы, начинающиеся с myconsole*
, действительно были скопированы в каталог consoleonly
. Это указывает на то, что элементы mylib*
в каталоге app
взяты из базового образа s/lib
только для библиотеки.
MyApi - Джейн
Второй проект, в котором используется mylib
, — это myapi
:
Он также использует mylib.dll
в качестве эталонной сборки точно так же, как myconsole
. Элементы myapi
публикуются в папке api
:
Файл образа Docker для создания s/api
почти такой же, как и файл для создания s/console
, отличающийся только включением образа dotnet/core/aspnet
:
Сборка и запуск контейнера:
Веб-API опубликован на порту 5000, и переход к нему показывает:
Ну-ну… Джилл и Джейн влюблены 👩❤️💋👩 . Подожди… Что случилось с Джеком❗
Что случилось с Джеком?
Давайте посмотрим, что мы сделали до сих пор.
- Мы создали основную библиотеку dotnet и базовый образ докера, содержащий эту библиотеку.
- Мы создали два разных типа проектов dotnet — консольный и webapi — для которых требуется библиотека dotnet в процессе сборки.
- Мы создали образы докеров для каждого из проектов dotnet, в которых используется библиотека, но явным образом убедились, что используемая библиотека не является той, на основе которой были построены эти проекты. Используемая библиотека — это та, которая получена из базового образа докера на шаге 1.
Шаг 3 кажется очень неудобным. Какой смысл использовать библиотеку из базового образа вместо сохранения зависимости для каждого проекта при создании образа? Что ж, посмотрим, в чем суть на самом деле.
Вот список образов Docker, которые мы создали на данный момент:
s/lib:latest
— содержит библиотеку классовs/console:latest
— содержит библиотеку классов +dotnet/core/runtime
s/api:latest
— содержит библиотеку классов +dotnet/core/aspnet
Давайте теперь продолжим и изменим код в общей библиотеке и создадим новую версию изображения s/lib:2.0
:
Теперь, не перестраивая нашу консоль или проекты API, давайте просто создадим новую версию соответствующих им образов, используя s/lib:2.0
в качестве базового образа:
Если мы сейчас запустим контейнер console 2.0:
Ну… Ну… Похоже, у Джека появилась новая девушка по имени Эми ❤️ .
И запустив контейнер api 2.0 мы видим:
Увы! Наша история плохо кончилась для Джека и Джилл 💔 . Найдут ли они друг друга снова?
Вывод
Я прекрасно провел время, изучая, как упаковать библиотеку в образ докера, чтобы ее можно было использовать в других образах. Конечно, мы должны помнить, что следует использовать только доверенные образы докеров — особенно в этом случае, поскольку мы динамически привязываемся к сборке. Более того, в этой статье я не использовал никакое версионирование, но в продакшене это крайне важно.
Надеюсь, вы получите такое же удовольствие, как и я, изучая контейнеры 😃. Как всегда, вы найдете весь код для этой статьи в моем репозитории GitHub.
Ссылка
https://andrewlock.net/exploring-the-net-core-mcr-docker-files-runtime-vs-aspnet-vs-sdk/