Am Ende von Lektion 2.9.6 stand eine unbequeme Zahl: Ihr Image war mehr als fünfmal so groß wie das aus dotnet publish. Darin lagen das
ganze SDK, alle NuGet-Pakete, der Quellcode und Werkzeuge wie git, curl und PowerShell. Die laufende Kladde braucht davon nichts. In
dieser Lektion trennen Sie Bauen und Ausführen. Das Ergebnis ist ein Image, das kleiner ist als das aus dotnet publish, keine Shell
enthält und trotzdem Deutsch und die Zeitzone des Büros kann.
Zwei Stufen in einem Dockerfile
Ein Dockerfile darf mehrere FROM enthalten. Jedes beginnt eine neue Stufe (englisch stage) mit eigenem Grund-Image. Eine spätere
Stufe kann mit COPY --from= Dateien aus einer früheren holen. Ins fertige Image kommt nur die letzte Stufe; alle anderen sind
Werkbänke, die nach dem Bauen weggeräumt werden.
Für die Kladde sind es zwei Stufen. Ersetzen Sie im Branch feature/dockerfile-mehrstufig das Dockerfile aus Lektion 2.9.6:
# Stage 1: build with the full SDK.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY global.json Directory.Build.props ./
COPY src/Kladde.Core/Kladde.Core.csproj src/Kladde.Core/
COPY src/Kladde.Data/Kladde.Data.csproj src/Kladde.Data/
COPY src/Kladde.Ted/Kladde.Ted.csproj src/Kladde.Ted/
COPY src/Kladde.ServiceDefaults/Kladde.ServiceDefaults.csproj src/Kladde.ServiceDefaults/
COPY src/Kladde.Web/Kladde.Web.csproj src/Kladde.Web/
RUN dotnet restore src/Kladde.Web/Kladde.Web.csproj
COPY src/ src/
RUN dotnet publish src/Kladde.Web/Kladde.Web.csproj -c Release --no-restore -o /app
# Stage 2: run on the chiseled ASP.NET Core image with ICU and time zones.
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra AS final
WORKDIR /app
COPY --from=build /app .
USER $APP_UID
EXPOSE 8080
ENTRYPOINT ["dotnet", "Kladde.Web.dll"]
AS buildgibt der ersten Stufe einen Namen. Sie ist bis zumpublishdieselbe wie in Lektion 2.9.6, mit derselben Reihenfolge für den Cache.- Das zweite
FROMbeginnt von vorn, auf einem Image, das nur die ASP.NET-Core-Laufzeit enthält und kein SDK. COPY --from=build /app .holt genau einen Ordner aus der ersten Stufe: die veröffentlichte Kladde. Pakete, Quellcode und Compiler bleiben drüben.USER $APP_UID: Die .NET-Images setzen die UmgebungsvariableAPP_UIDauf 1654, die Nummer des Benutzersapp. Eine Nummer statt eines Namens hat einen Vorteil: Plattformen wie Kubernetes können an ihr prüfen, dass der Container nicht alsrootläuft, ohne ins Image zu sehen. Die Chiseled-Images laufen ohnehin schon alsapp; die Zeile macht es sichtbar und gilt auch, wenn jemand das Grund-Image wechselt.EXPOSE 8080ist die Notiz aus Lektion 2.9.6 für Menschen und Werkzeuge. Den Port selbst setzt das Grund-Image mitASPNETCORE_HTTP_PORTS=8080. Port 8080 statt 80 hat einen Grund: Ports unter 1024 darf unter Linux nurrootöffnen.
Bauen Sie wie gewohnt aus dem Hauptordner der Kladde:
docker build -f src/Kladde.Web/Dockerfile -t kladde-web:mehrstufig --progress=plain .
docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" kladde-web
REPOSITORY TAG SIZE
kladde-web mehrstufig 245MB
kladde-web dockerfile 1.72GB
kladde-web 0.9.0 314MB
Von 1,72 Gigabyte auf 245 Megabyte, und auch kleiner als das Image aus dotnet publish, das auf dem vollen Ubuntu-Image aspnet:10.0
aufbaut. Die Zahlen stammen aus einer Beispiel-Kladde; bei Ihnen weichen sie ab.
Wollen Sie nur die erste Stufe bauen, etwa um in ihr nach einem Fehler zu suchen, sagen Sie es mit --target build. Davon macht Übung 2
Gebrauch.