Посмотрите, как упаковать основные библиотеки 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, и переход к нему показывает:

Ну-ну… Джилл и Джейн влюблены 👩‍❤️‍💋‍👩 . Подожди… Что случилось с Джеком❗

Что случилось с Джеком?

Давайте посмотрим, что мы сделали до сих пор.

  1. Мы создали основную библиотеку dotnet и базовый образ докера, содержащий эту библиотеку.
  2. Мы создали два разных типа проектов dotnet — консольный и webapi — для которых требуется библиотека dotnet в процессе сборки.
  3. Мы создали образы докеров для каждого из проектов dotnet, в которых используется библиотека, но явным образом убедились, что используемая библиотека не является той, на основе которой были построены эти проекты. Используемая библиотека — это та, которая получена из базового образа докера на шаге 1.

Шаг 3 кажется очень неудобным. Какой смысл использовать библиотеку из базового образа вместо сохранения зависимости для каждого проекта при создании образа? Что ж, посмотрим, в чем суть на самом деле.

Вот список образов Docker, которые мы создали на данный момент:

  1. s/lib:latest — содержит библиотеку классов
  2. s/console:latest — содержит библиотеку классов + dotnet/core/runtime
  3. 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/