Поделиться через


Руководство. Контейнеризация приложения .NET

В этом руководстве описано, как контейнеризировать приложение .NET с помощью Docker. Контейнеры имеют множество функций и преимуществ, таких как неизменяемая инфраструктура, предоставление переносимой архитектуры и включение масштабируемости. Образ можно использовать для создания контейнеров для локальной среды разработки, частного облака или общедоступного облака.

В этом руководстве вы:

  • Создание и публикация простого приложения .NET
  • Создание и настройка Dockerfile для .NET
  • Создание образа Docker
  • Создание и запуск контейнера Docker

Вы изучите задачи сборки и развертывания контейнеров Docker для приложения .NET. Платформа Docker использует подсистемы Docker для быстрой сборки и упаковки приложений в качестве образов Docker. Эти изображения записываются в формате Dockerfile для развертывания и запуска в многоуровневом контейнере.

Совет

Если вы заинтересованы в публикации приложения .NET в виде контейнера без необходимости использования Docker или Podman, см. Контейнеризация приложения .NET с помощью dotnet publish.

Заметка

Данное учебное пособие не предназначено для приложений ASP.NET Core. Если вы используете ASP.NET Core, ознакомьтесь с руководством по контейнеризации приложения ASP.NET Core.

Необходимые условия

Установите следующие предварительные требования:

  • .NET 8+ SDK.
    Если вы установили .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. Теперь вы изменяете каталоги и переходите в папку приложения из сеанса терминала. Используйте команду dotnet run для запуска приложения. Приложение запускается и выводит Hello World! ниже команды:

cd App
dotnet run
Hello World!

Шаблон по умолчанию создает приложение, которое выводится в терминал, а затем немедленно завершает работу. В этом руководстве используется приложение, которое циклит бесконечно. Откройте файл Program.cs в текстовом редакторе.

Совет

Если вы используете Visual Studio Code, в предыдущем сеансе терминала введите следующую команду:

code .

Эта команда открывает папку приложения , содержащую проект в 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

Чтобы приложение было подходящим для создания образа, его необходимо скомпилировать. Команда dotnet publish наиболее подходит для этого, так как она создает и публикует приложение. См. подробные сведения в документации по командам dotnet build и dotnet publish.

dotnet publish -c Release

Совет

Если вы заинтересованы в упаковке вашего приложения .NET в контейнер без использования Docker, ознакомьтесь с контейнеризацией приложения .NET с помощью dotnet publish.

Команда dotnet publish компилирует приложение в папку для публикации . Путь к папке публикации из рабочей папки должен быть ./App/bin/Release/<TFM>/publish/:

В папке App получите список каталогов папки публикации, чтобы убедиться, что файл DotNet.Docker.dll был создан.

dir .\bin\Release\net9.0\publish\

    Directory: C:\Users\default\docker-working\App\bin\Release\net9.0\publish

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----          1/6/2025  10:11 AM            431 DotNet.Docker.deps.json
-a----          1/6/2025  10:11 AM           6144 DotNet.Docker.dll
-a----          1/6/2025  10:11 AM         145408 DotNet.Docker.exe
-a----          1/6/2025  10:11 AM          11716 DotNet.Docker.pdb
-a----          1/6/2025  10:11 AM            340 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net8.0\publish\

    Directory: C:\Users\default\docker-working\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

Создайте Dockerfile

Файл Dockerfile используется командой для создания образа контейнера. Этот файл представляет собой текстовый файл с именем Dockerfile, который не имеет расширения.

Создайте файл с именем Dockerfile в каталоге, содержащий CSPROJ и откройте его в текстовом редакторе. В этом руководстве используется образ среды выполнения ASP.NET Core (который содержит образ среды выполнения .NET) и соответствует консольному приложению .NET.

FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Заметка

Образ среды выполнения ASP.NET Core используется намеренно здесь, хотя вместо этого можно использовать образ mcr.microsoft.com/dotnet/runtime:9.0.

FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /App

# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -o out

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0@sha256:6c4df091e4e531bb93bdbfe7e7f0998e7ced344f54426b7e874116a3dc3233ff
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Заметка

Образ среды выполнения ASP.NET Core используется намеренно здесь, хотя вместо этого можно использовать образ mcr.microsoft.com/dotnet/runtime:8.0.

Важный

Включение безопасного хэш-алгоритма (SHA) после тега изображения в Dockerfile рекомендуется. Это гарантирует, что изображение не изменено и что изображение совпадает с ожидаемым. SHA — это уникальный идентификатор изображения. Дополнительные сведения см. в статье Документация Docker: загрузка изображения по дайджесту.

Совет

В этом Dockerfile используются многоэтапные сборки, которые оптимизируют окончательный размер образа путем создания слоя сборки и оставляя только необходимые артефакты. Дополнительные сведения см. в документации Docker: многоэтапные сборки.

Ключевое слово FROM требует полного квалифицированного имени образа контейнера Docker. Реестр контейнеров Майкрософт (MCR, mcr.microsoft.com) — это синдикат Docker Hub, на котором размещаются общедоступные контейнеры. Сегмент dotnet — это репозиторий контейнеров, а сегмент sdk или aspnet — имя образа контейнера. Изображение помечено тегом 9.0, который используется для версионирования. Таким образом, mcr.microsoft.com/dotnet/aspnet:9.0 — это среда выполнения .NET 9.0. Убедитесь, что вы извлеките версию среды выполнения, соответствующую среде выполнения, предназначенной для пакета SDK. Например, приложение, созданное в предыдущем разделе, использовал пакет SDK для .NET 9.0, а базовый образ, указанный в Dockerfile, помечен как 9.0.

Важный

