Учебник. Контейнеризация приложения .NET
Из этого руководства вы узнаете, как контейнеризировать приложение .NET с помощью Docker. Контейнеры имеют множество функций и преимуществ, таких как неизменяемая инфраструктура, предоставление переносимой архитектуры и обеспечение масштабируемости. Этот образ можно использовать для создания контейнеров в вашей локальной среде разработки, частном или общедоступном облаке.
Изучив это руководство, вы:
- Создание и публикация простого приложения .NET.
- Создание и настройка Dockerfile для .NET.
- Создание образа Docker
- создать и запустить контейнер Docker.
Вы также узнаете о задачах сборки и развертывания контейнера Docker для приложения .NET. Платформа Docker использует модуль Docker для быстрой сборки и упаковки приложений в качестве образов Docker. Эти образы имеют формат Dockerfile и предназначены для развертывания и запуска в многоуровневом контейнере.
Примечание.
Это руководство не относится к приложениям ASP.NET Core. Если вы используете ASP.NET Core, см. руководство по контейнеризации приложений ASP.NET Core.
Необходимые компоненты
Установите следующие необходимые компоненты:
- Пакет SDK для .NET 8+
Если у вас установлена платформа .NET, воспользуйтесь командойdotnet --info
, чтобы определить используемую версию пакета SDK. - Docker Community Edition.
- Временная рабочая папка для Dockerfile и примера приложения .NET. В этом руководстве в качестве рабочей папки используется имя docker-working.
- Пакет SDK для .NET 7+
Если у вас установлена платформа .NET, воспользуйтесь командойdotnet --info
, чтобы определить используемую версию пакета SDK. - Docker Community Edition.
- Временная рабочая папка для Dockerfile и примера приложения .NET. В этом руководстве в качестве рабочей папки используется имя docker-working.
Создание приложения .NET
Вам нужно приложение .NET, которое запускает контейнер Docker. Откройте терминал, создайте рабочую папку, если вы еще этого не сделали, и войдите в нее. В рабочей папке выполните следующую команду, чтобы создать проект в подкаталоге App:
dotnet new console -o App -n DotNet.Docker
Дерево папок выглядит следующим образом:
📁 docker-working
└──📂 App
├──DotNet.Docker.csproj
├──Program.cs
└──📂 obj
├── DotNet.Docker.csproj.nuget.dgspec.json
├── DotNet.Docker.csproj.nuget.g.props
├── DotNet.Docker.csproj.nuget.g.targets
├── project.assets.json
└── project.nuget.cache
Команда dotnet new
создает папку с именем App и консольное приложение Hello World. Измените каталоги и перейдите в папку App из сеанса терминала. Используйте команду dotnet run
, чтобы запустить приложение. Приложение запускается и выводится Hello World!
под командой:
cd App
dotnet run
Hello World!
Шаблон по умолчанию создает приложение, которое выводит текст в терминал и затем завершает работу. В этом руководстве используется приложение, которое циклит бесконечно. Откройте файл Program.cs в текстовом редакторе.
Совет
Если вы используете Visual Studio Code, в предыдущем сеансе терминала введите следующую команду:
code .
Откроется папка App, которая содержит проект в Visual Studio Code.
Файл Program.cs должен выглядеть как следующий фрагмент кода C#:
Console.WriteLine("Hello World!");
Замените его кодом, который считает числа каждую секунду:
var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
Console.WriteLine($"Counter: {++counter}");
await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}
var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
Console.WriteLine($"Counter: {++counter}");
await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}
Сохраните файл и протестируйте программу еще раз с помощью команды dotnet run
. Помните, что это приложение выполняется бесконечно. Остановите его с помощью команды отмены, нажав клавиши CTRL+C. Ниже представлен пример таких выходных данных:
dotnet run
Counter: 1
Counter: 2
Counter: 3
Counter: 4
^C
Если приложению передать число в командной строке, оно досчитает до такого числа и завершит работу. Введите команду dotnet run -- 5
, чтобы приложение досчитало до пяти.
Важно!
Все параметры после --
не передаются команде dotnet run
, а передаются в приложение.
Публикация приложения .NET
Прежде чем добавлять приложение .NET в образ Docker, его необходимо опубликовать. Лучше всего запустить опубликованную версию приложения контейнером. Чтобы опубликовать приложение, выполните следующую команду:
dotnet publish -c Release
Эта команда компилирует приложение и помещает результат в папку publish. Путь к папке публикации из рабочей папки должен быть .\App\bin\Release\net8.0\publish\
.
Эта команда компилирует приложение и помещает результат в папку publish. Путь к папке публикации из рабочей папки должен быть .\App\bin\Release\net7.0\publish\
.
Получите список файлов для папки publish из папки App, чтобы проверить, был ли создан файл DotNet.Docker.dll.
dir .\bin\Release\net8.0\publish\
Directory: C:\Users\default\App\bin\Release\net8.0\publish
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/22/2023 9:17 AM 431 DotNet.Docker.deps.json
-a--- 9/22/2023 9:17 AM 6144 DotNet.Docker.dll
-a--- 9/22/2023 9:17 AM 157696 DotNet.Docker.exe
-a--- 9/22/2023 9:17 AM 11688 DotNet.Docker.pdb
-a--- 9/22/2023 9:17 AM 353 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net7.0\publish\
Directory: C:\Users\default\App\bin\Release\net7.0\publish
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2/13/2023 1:52 PM 431 DotNet.Docker.deps.json
-a--- 2/13/2023 1:52 PM 6144 DotNet.Docker.dll
-a--- 2/13/2023 1:52 PM 153600 DotNet.Docker.exe
-a--- 2/13/2023 1:52 PM 11052 DotNet.Docker.pdb
-a--- 2/13/2023 1:52 PM 253 DotNet.Docker.runtimeconfig.json
Создание файла Dockerfile
Файл Dockerfile используется командой docker build
для создания образа контейнера. Это текстовый файл с именем Dockerfile, не имеющий расширения.
Создайте файл с именем Dockerfile в каталоге, содержащий CSPROJ , и откройте его в текстовом редакторе. В этом руководстве используется образ среды выполнения ASP.NET Core (который содержит образ среды выполнения .NET) и соответствует консольного приложения .NET.
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Примечание.
Образ среды выполнения ASP.NET Core используется намеренно, хотя может использоваться образ mcr.microsoft.com/dotnet/runtime:8.0
.
Совет
Этот файл Dockerfile использует многоэтапные сборки, которые оптимизируют окончательный размер образа путем слоев сборки и выхода только необходимых артефактов. Дополнительные сведения см. в документации Docker: многоэтапные сборки.
Ключевое слово FROM
требует полного имени образа контейнера Docker. Реестр контейнеров Майкрософт (MCR, mcr.microsoft.com) — это синдикат Docker Hub, на котором размещаются общедоступные контейнеры. Сегмент dotnet
— это репозиторий контейнеров, а sdk
aspnet
сегмент — это имя образа контейнера. Образ помечается 8.0
для управления версиями. Таким образом, mcr.microsoft.com/dotnet/aspnet:8.0
среда выполнения .NET 8.0. Убедитесь, что вызываете версию среды выполнения, которая соответствует версии среды выполнения, с которой работает пакет SDK. Например, приложение, созданное в предыдущем разделе, использовал пакет SDK для .NET 8.0, а базовый образ, упомянутый в Dockerfile , помечен как 8.0.
Важно!
При использовании образов контейнеров под управлением Windows необходимо указать тег изображения, кроме простого 8.0
, например, mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-1809
вместо mcr.microsoft.com/dotnet/aspnet:8.0
. Выберите имя образа в зависимости от того, используете ли вы Nano Server или Windows Server Core и какую версию этой ОС. Полный список всех поддерживаемых тегов можно найти. Страница Docker Hub в NET.
Сохраните файл Dockerfile. Структура каталогов рабочей папки должна выглядеть следующим образом. Некоторые файлы и папки на более глубоком уровне были опущены для экономии места в статье:
📁 docker-working
└──📂 App
├── Dockerfile
├── DotNet.Docker.csproj
├── Program.cs
├──📂 bin
│ └──📂 Release
│ └──📂 net8.0
│ └──📂 publish
│ ├── DotNet.Docker.deps.json
│ ├── DotNet.Docker.exe
│ ├── DotNet.Docker.dll
│ ├── DotNet.Docker.pdb
│ └── DotNet.Docker.runtimeconfig.json
└──📁 obj
└──...
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Примечание.
Образ среды выполнения ASP.NET Core используется намеренно, хотя может использоваться образ mcr.microsoft.com/dotnet/runtime:7.0
.
Совет
Этот файл Dockerfile использует многоэтапные сборки, которые оптимизируют окончательный размер образа путем слоев сборки и выхода только необходимых артефактов. Дополнительные сведения см. в документации Docker: многоэтапные сборки.
Ключевое слово FROM
требует полного имени образа контейнера Docker. Реестр контейнеров Майкрософт (MCR, mcr.microsoft.com) — это синдикат Docker Hub, на котором размещаются общедоступные контейнеры. Сегмент dotnet
— это репозиторий контейнеров, а sdk
aspnet
сегмент — это имя образа контейнера. Образ помечается 7.0
для управления версиями. Таким образом, mcr.microsoft.com/dotnet/aspnet:7.0
это среда выполнения .NET 7.0. Убедитесь, что вызываете версию среды выполнения, которая соответствует версии среды выполнения, с которой работает пакет SDK. Например, приложение, созданное в предыдущем разделе, использовало пакет SDK для .NET 7.0, а базовый образ, упомянутый в Dockerfile , помечен как 7.0.
Сохраните файл Dockerfile. Структура каталогов рабочей папки должна выглядеть следующим образом. Некоторые файлы и папки на более глубоком уровне были опущены для экономии места в статье:
📁 docker-working
└──📂 App
├── Dockerfile
├── DotNet.Docker.csproj
├── Program.cs
├──📂 bin
│ └──📂 Release
│ └──📂 net7.0
│ └──📂 publish
│ ├── DotNet.Docker.deps.json
│ ├── DotNet.Docker.exe
│ ├── DotNet.Docker.dll
│ ├── DotNet.Docker.pdb
│ └── DotNet.Docker.runtimeconfig.json
└──📁 obj
└──...
В терминале выполните следующую команду:
docker build -t counter-image -f Dockerfile .
Docker обработает все строки файла Dockerfile. В .
команде docker build
задается контекст сборки образа. Переключение -f
— это путь к Dockerfile. Эта команда создает образ и локальный репозиторий с именем counter-image, который указывает на такой образ. После завершения работы этой команды выполните команду docker images
, чтобы просмотреть список установленных образов:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest 2f15637dc1f6 10 minutes ago 217MB
Репозиторий counter-image
— это имя образа. Тег latest
— это тег, используемый для идентификации изображения. Идентификатор 2f15637dc1f6
изображения. Время 10 minutes ago
создания образа. Размер 217MB
изображения. Заключительные шаги Dockerfile — создать контейнер из образа и запустить приложение, скопировать опубликованное приложение в контейнер и определить точку входа.
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest 2f15637dc1f6 10 minutes ago 208MB
Репозиторий counter-image
— это имя образа. Тег latest
— это тег, используемый для идентификации изображения. Идентификатор 2f15637dc1f6
изображения. Время 10 minutes ago
создания образа. Размер 208MB
изображения. Заключительные шаги Dockerfile — создать контейнер из образа и запустить приложение, скопировать опубликованное приложение в контейнер и определить точку входа.
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Команда COPY
предписывает Docker скопировать указанную папку на вашем компьютере в папку в контейнере. В этом примере папка публикации копируется в папку с именем App/out в контейнере.
Команда WORKDIR
изменяет текущий каталог в контейнере на App.
Следующая команда ENTRYPOINT
используется, чтобы настроить с помощью Docker контейнер для запуска в качестве исполняемого файла. При запуске контейнера выполняется команда ENTRYPOINT
. После выполнения команды контейнер автоматически остановится.
Совет
До .NET 8 контейнеры, настроенные для запуска только для чтения, могут завершиться ошибкой Failed to create CoreCLR, HRESULT: 0x8007000E
. Чтобы устранить эту проблему, укажите DOTNET_EnableDiagnostics
переменную среды как 0
(непосредственно перед ENTRYPOINT
шагом):
ENV DOTNET_EnableDiagnostics=0
Дополнительные сведения о различных переменных среды .NET см. здесь.
Примечание.
.NET 6 стандартизует префикс DOTNET_
вместо COMPlus_
для переменных среды, которые настраивают поведение .NET во время выполнения. Но префикс COMPlus_
будет и дальше работать. Если вы используете предыдущую версию среды выполнения .NET, следует и дальше использовать префикс COMPlus_
для переменных среды.
Создание контейнера
Теперь, когда у вас есть образ, содержащий приложение, вы можете создать контейнер. Контейнер можно создать двумя способами. Сначала создайте остановленный контейнер.
docker create --name core-counter counter-image
Эта docker create
команда создает контейнер на основе изображения счетчика. В выходных данных этой команды показано, что идентификатор контейнера (ваш будет отличаться) созданного контейнера:
d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf
Чтобы просмотреть список всех контейнеров, воспользуйтесь командой docker ps -a
:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d0be06126f7d counter-image "dotnet DotNet.Docke…" 12 seconds ago Created core-counter
Управление контейнером
Контейнер был создан с определенным именем core-counter
. Для управления контейнером используется это имя. В следующем примере используется команда docker start
для запуска контейнера, а затем — команда docker ps
для отображения только запущенных контейнеров:
docker start core-counter
core-counter
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cf01364df453 counter-image "dotnet DotNet.Docke…" 53 seconds ago Up 10 seconds core-counter
Аналогичным образом docker stop
команда останавливает контейнер. В следующем примере используется команда docker stop
для остановки контейнера, а затем — команда docker ps
для подтверждения того, что контейнеры не запущены:
docker stop core-counter
core-counter
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Подключение к контейнеру
После запуска контейнера вы можете подключиться к нему, чтобы просмотреть выходные данные. С помощью команд docker start
и docker attach
запустите контейнер и просмотрите поток вывода. В этом примере команда, вызываемая нажатием клавиш CTRL+C, используется для отключения от запущенного контейнера. Этот нажатие клавиш завершает процесс в контейнере, если не указано иное, что остановит контейнер. Параметр --sig-proxy=false
гарантирует, что ctrl+C не остановит процесс в контейнере.
После отключения от контейнера снова подключитесь к нему, чтобы убедиться в том, что он продолжает работать и считать числа.
docker start core-counter
core-counter
docker attach --sig-proxy=false core-counter
Counter: 7
Counter: 8
Counter: 9
^C
docker attach --sig-proxy=false core-counter
Counter: 17
Counter: 18
Counter: 19
^C
Удаление контейнера
Для этой статьи вы не хотите, чтобы контейнеры висят вокруг, что ничего не делать. Удалите созданный ранее контейнер. Если контейнер запущен, остановите его.
docker stop core-counter
В примере ниже выводится список всех контейнеров, Затем она использует docker rm
команду для удаления контейнера, а затем проверка второй раз для всех запущенных контейнеров.
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f6424a7ddce counter-image "dotnet DotNet.Dock…" 7 minutes ago Exited (143) 20 seconds ago core-counter
docker rm core-counter
core-counter
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Однократный запуск
Docker предоставляет единую команду docker run
для создания и запуска контейнера. Она исключает необходимость в поочередном выполнении команд docker create
и docker start
. Вы также можете настроить ее для автоматического удаления контейнера при его остановке. Например, команда docker run -it --rm
выполняет две операции. Сначала она автоматически подключается к контейнеру с помощью текущего терминала, а потом, после завершения работы контейнера, удаляет его:
docker run -it --rm counter-image
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
^C
Контейнер также передает параметры для выполнения приложения .NET. Чтобы указать приложению .NET число только до трех, передайте 3.
docker run -it --rm counter-image 3
Counter: 1
Counter: 2
Counter: 3
При этом docker run -it
команда CTRL+C останавливает процесс, выполняемый в контейнере, который, в свою очередь, останавливает контейнер. Так как в команде указан параметр --rm
, контейнер автоматически удалится после остановки процесса. Убедитесь, что он больше не существует:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Изменение команды ENTRYPOINT
Команда docker run
также позволяет изменить команду ENTRYPOINT
из файла Dockerfile для запуска другой программы, но только для соответствующего контейнера. Например, воспользуйтесь указанной ниже командой, чтобы запустить bash
или cmd.exe
. При необходимости измените команду.
В этом примере команда ENTRYPOINT
изменена на cmd.exe
. Нажав клавиши CTRL+C, вы можете завершить процесс и остановить контейнер.
docker run -it --rm --entrypoint "cmd.exe" counter-image
Microsoft Windows [Version 10.0.17763.379]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\>dir
Volume in drive C has no label.
Volume Serial Number is 3005-1E84
Directory of C:\
04/09/2019 08:46 AM <DIR> app
03/07/2019 10:25 AM 5,510 License.txt
04/02/2019 01:35 PM <DIR> Program Files
04/09/2019 01:06 PM <DIR> Users
04/02/2019 01:35 PM <DIR> Windows
1 File(s) 5,510 bytes
4 Dir(s) 21,246,517,248 bytes free
C:\>^C
Основные команды
В Docker есть множество различных команд, которые создают контейнеры и образы, управляют ими, а также взаимодействуют с ними. Для управления контейнерами в основном используются такие команды Docker:
Очистка ресурсов
В этом учебнике описано, как создать контейнеры и образы. При желании эти ресурсы можно удалить. Ниже представлены команды, которые позволяют сделать следующее:
Вывод списка всех контейнеров
docker ps -a
Остановить запущенные контейнеры по имени.
docker stop core-counter
Удаление контейнера
docker rm core-counter
Затем удалите все ненужные образы на компьютере. Удалите образ, созданный с помощью файла Dockerfile, а затем удалите образ .NET, на основе которого был создан файл Dockerfile. Вы можете использовать значение IMAGE ID или строку в формате РЕПОЗИТОРИЙ:МЕТКА.
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:8.0
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:7.0
С помощью команды docker images
просмотрите список установленных образов.
Совет
Файлы образов могут иметь большой размер. Как правило, удаляются временные контейнеры, созданные в ходе тестирования и разработки приложения. При этом рекомендуется оставить базовые образы с установленной средой выполнения, если на ее основе вы планируете создавать другие образы.