.NET 6 und Kubernetes Migration

Rene Adomeit und Hauke Richter  /  20.06.22  /  Managed Services

 

Bei unseren Kunden haben wir schon seit vielen Jahren verschiedene ASP.NET-Web-API-Anwendungen im Einsatz. Diese werden als HTTP APIs von verschiedenen Browser Frontends, insbesondere Angular, im Rahmen von Microservice-Architekturen verwendet. Historisch bedingt laufen diese auf Windows Servern unter dem klassischen .NET Framework.

Microsoft versorgt .NET Framework zwar weiter auf unbestimmte Zeit mit Sicherheitsupdates, fügt aber keinerlei neue Funktionen und Verbesserungen mehr seit 2020 hinzu. Somit ist ein Upgrade auf .NET 6 (zwischenzeitlich .NET Core genannt) zwar nicht zwingend erforderlich, macht aber aus folgenden Gründen durchaus Sinn:

  • Plattformunabhängigkeit: .NET 6-Anwendungen sind unter Linux lauffähig
  • Innovationen und Verbesserungen: neue Sprachversionen, APIs und 3rd Party Libraries auf aktuellem Stand
  • Optimierung von Performance und Speicherverbrauch

Microsoft bietet verschiedene Tools an, die bei der Migration unterstützen:

Ablauf der .NET 6-Migration

Zuerst muss das .NET 6 SDK installiert werden:
https://dotnet.microsoft.com/en-us/download/dotnet/6.0

Dann kann der .NET Upgrade Assistent ausgeführt werden. Der nun folgende Migrationsaufwand  hängt dabei von der Größe und dem Aufbau der bisherigen Anwendung ab.

Wenn nicht mehr unterstützte .NET Framework Funktionen/APIs genutzt werden, so sind deren Aufrufe auszutauschen. Eventuell stehen aber Shims wie das Microsoft Compatibility Pack https://www.nuget.org/packages/Microsoft.Windows.Compatibility zur Verfügung.

ASP.NET Controller, Filter, Attribute, AppSettings, Publish Profile, der gesamte Startup-Code und einiges mehr muss händisch migriert werden. Hier entsteht ein nicht unerheblicher Aufwand. Unter anderem kann dabei dieser Migration Guide helfen:
https://docs.microsoft.com/de-de/aspnet/core/migration/webapi?view=aspnetcore-6.0 .

Entity Framework 6.4 (der O/R Mapper von Microsoft) ist mit .NET 6 kompatibel. Es ist also keine zusätzliche Migration zum neuen Entity Framework Core notwendig. Dies kann aber sinnvoll sein, wenn von den neuen Funktionen und Performanceoptimierungen profitiert werden soll. Der Migrationsaufwand zu EF 6.4 ist daher gering. Nutzt die Anwendung jedoch eine ältere Entity Framework Version, muss der entsprechende Code zuerst aktualisiert werden. Das kann wiederum sehr aufwendig werden.

JSON.NET kann weiterverwendet werden und hat mehr Funktionen als der Microsoft Serializer, ist aber etwas langsamer.

CORS muss neu eingerichtet werden:
https://docs.microsoft.com/de-de/aspnet/core/security/cors?view=aspnetcore-6.0

Falls die Anwendung SignalR nutzt, ist der Migrationsaufwand höher, da sich .NET 6 erheblich von der klassischen ASP.NET / .NET Framework - Variante unterscheidet. Auch die SignalR Client Libraries müssen aktualisiert werden. Einige Unterschiede sind hier aufgelistet:
https://docs.microsoft.com/de-de/aspnet/core/signalr/version-differences?view=aspnetcore-6.0

Andere Libraries wie z.B. Log4Net, Sentry for ASP.NET (Error-Reporting) und Swagger lassen sich relativ unkompliziert upgraden und man profitiert natürlich auch hier von neuen Funktionen der Libraries.

3rd-Party-Libraries, die nicht .NET 6 kompatibel sind, müssen ausgetauscht werden. In unserem Fall war das z.B. der DI Container Castle Windsor, der zum Zeitpunkt der Migration .NET 6 nicht unterstützt hatte. Daher haben wir ihn durch AutoFac ersetzt.

Wenn ein Wechsel vom Windows IIS Webserver hin zu Docker und dem Kestrel Webserver geplant ist, muss ein entsprechendes Dockerfile angelegt werden. Andernfalls muss die IIS-Installation und -Konfiguration angepasst werden. Siehe hierzu: https://docs.microsoft.com/de-de/aspnet/core/host-and-deploy/iis/?view=aspnetcore-6.0

Ablauf der Kubernetes-Migration

Dank der Aktualisierung auf .NET 6 sind die Anwendungen nun in Linux-Docker-Containern und damit auch im Kubernetes Cluster lauffähig.