При использовании образов контейнеров под управлением Windows необходимо указать тег изображения, кроме 9.0, например, mcr.microsoft.com/dotnet/aspnet:9.0-nanoserver-1809 вместо mcr.microsoft.com/dotnet/aspnet:9.0. Выберите имя образа в зависимости от того, используете ли вы Nano Server или Windows Server Core и какую версию этой ОС. Полный список всех поддерживаемых тегов можно найти на странице Docker Hub .NET .

Сохраните файл Dockerfile. Структура каталогов рабочей папки должна выглядеть следующим образом. Некоторые из более глубоких файлов и папок опущены для экономии места в статье:

📁 docker-working
    └──📂 App
        ├── Dockerfile
        ├── DotNet.Docker.csproj
        ├── Program.cs
        ├──📂 bin
        │   └───📂 Release
        │        └───📂 net9.0
        │             ├───📂 publish
        │             │    ├─── DotNet.Docker.deps.json
        │             │    ├─── DotNet.Docker.dll
        │             │    ├─── DotNet.Docker.exe
        │             │    ├─── DotNet.Docker.pdb
        │             │    └─── DotNet.Docker.runtimeconfig.json
        │             ├─── DotNet.Docker.deps.json
        │             ├─── DotNet.Docker.dll
        │             ├─── DotNet.Docker.exe
        │             ├─── DotNet.Docker.pdb
        │             └─── DotNet.Docker.runtimeconfig.json
        └──📁 obj
            └──...

Ключевое слово 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
            └──...

Инструкция ENTRYPOINT устанавливает dotnet в качестве узла для DotNet.Docker.dll. Однако вместо этого можно определить ENTRYPOINT как исполняемый файл приложения, опираясь на ОС в качестве узла приложения:

ENTRYPOINT ["./DotNet.Docker"]

Это приводит к выполнению приложения непосредственно, без dotnet, вместо этого полагаясь на хост приложения и базовую ОС. Дополнительные сведения о развертывании кроссплатформенных двоичных файлов см. в разделе Создание кроссплатформенного двоичного файла.

Чтобы создать контейнер, выполните следующую команду:

docker build -t counter-image -f Dockerfile .

Docker обрабатывает каждую строку в Dockerfile. . в команде docker build задает контекст сборки образа. Переключатель -f — это путь к Dockerfile . Эта команда создает образ и локальный репозиторий с именем counter-image, который указывает на этот образ. После завершения этой команды запустите docker images, чтобы просмотреть список установленных образов:

REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
counter-image    latest    1c1f1433e51d   32 seconds ago   223MB
docker images
REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
counter-image    latest    2f15637dc1f6   10 minutes ago   217MB

Репозиторий counter-image — это имя образа. Кроме того, тег изображения, идентификатор изображения, размер и когда он был создан, являются частью выходных данных. Последние шаги Dockerfile — создать контейнер из образа и запустить приложение, скопировать опубликованное приложение в контейнер и определить точку входа:

FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]

Команда FROM указывает базовый образ и тег для использования. Команда WORKDIR изменяет текущий каталог внутри контейнера на каталог приложения App.

Команда COPY сообщает Docker скопировать указанный исходный каталог в целевую папку. В этом примере содержимое публикации на уровне выводится в папку с именем App/out, поэтому это источник для копирования. Все опубликованное содержимое в каталоге App/out копируются в текущий рабочий каталог (App).

Следующая команда, ENTRYPOINT, сообщает Docker настроить контейнер для запуска в качестве исполняемого файла. При запуске контейнера выполняется команда ENTRYPOINT. После завершения этой команды контейнер автоматически останавливается.

Совет

До .NET 8 контейнеры, настроенные для запуска только для чтения, могли завершиться ошибкой с Failed to create CoreCLR, HRESULT: 0x8007000E. Чтобы устранить эту проблему, укажите переменную среды DOTNET_EnableDiagnostics как 0 (непосредственно перед шагом ENTRYPOINT):

ENV DOTNET_EnableDiagnostics=0

Дополнительные сведения о различных переменных среды .NET см. в разделе переменных среды .NET.

Заметка

.NET 6 стандартизирует префикс DOTNET_ вместо COMPlus_ переменных среды, которые настраивают поведение во время выполнения .NET. Однако префикс COMPlus_ продолжит работать. Если вы используете предыдущую версию среды выполнения .NET, необходимо по-прежнему использовать префикс COMPlus_ для переменных среды.

Создание контейнера

Теперь, когда у вас есть образ, содержащий приложение, можно создать контейнер. Контейнер можно создать двумя способами. Сначала создайте остановленный контейнер.

docker create --name core-counter counter-image

Эта команда docker create создает контейнер на основе контр-образа изображения. Выходные данные команды 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

Изменение ТОЧКИ ВХОДА

Команда 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

Заметка

Этот пример работает только в контейнерах Windows. Контейнеры Linux не имеют cmd.exe.

Основные команды

Docker имеет множество различных команд, которые создают, управляют и взаимодействуют с контейнерами и изображениями. Эти команды Docker необходимы для управления контейнерами:

Очистка ресурсов

В этом руководстве вы создали контейнеры и образы. Если вы хотите, удалите эти ресурсы. Используйте следующие команды, чтобы

  1. Список всех контейнеров

    docker ps -a
    
  2. Остановите контейнеры, работающие с указанным именем.

    docker stop core-counter
    
  3. Удаление контейнера

    docker rm core-counter
    

Затем удалите все изображения, которые больше не нужны на компьютере. Удалите образ, созданный вашим Dockerfile, а затем удалите образ .NET, на основе которого был создан Dockerfile. Можно использовать идентификатор IMAGE или строку в формате РЕПОЗИТОРИЯ:TAG.

docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:9.0
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:8.0

Используйте команду docker images, чтобы просмотреть список установленных образов.

Совет / Чаевые

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

Дальнейшие действия