Уже есть много статей, которые предоставляют вам подробную информацию о том, как контейнерировать .NET .NET Основное приложение. Тем не менее, я все еще видел необходимость написать немного более подробный пост, который поможет вам создать готовый к производству изображение контейнера на основе контейнера и .net Основные передовые практики.
Для лучшего понимания я объясню все подробно, основываясь на небольшом примере ASPNET Core Web. Вы найдете более подробную информацию о самом приложении Здесь Анкет Конечно, общие лучшие практики не ограничены .NET .NET Основной. Вы можете настроить их и использовать их с любым из ваших проектов.
Существуют миллионы вариантов использования, и поэтому нет ни одного решения для всех. Я хотел бы познакомить вас с двумя разными вариантами, которые я использую больше всего. Вы получите все детали, чтобы решить, кто из них работает лучше всего для вас. Давайте сначала начнем с оснований.
Это обычный пример Dockerfile, который встречается на моем пути довольно часто:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 WORKDIR /app COPY /app/output . EXPOSE 8080 ENTRYPOINT ["dotnet", "sample-mvc.dll"]
В этом нет ничего особенного. Это будет работать, но вообще не настроен и не оптимизирован. Ни для производительности, ни для проблем, связанных с безопасностью, и, следовательно, не оптимально для производственной среды. Некоторые примеры:
- контейнеры, выполняющие свой процесс как корень
- Неэффективный порядок сортировки, который приводит к более медленному времени сборки * из -за недопустимых кэшей слоя изображения
- неправильное управление слоем изображения, которое влияет на конечный размер изображения
- более медленные сборки из -за отсутствия
.dockerignore
файл
Давайте внимательно посмотрим на другой пример Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 WORKDIR /app ADD /src . RUN dotnet publish \ -c Release \ -o ./output EXPOSE 8080 ENTRYPOINT ["dotnet", "sample-mvc.dll"]
В этом примере используется контейнерная сборка. Это означает, что сама сборка приложения перемещается в процесс сборки Docker. Выполнение этого довольно хорошего шаблона, которая позволяет вам встроить неизменную и изолированную среду сборки со всеми встроенными зависимостями. Но в качестве недостатка вам нужно построить свое изображение на основе более крупного изображения SDK. Изображение SDK обеспечивает необходимые зависимости для создания приложения, но после этого не потребуется его выполнить. К счастью, есть решение, которое решает эту конкретную проблему.
Если вы используете аналогичную версию вышеупомянутых DockerFiles, вы, возможно, не слышали о функции, называемой многоэтапной сборкой. Многоценные сборки позволяют нам разделить процесс сборки изображения на несколько этапов. Первый этап используется для создания нашего приложения, которое требует, чтобы нам необходимо обеспечить необходимые зависимости. На втором этапе мы копируем артефакты приложения в меньшую среду выполнения, которая затем используется в качестве последнего изображения. Этот соответствующий Dockerfile может выглядеть так:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env WORKDIR /app ADD /src . RUN dotnet publish \ -c Release \ -o ./output FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 WORKDIR /app COPY --from=build-env /app/output . EXPOSE 8080 ENTRYPOINT ["dotnet", "sample-mvc.dll"]
Давайте поближе посмотрим на отдельные шаги:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env ... RUN dotnet publish \ -c Release \ -o ./output ...
Наш первый этап основан на изображении SDK, которое обеспечивает все зависимости для создания нашего приложения.
... FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 ... COPY --from=build-env /app/output . ...
На втором этапе мы определяем новое базовое изображение, которое на этот раз содержит только наши зависимости времени выполнения. Затем мы копируем артефакты приложения с первого этапа во второй.
При этом мы теперь можем создать меньшее и более безопасное изображение контейнера, потому что оно содержит только зависимости, необходимые для выполнения приложения. Но у нас все еще есть место для дальнейшего улучшения, о котором мы говорим в следующем абзаце.
Если вам нравится узнать больше о лучших практиках Dockerfile, я бы порекомендовал вам проверить Эта страница официальной документации Docker.
Как уже упоминалось выше, нет ни одной лучшей практики. Это варьируется от варианта использования для использования. С примерами ниже, вы получите два чертежа, а также их плюсы и минусы, которые вы можете использовать для их адаптации в соответствии с вашими потребностями.
Хорошая отправная точка
Приведенный ниже DockerFile является оптимизированной версией приведенного выше многоступенчатого примера и должна подходить для большинства сценариев.
ARG VERSION=3.1-alpine3.10 FROM mcr.microsoft.com/dotnet/core/sdk:$VERSION AS build-env WORKDIR /app ADD /src/*.csproj . RUN dotnet restore ADD /src . RUN dotnet publish \ -c Release \ -o ./output FROM mcr.microsoft.com/dotnet/core/aspnet:$VERSION RUN adduser \ --disabled-password \ --home /app \ --gecos '' app \ && chown -R app /app USER app WORKDIR /app COPY --from=build-env /app/output . ENV DOTNET_RUNNING_IN_CONTAINER=true \ ASPNETCORE_URLS=http://+:8080 EXPOSE 8080 ENTRYPOINT ["dotnet", "sample-mvc.dll"]
Опять же, мы внимательно рассмотрим отдельные шаги:
ARG VERSION=3.1-alpine3.10 FROM mcr.microsoft.com/dotnet/core/sdk:$VERSION AS build-env ...
Сначала мы определяем наш базовый тег изображения, используя инструкцию ARG. Это помогает нам легко обновить тег вместо того, чтобы менять несколько строк. Как вы могли заметить, мы используем другой тег. TAG3.1- Alpine3.10 утверждает, что это изображение содержит версию ASPNET 3.1 и основано на Alpine 3.10.
Alpine Linux — это распределение Linux, предназначенное для вариантов использования безопасности, простоты и эффективности ресурсов. На этом этапе Alpine Linux уже может помочь нам уменьшить след нашей стадии сборки.
... FROM mcr.microsoft.com/dotnet/core/aspnet:$VERSION ...
Поскольку мы используем многоэтапную сборку, нам также необходимо определить изображение, используемое на нашей последней стадии. Еще раз мы будем использовать среду выполнения ASPNet на основе альпийского базового изображения. Как уже говорилось, создание нашего изображения на основе альпийского позволяет нам создавать меньшее и более безопасное изображение контейнера.
ADD /src/*.csproj . RUN dotnet restore ADD /src . RUN dotnet publish \ -c Release \ -o ./output
В отличие от приведенного выше примера, мы разделим процесс сборки на несколько частей. Dotnet Restore
Команда использует Nuget для восстановления зависимостей, а также инструментов для конкретных проектов, которые указаны в файле проекта. Восстановление зависимостей также является частью Dotnet Pubish
Команда, но разделение позволяет нам встроить зависимости в отдельный слой изображения. Это сокращает время, необходимое для создания изображения, и уменьшает размер загрузки, поскольку зависимости слоя изображений восстанавливаются только в том случае, если зависимости изменяются.
... RUN adduser \ --disabled-password \ --home /app \ --gecos '' app \ && chown -R app /app USER app ...
Чтобы обеспечить время выполнения нашего приложения, нам нужно выполнить их без каких -либо привилегий корневых. Из -за этого мы создаем нового пользователя и изменяем пользовательский контекст, используя определение пользователя.
... ENV DOTNET_RUNNING_IN_CONTAINER=true \ ASPNETCORE_URLS=http://+:8080 EXPOSE 8080 ...
Поскольку мы запускаем наше приложение без каких -либо корневых привилегий, нам необходимо разоблачить его на порту выше 1024. В этом примере был выбран 8080. С определением ENV мы подвергаем дальнейшие переменные среды для нашего процесса подачи заявки. Dotnet_running_in_container = true является лишь неформальной переменной среды, позволяющей разработчику/приложению знать, что процесс работает в контейнере. Aspnetcore_urls = http://+: 8080 используется для предоставления времени выполнения информации для разоблачения процесса на порту 8080.
Меньше, меньше, меньше
Как уже упоминалось, приведенный выше пример должен соответствовать большинству сценариев. В следующем примере описывается способ создать наименьшее возможное изображение контейнера. Возможный вариант использования может быть для Крайневые варианты использования краев IoT или среды, которые требуют оптимизированного времени начала. К сожалению, мы также получаем некоторые недостатки, о которых я подробно расскажу ниже.
ARG VERSION=3.1-alpine3.10 FROM mcr.microsoft.com/dotnet/core/sdk:$VERSION AS build-env WORKDIR /app ADD /src . RUN dotnet publish \ --runtime alpine-x64 \ --self-contained true \ /p:PublishTrimmed=true \ /p:PublishSingleFile=true \ -c Release \ -o ./output FROM mcr.microsoft.com/dotnet/core/runtime-deps:$VERSION RUN adduser \ --disabled-password \ --home /app \ --gecos '' app \ && chown -R app /app USER app WORKDIR /app COPY --from=build-env /app/output . ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \ DOTNET_RUNNING_IN_CONTAINER=true \ ASPNETCORE_URLS=http://+:8080 EXPOSE 8080 ENTRYPOINT ["./sample-mvc", "--urls", "http://0.0.0.0:8080"]
Еще раз, мы внимательно рассмотрим отдельные шаги:
... RUN dotnet publish \ --runtime alpine-x64 \ --self-contained true \ /p:PublishTrimmed=true \ /p:PublishSingleFile=true \ -c Release \ -o ./output ...
Большая разница в верхней части заключается в том, что мы построим автономное приложение. Предоставление параметра -Содержал истинность
заставит сборку включать все зависимости в артефакт приложения. Который включает .NET Основное время выполнения. Из -за этого нам также необходимо определить время выполнения, в которой мы хотели бы выполнить бинар. Это делается с -Проберите Alpine-X64
параметр.
Поскольку конечное изображение должно быть оптимизировано для размера, мы определяем /п: Pubishtrimmed = true
Флаг, который советует процессу сборки не включать неиспользованные библиотеки. /п: Publishsinglefile = true
Флаг позволяет нам ускорить сам процесс сборки. В качестве недостатка, вам придется определить динамически загруженные сборки заранее, чтобы убедиться, что необходимые библиотеки не обрезаны и, следовательно, недоступны на изображении. Более подробная информация об этом доступна Здесь Анкет
Вторым недостатком наличия меньшего изображения является то, что изменения кода приводят к большему изменению. Это связано с тем, что код и время выполнения упакованы вместе в одном слое изображения. Каждый раз, когда код меняет, весь уровень изображения должен восстановить, а также перераспределяется на систему, запускающую код.
... FROM mcr.microsoft.com/dotnet/core/runtime-deps:$VERSION ...
Поскольку артефакт приложения является автономным, нам не нужно предоставлять время выполнения с изображением. В этом примере я выбрал изображение времени выполнения, основанное на альпийском Linux. Это изображение снято до минимальных местных зависимостей, необходимых для выполнения артефакта приложения.
... ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \ DOTNET_RUNNING_IN_CONTAINER=true \ ASPNETCORE_URLS=http://+:8080 ...
Другое улучшение размера изображения заключается в использовании инвариантного режима глобализации. Этот режим полезен для приложений, которые не осведомлены по всему миру, и которые могут использовать соглашения о форматировании, конвенции об корпусе, а также сравнение струн и порядок сортировки инвариантной культуры. Режим инварианта глобализации включен через Dotnet_system_globalization_invariant = 1 переменная среды. Если ваше приложение требует глобализации, вам нужно будет установить Библиотека ICU и удалить вышеуказанную переменную среды. Это увеличит размер изображения вашего контейнера примерно на 28 МБ. Вы найдете более подробную информацию о инвариантном режиме глобализации Здесь Анкет
... ENTRYPOINT ["./sample-mvc", "--urls", "http://0.0.0.0:8080"]
Для автономных приложений нам необходимо изменить определение точки входа, чтобы запустить сам двоичный файл. Размер этого изображения будет составлять около 73 МБ (включая мое применение применения). Давайте сравним это с другими изображениями:
- Изображение, основанное на общем многоступенчатом Dockerfile: 250 МБ
- Изображение, основанное на многоэтапном многоступенчатом Dockerfile: 124 МБ
Как уже упоминалось выше: какой DockerFile наиболее подходит для вас, зависит от вашего варианта использования. Меньше не обязательно лучше.
Если вы планируете развернуть свое приложение в Azure Kubernetes Service или экземпляры контейнеров Azure, вы также можете подумать о хранении ваших изображений в реестре контейнеров Azure. Реестр контейнеров Azure также поддерживает для создания контейнеров. Вы можете включить его в свой сборник сборки или просто назвать его вручную, используя AZ ACR BUILD
командование
Я надеюсь, что эти детали помогут вам в контейнере .NET .NET Основное приложение. Как уже упоминается, вышеупомянутые примеры являются лучшими практиками и, возможно, должны быть настроены в соответствии с вашими потребностями. Проверьте Майкл Димодис Пост На подробности о том, как укрепить свой .net Основные контейнерные изображения.
Оригинал: «https://dev.to/nmeisenzahl/containerize-your-net-core-app-the-right-way-2ceh»