🐳 Как создать многоступенчатый Dockerfile для .NET Core

by itisgood
Если вы работаете с .NET Core и Docker, то наверняка задавались вопросом, как сделать свои контейнерные приложения более компактными, быстрыми и безопасными.

Зачем нужны многоступенчатые Docker-файлы?

Представьте себе: у вас есть приложение .NET Core – возможно, небольшой API или веб-приложение – и вы хотите его контейнеризировать.
Вы можете просто запихнуть все в один Dockerfile, но тогда вы будете таскать с собой весь .NET SDK, инструменты сборки и кучу всего, что вам не понадобится, когда приложение действительно запустится.
Это все равно что собирать в поездку весь набор инструментов, когда нужна только отвертка.
Многоступенчатые Docker-файлы разделяют процесс на – вы угадали – этапы.
На одном этапе выполняется тяжелая работа по созданию приложения, а на другом – только готовый продукт и его запуск.
Результат?
Меньшие образы, меньший объем и более счастливый конвейер развертывания.
Кроме того, это шанс применить навыки работы с Docker и почувствовать себя немного волшебником.

Давайте соберем

Вот реальный пример многоступенчатого Dockerfile для приложения .NET Core.
Мы будем разбирать его по ходу дела.
# Stage 1: Build environment
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy csproj and restore dependencies
COPY *.csproj ./
RUN dotnet restore

# Copy the rest of the code
COPY . ./
RUN dotnet build -c Release --no-restore

# Publish the app
RUN dotnet publish -c Release -o /app --no-restore

# Stage 2: Runtime environment
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app

# Add a non-root user for security
RUN useradd -m -r -u 1000 appuser

# Copy the published files
COPY --from=build /app ./

# Set ownership
RUN chown -R appuser:appuser /app

# Switch to non-root user
USER appuser

# Expose the port
EXPOSE 8080

# Set the entry point
ENTRYPOINT ["dotnet", "YourAppName.dll"]

Этап 1: сборка

Первая часть, обозначенная как сборка, – это то место, где происходит все волшебство.

Мы начинаем с образа .NET SDK (mcr.microsoft.com/dotnet/sdk:8.0).

В нем есть все, что нужно для компиляции нашего кода: SDK, инструменты, все остальное.

Мы установили в качестве рабочего каталога /src, просто чтобы все было аккуратно.

Сначала мы копируем файл .csproj и запускаем dotnet restore.

Почему только файл проекта?

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

Это экономит кучу времени.

Затем я переношу остальной код с помощью COPY . ./ и запускаю dotnet build. Флаг –no-restore говорит ему, что мы уже справились с зависимостями.

Наконец, dotnet publish выплевывает скомпилированное приложение в папку /app.

Это наш золотой билет на следующий этап.

Этап 2: Runtime

Теперь мы переключаем передачу.

На этапе выполнения используется более компактный образ: mcr.microsoft.com/dotnet/aspnet:8.0.

Это просто среда выполнения ASP.NET – ни SDK, ни дополнительного багажа.

В качестве рабочего каталога я выбрал /app, потому что именно там мы будем жить.

Безопасность имеет большое значение, поэтому я добавляю пользователя, не являющегося root, под именем appuser.

Работать от имени root в контейнере – все равно что оставлять входную дверь незапертой, не самая лучшая идея.

Затем я забираю опубликованные файлы со стадии сборки с помощью COPY –from=build /app ./. Быстрый chown передает права на appuser, а USER appuser гарантирует, что мы не работаем от имени root.

Я открываю порт 8080 (вы можете изменить его, если ваше приложение использует что-то другое), и ENTRYPOINT запускает приложение с dotnet YourAppName.dll.

Не забудьте вставить сюда имя вашей реальной DLL!

Запуск

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

docker build -t mycoolapp . docker run -p 8080:8080 mycoolapp

Нажмите http://localhost:8080 в браузере, и бум – ваше приложение готово к работе.

Итак, в чем же заключается выгода?

Во-первых, конечный образ получается крошечным по сравнению с одноэтапной сборкой.

Вы не таскаете с собой SDK или инструменты сборки – только среда выполнения и ваше приложение.

Кроме того, это более безопасно при использовании не root-пользователя.

А благодаря кэшированию Docker изменение кода не означает пересоздание всего с нуля.

Меньшие образы означают более быстрое развертывание, а постоянство Docker позволяет мне не бороться с проблемами «работает на моей машине».

Настройки и советы

Это надежная отправная точка, но вы можете подстроить ее под свои нужды.

Нужна проверка хэлс чек ?

Добавьте:

HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:8080/health || exit 1
У вас есть постоянные данные?
Добавьте том:
VOLUME /app/data
А если вы ориентируетесь на несколько архитектур, измените строку FROM с помощью параметра –platform=$BUILDPLATFORM.
Подобные небольшие настройки могут принести много пользы.

Заключение

Многоступенчатые Docker-файлы стали моей палочкой-выручалочкой для приложений .NET Core.

Они требуют некоторой настройки, но как только вы освоите их, все пойдет как по маслу.

Вы получаете более компактный контейнер, готовый к работе от сборки до выполнения.

Попробуйте его в своем следующем проекте – наверняка вы удивитесь, как раньше обходились без него.

см. также:

 

Loading

You may also like

Leave a Comment