Как оптимизировать сборку образа Docker
Что проверяют
Понимаете ли вы, как работает кэш слоёв и чем образ для сборки отличается от образа для запуска. В .NET это особенно заметно: SDK весит в разы больше рантайма, и без multi-stage в прод уезжает компилятор. Следом часто спрашивают, почему после правки одной строки кода заново скачиваются все NuGet-пакеты.
Как отвечать
Основные приёмы:
- multi-stage: сборка в
mcr.microsoft.com/dotnet/sdk, запуск вmcr.microsoft.com/dotnet/aspnetтой же версии. В финальный образ копируется только результатdotnet publish; - порядок слоёв: сначала
COPYдля*.csprojи*.sln, затемdotnet restore, и только потомCOPY . .иdotnet publish -c Release --no-restore. Пока проектные файлы не менялись, слой с восстановлением пакетов берётся из кэша; - кэш BuildKit:
RUN --mount=type=cache,target=/root/.nuget/packages dotnet restoreсохраняет пакеты между сборками, даже когда слой инвалидирован; .dockerignore: исключитьbin/,obj/,.gitи локальные настройки. Иначе контекст раздувается, аobjс хоста может сломать restore внутри контейнера;- размер рантайма: варианты на Alpine или chiseled-образы на Ubuntu без шелла и пакетного менеджера. Для self-contained и Native AOT подходит ещё более лёгкий
runtime-deps; - безопасность: современные образы .NET содержат непривилегированного пользователя
app, и запускать приложение стоит от него черезUSER.
Схема финального Dockerfile: FROM ... AS build, restore, publish, затем вторая стадия и COPY --from=build /app/publish ..
Где спотыкаются
Собирают на голом alpine и вызывают там dotnet publish. SDK в таком образе нет, команда просто не найдётся. Маленький базовый образ нужен стадии запуска, а не сборки.
Удаляют файлы отдельной инструкцией RUN rm в следующем слое. Размер образа от этого не уменьшается: файлы остаются в предыдущем слое. Чистить нужно в той же инструкции, где они появились, или не переносить их в финальную стадию.
Берут устаревшие теги вроде aspnet:5.0. Эта версия давно без поддержки и обновлений безопасности. Для прода нужна актуальная LTS, а тег закрепляют до конкретной версии или дайджеста, а не latest.