Für das Deployment einer Anwendung in den Kubernetes Cluster haben wir folgende Voraussetzungen geschaffen, die in den nachfolgenden Abschnitten beschrieben werden:

  • Ein Namespace im Kubernetes Cluster
  • Ein Dockerfile für die Anwendung
  • Einen Helm Charts Ordner (für ein vereinfachtes Deployment im Kubernetes Cluster)
  • Einstellungen im Gitlab
  • Eine .gitlab-ci.yml Datei (wir benutzen Gitlab als CI/CD-Umgebung)

Namespace

Zur Separierung verschiedener Anwendungen werden in Kubernetes Namespaces verwendet.
Beispielsweise kann ein Namespace „consist-blog-development“ angelegt werden, um dort die Entwicklungsumgebung zu deployen.

Für weitere Umgebungen kann man jeweils einen eigenen Namespace anlegen oder mehrere Umgebungen in den gleichen Namespace deployen. Das ist Geschmackssache.

Dockerfile

Mithilfe des Dockerfiles wird ein Docker Image gebaut, das in einem sogenannten Pod in Kubernetes gestartet werden kann. Für den Kubernetes Cluster muss das Dockerfile nicht noch speziell angepasst werden.

Hier ist ein Beispiel für ein vollständiges Dockerfile, um eine ASP.NET Core 6 Web API in einem Docker Container zu starten.

beispiel dockerfile

Helm Charts

Helm Charts helfen beim Installieren und Updaten der Anwendung im Kubernetes Cluster.

Ein Helm Chart ist eine Ordnerstruktur mit YAML Templates für die verschiedenen Kubernetes-Ressourcen. Die YAML Templates enthalten Variablen, die man nach Wunsch in eine eigene Datei auslagern kann, die für jede Anwendungsumgebung unterschiedlich ist.

Ein Vorteil ist, dass der Helm-Charts-Ordner in Gitlab eingecheckt wird, sodass die Einstellungen nicht verloren gehen.

Die Anwendung lässt sich außerdem über einen einzigen Helm-Befehl im Cluster deployen. So müssen keine manuellen Einstellungen im Cluster vorgenommen werden.

Die Templates im Helm Chart enthalten Konfigurationen wie das Prüfen der Erreichbarkeit der Anwendung oder die URL, mit der die Anwendung aufgerufen wird.

Einstellungen im Gitlab

Bei uns wird Gitlab als Container Registry benutzt. Bei jedem neuen Commit wird ein Image für alle Umgebungen im Format [Umgebung]-[CommitHash] gebaut.

Es ist nötig, dass Gitlab mit dem Kubernetes Cluster verbunden wird, damit der Cluster die Docker Images der Anwendung herunterladen kann, um daraus Pods zu bauen. Die Einstellung befindet sich unter Infrastructure → Kubernetes clusters.

Außerdem muss die Einstellung „Container registry“ eingeschaltet werden, damit der Kubernetes Cluster auf die Images zugreifen kann.

Gitlab-CI/CD-Konfigurationsdatei .gitlab-ci.yml

Über diesen Helm-Befehl in der .gitlab-ci.yml wird die Anwendung mit den Einstellungen der Entwicklungsumgebung vom Helm Chart in den Kubernetes Cluster deployt.

Helm-Befehl

Die Datei „dev_values.yaml“ befindet sich ebenfalls im Helm Charts Verzeichnis. Dort sind umgebungsspezifische Konfigurationen abgelegt.

Um eine neue Umgebung zu erzeugen kann man beispielsweise eine „prod_values.yaml“ anlegen und ändert die Werte so, dass sie für die Produktionsumgebung passen und schon hat man eine neue Umgebung erzeugt.

Fazit

Wenn eine Anwendung noch ein paar Jahre laufen soll und weiterhin regelmäßige Änderungen oder Erweiterungen erhalten wird, empfiehlt sich in den meisten Fällen ein .NET 6 Upgrade. Blocker wie fehlende .NET APIs oder 3rd Party Libraries in Uralt-Versionen, die erst aufwendig aktualisiert oder ersetzt werden müssen, können aber gegen ein Upgrade sprechen.

Dank einer CI/CD-Umgebung wie Gitlab werden die Anwendungen per Knopfdruck voll automatisiert deployt.

Der Kubernetes Cluster stellt dabei die Laufzeitumgebung bereit und sorgt unter anderem für die automatische Skalierung. Außerdem stellt der Cluster sicher, dass immer die gewünschte Anzahl an Instanzen läuft. Er bietet zudem verschiedene Deployment-Strategien an und übernimmt den automatischen Neustart fehlerhafter Instanzen.