DOTNET: AOT Kompilierung in Docker/Container

Einleitung
Dieser Artikel befasst sich mit der AOT (Ahead-Of-Time) Kompilierung einer .NET Core 8-Anwendung in Docker und deren Bereitstellung als fertiges Image.
Bevor wir uns der konkreten Implementierung der AOT-Kompilierung in Docker widmen, folgt eine kurze Einführung in AOT und Docker-Kompilierung.
AOT
Die AOT Kompilierung in C# ist eine Technik, die den Code vor der Laufzeit kompiliert. Dies kann zu einer verbesserten Leistung führen, da der Code nicht zur Laufzeit interpretiert oder just-in-time kompiliert werden muss.
In ASP.NET Core 8.0 wurde Unterstützung für native .NET-AOT-Kompilierung eingeführt1. Die Veröffentlichung und Bereitstellung einer nativen AOT-App bietet folgende Vorteile:
- Minimierter benötigter Speicherplatz auf dem Datenträger: Bei der Veröffentlichung mit nativer AOT-Kompilierung wird eine einzelne ausführbare Datei erstellt, die nur den Code aus externen Abhängigkeiten enthält, der zur Unterstützung des Programms erforderlich ist.
- Verkürzte Startzeit: Native AOT-Anwendungen können kürzere Startzeiten aufweisen.
- Reduzierter Speicherbedarf: Native AOT-Apps können je nach den von der App ausgeführten Aktionen einen geringeren Speicherbedarf aufweisen.
Es ist jedoch wichtig zu beachten, dass nicht alle ASP.NET Core-Features mit nativer AOT-Kompilierung kompatibel sind. Beispielsweise sind MVC, Blazor Server und SignalR nicht unterstützt
Kompilierung in Docker/Container
Bei der Kompilierung einer Anwendung in Docker wird ein neues Build-Image erstellt, zu dem der Quellcode hinzugefügt wird. Zudem werden alle Anwendungen und Dateien, die für die Kompilierung notwendig sind, ebenfalls hinzugefügt. Anschließend wird die Anwendung kompiliert.
Die Verwendung von Docker zum Kompilieren bietet den Vorteil der Reproduzierbarkeit und Skalierbarkeit. Das Image muss nur einmal in einem Dockerfile konfiguriert werden und kann dann immer wieder verwendet werden. Das Build-Image kann zunächst lokal auf dem eigenen Client getestet werden, bevor es für die Build-Pipeline freigegeben wird. Spezifische Einstellungen auf dem Build-Server entfallen. Bei einer neuen .Net Core-Version muss lediglich das Image angepasst werden. Es sind keine Installationen auf dem Build-Server erforderlich. Dadurch können neue Build-Server einfach integriert werden. Darüber hinaus steht nach dem Erstellen direkt ein Docker-Image zur Verfügung.
.Net Core AOT Kompilierung in Docker
Dieses Kapitel veranschaulicht beispielhaft, wie eine .dockerignore- und Dockerfile-Datei aussehen könnten. Es ist notwendig, diese Dateien entsprechend den Anforderungen Ihres eigenen Projekts anzupassen. Vorausgesetzt werden Kenntnisse in Docker und eine bestehende Installation. Theoretisch sollte auch Podman funktionieren.
Für eine umfassendere Auseinandersetzung mit Docker empfehle ich das Buch “Skalierbare Container-Infrastrukturen: Das Handbuch für Planung und Administration”, welches auch die Container-Orchestrierung mit Kubernetes und OpenShift behandelt.
.dockerignore
Die .dockerignore-Datei dient dazu, festzulegen, welche Dateien beim Kopieren mit dem Befehl Copy
ignoriert werden sollen. Im untenstehenden Beispiel werden alle Dateien in den Ordnern ‘obj’ und ‘bin’ ignoriert. Durch die Reduzierung der Datenmenge kann die Erstellung des Build-Images beschleunigt werden.
**/obj/
**/bin/
Dockerfile
Zunächst wird das Runtime-Image definiert und ein Benutzer hinzugefügt. Die Anwendung befindet sich im Verzeichnis /app
. Anschließend wird der Build mit der .NET SDK 8.0 erstellt. Dabei werden sowohl die Projektmappe als auch der Quellcode zum Image hinzugefügt. Danach wird die Anwendung kompiliert, in diesem Fall für Linux-x64. Für win-x64 (Windows) wäre ein Windows-Container erforderlich. Bei der Verwendung von Docker-Hub mit Windows WSL 2 wird stets ein Linux-Container erstellt.
Schließlich wird das eigentliche Image erstellt. Die kompilierte Anwendung wird in das Verzeichnis /app
kopiert und das auszuführende Programm wird als Entrypoint
festgelegt. Bei Linux hat die auszuführende Datei keine .exe
-Endung. Die Labels sind optional.
Das Image kann mit dem Befehl docker build -t {ImageName} .
ausgeführt werden.
# Runtime Image
FROM alpine:3.19.1 AS prepare
# libc6-compat wird zum ausführen benötigt
RUN apk add --no-cache libc6-compat
WORKDIR /app
RUN adduser -u 1000 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser
# Build Image
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
# clang zlib1g-dev werden für AOT benötigt
RUN apt update && apt upgrade -y && apt install -y clang zlib1g-dev
WORKDIR /build
COPY ./Projektmappe.sln ./
COPY ./src ./src
RUN dotnet publish ./src/Projekt/Projekt.csproj -c Release -r linux-x64 -o /app
FROM prepare AS finalalpine
# Labels.
LABEL maintainer=""
LABEL org.label-schema.schema-version="1.0"
LABEL org.label-schema.name=""
LABEL org.label-schema.description=""
LABEL org.label-schema.version="1.0"
COPY --chown=appuser --from=build /app ./
ENTRYPOINT ["./Programm"]
Bei der Verwendung eines privaten Nuget-Repositorys müssen Sie Anmeldedaten angeben. Die genaue Vorgehensweise wird auf der entsprechenden GitHub-Seite erläutert.