Best Practices für die Containerisierung von .NET-Anwendungen
Marcelo Oliveira
31. August 2022
0 Min. LesezeitDie Containerisierung mit Docker hat sich zu einem wichtigen Trend in der Webentwicklung entwickelt, den viele .NET-Entwickler übernommen haben. Es gibt viele überzeugende Gründe für Entwickler und DevOps-Engineers, .NET-Anwendungen zu containerisieren – auch bei der Arbeit mit den älteren Versionen des .NET Framework 4.x. Wenn wir jedoch nicht wissen, wie wir Container richtig einsetzen, profitieren wir nur wenig davon.
In diesem Artikel behandeln wir Best Practices für die Containerisierung von .NET-Anwendungen – auch für Anwendungen mit dem .NET Framework 4.x. Außerdem geht es darum, kleine Images zu verwenden und Images zu scannen, um Sicherheitsrisiken zu verringern und unnötige Komponenten aus unseren Containern zu entfernen.
Voraussetzungen
Bevor Sie beginnen, benötigen Sie Folgendes:
Laden Sie Visual Studio 2022 herunter und installieren Sie es.
Sie benötigen einen lokalen Docker-Client, um Container lokal auszuführen. Sie können Docker Desktop verwenden. Dafür ist Windows 10 oder höher erforderlich. Achten Sie darauf, die Windows-Version zu wählen.
Laden Sie die Snyk Command Line Interface (CLI) herunter und installieren Sie sie.
Registrieren Sie sich für ein kostenloses Snyk-Konto.
Ziehen Sie die Containerisierung Ihrer .NET-Apps in Betracht (auch mit .NET Framework 4.x)
Mit .NET Core 1.0 wurden Web-Apps plattformübergreifend, portabel und Open Source. Dadurch konnten Entwickler sie auf Docker portieren und die Vorteile der Containerisierung voll ausschöpfen.
Neuere .NET-Versionen unter Linux erhalten zwar die meiste Aufmerksamkeit, doch Windows-Container unterstützen auch die älteren, ausschließlich für Windows verfügbaren .NET-Versionen, auf die unzählige Unternehmensanwendungen angewiesen sind. Außerdem sind nicht alle Unternehmen, die Web-Apps mit dem .NET Framework warten, bereit, sie auf neuere Versionen zu portieren. Deshalb ist es sinnvoll, unsere .NET Framework 4.x-Anwendungen zu containerisieren.
Die folgenden Schritte zeigen, wie Sie eine containerisierte ASP.NET-Web-Forms-Anwendung in Visual Studio 2022 erstellen und ausführen.
Starten Sie zunächst Docker Desktop auf Ihrem Entwicklungscomputer:

Wählen Sie im Fenster Erste Schritte von Visual Studio die Option Neues Projekt erstellen aus.

Geben Sie „Web Forms“ in das Textfeld Nach Vorlagen suchen ein und wählen Sie ASP.NET-Webanwendung (.NET Framework) aus.

Geben Sie einen Namen für Ihre neue Anwendung ein (zum Beispiel WebFormsApp), legen Sie den Speicherort auf dem Datenträger fest, wählen Sie .NET Framework 4.8 aus und klicken Sie auf Erstellen.

Wählen Sie anschließend den Typ Web Forms aus.

Vergewissern Sie sich, dass die Optionen Web Forms und Docker-Unterstützung ausgewählt sind, und klicken Sie dann auf Erstellen.

Warten Sie, bis Visual Studio das Projekt erstellt hat. So sieht die Projektstruktur in Visual Studio aus:

Öffnen Sie nun Docker Desktop. Sie werden sehen, dass der Container WebFormsApp auf Port 61469 ausgeführt wird:

Öffnen Sie die Registerkarte Images, um das Image WebFormsApp zu sehen, aus dem der Container WebFormsApp ausgeführt wird.

Drücken Sie abschließend Ctrl+F5, um Ihr Docker-Image zu erstellen und lokal auszuführen. Sobald das Container-Image erstellt wurde und in einem Docker-Container läuft, öffnet Visual Studio die Web-App in Ihrem Standardbrowser.

Kehren Sie nun zu Visual Studio zurück und klicken Sie auf die Datei Dockerfile:

Dadurch wird die Dockerfile geöffnet, mit der Ihr Projekt für die Ausführung in einem Container konfiguriert wird.

Die Anweisung FROM startet eine neue Build-Phase und legt das Basis-Image für die folgenden Anweisungen fest. In diesem Fall ist das Image aspnet:4.8-windowsservercore-ltsc2019, das Docker aus dem Image-Repository von Microsoft herunterlädt.
Natürlich wurde Ihr Web-Forms-Projekt wahrscheinlich nicht von Anfang an mit Docker-Unterstützung erstellt. Um genau diese Situation nachzustellen, erstellen Sie ein neues ASP.NET-Web-Forms-Projekt, diesmal jedoch ohne Docker-Unterstützung:

Fügen wir dem neuen Projekt nun eine Dockerfile hinzu. Klicken Sie mit der rechten Maustaste auf den Projektnamen, wählen Sie das Menü Hinzufügen und dann das Untermenü Docker-Unterstützung aus:

Visual Studio erstellt die Dockerfile des Projekts daraufhin automatisch.

Beachten Sie, dass Visual Studio das Image und den Quellcodepfad, aus denen der Container erstellt wird, bereits ausgewählt hat.

Ihr Projekt verfügt nun über die Unterstützung und Vorteile, die mit der Containerisierung durch Docker einhergehen – darunter Portabilität, Agilität, schnellere Bereitstellung, höhere Sicherheit und einfachere Verwaltung.
Halten Sie Container-Images so klein wie möglich
Sowohl Linux- als auch Windows-Container bieten schlanke Basis-Images, die nur die Apps und Dienste enthalten, die für den Betrieb Ihrer Anwendung erforderlich sind. Ein kleines Image mit möglichst wenigen Abhängigkeiten verringert die Anzahl der über diese Abhängigkeiten eingebrachten Schwachstellen und damit die Angriffsfläche.
Sehen wir uns einige Basis-Images an, die .NET-Entwickler für kleine Container in Betracht ziehen können. Wenn Sie noch ASP.NET Framework 4.x verwenden, das unter Linux nicht unterstützt wird, sind Windows-Container Ihre einzige Option. Microsoft empfiehlt als schlanke Basis-Images Windows Server Core und Nano Server.
Windows Server Core ist ein minimales, befehlszeilenbasiertes Betriebssystem zum Ausführen von Windows-Serverrollen und Anwendungen, die keine standardmäßige grafische Windows-Benutzeroberfläche benötigen. Nano Server ist ein Betriebssystem, das für Private Clouds und Rechenzentren optimiert ist. Nano Server bietet keine lokale Anmeldefunktion, ist kleiner und startet deutlich schneller neu als Server Core.
Für .NET Core und .NET 5+ sollten SDK-Images nur für den Build und Runtime-Images für Produktionsbereitstellungen im Rahmen eines mehrstufigen Builds verwendet werden. Zu den geeigneten schlanken Basis-Images gehören Debian bullseye-slim-amd64 und Alpine Linux.
Wir können Basis-Images noch weiter verkleinern, indem wir Images für die .NET-Runtime-Abhängigkeiten verwenden und unsere .NET-Anwendung im eigenständigen Modus erstellen. So laden wir nur die Abhängigkeiten herunter, die unsere App benötigt – und nichts darüber hinaus.
Üben wir nun, eine .NET-6-Webanwendung mit schlanken Images zu erstellen. Öffnen Sie Visual Studio 2022, klicken Sie auf Neues Projekt erstellen und wählen Sie den Typ ASP.NET Core-Web-App aus.

Geben Sie dem Projekt einen Namen, zum Beispiel WebApp.

Klicken Sie auf Weiter und wählen Sie die folgenden Optionen aus:
.NET 6.0 Framework
Authentifizierungstyp: Keine
Für HTTPS konfigurieren: Aktiviert
Docker aktivieren: Aktiviert
Docker-Betriebssystem: Linux

Klicken Sie auf Erstellen und sehen Sie sich das neue Projekt in Visual Studio an.

Drücken Sie nun F5, um die Web-App in Ihrem lokalen Docker-Container auszuführen.

Führen Sie nun den Befehl docker images aus, um eine Liste der Docker-Images auf Ihrem System abzurufen:
Der Befehl gibt ungefähr Folgendes aus:
Beachten Sie, dass die App in einem Linux-Container ausgeführt wird und das webapp-Image oben 208 MB groß ist.
Verkleinern wir als Nächstes das Container-Image unserer Web-App.
Beenden Sie die App in Visual Studio und öffnen Sie die Datei Dockerfile. Im folgenden Abschnitt wird Docker angewiesen, die Images dotnet/aspnet:6.0 und dotnet/sdk:6.0 aus der Microsoft Container Registry (mcr) herunterzuladen und als Basis-Images für Ihren App-Container zu verwenden:
Drücken Sie anschließend F5, um die Anwendung erneut auszuführen. Sobald die Web-App läuft, führen Sie den Befehl docker images aus, um eine Liste der Docker-Images auf Ihrem System abzurufen:
Ihre Image-Liste sieht etwa so aus:
Beachten Sie, dass das webapp-Image neu erstellt wurde und nun 100 MB groß ist (108 MB kleiner als das ursprüngliche Image).
Images auf Schwachstellen scannen
Die Codebasis regelmäßig zu aktualisieren, ist an sich schon eine Best Practice. Zu den offensichtlichen Vorteilen zählen der Zugriff auf neue Funktionen, Fehlerkorrekturen, eine bessere Benutzeroberfläche und eine höhere Leistung. Sicherheitsupdates sind jedoch genauso wichtig.
Das Betriebssystem oder die Bibliotheken im Basis-Image können ungepatchte Schwachstellen enthalten. Auch Skripte, Code und Apps, die dem Basis-Image hinzugefügt werden, können Schwachstellen aufweisen, die wir nicht versehentlich in der Produktionsumgebung bereitstellen möchten.
Das Scannen von Containern auf Schwachstellen ist unverzichtbar, um potenzielle Sicherheitsrisiken zu bewerten und geeignete Maßnahmen zu ergreifen. In diesem Beispiel wählen wir ein veraltetes .NET-Image aus: mcr.microsoft.com/dotnet/core/aspnet:2.2.
Öffnen Sie das Projekt in Visual Studio und fügen Sie dem neuen Projekt eine Dockerfile hinzu. Klicken Sie mit der rechten Maustaste auf den Projektnamen, wählen Sie das Menü Hinzufügen und klicken Sie dann auf das Untermenü Docker-Unterstützung.
Öffnen Sie nun die Datei Dockerfile und ersetzen Sie ihren Inhalt durch den folgenden Code:
Als Nächstes erfahren Sie, wie Sie Image-Scans einsetzen, um herauszufinden, wie anfällig unser Container ist. Führen Sie zunächst den CLI-Befehl docker images aus, um Ihre aktuellen lokalen Images aufzulisten:
Führen Sie anschließend den Befehl snyk auth aus, um sich für die Snyk CLI zu authentifizieren:
Sobald Ihr Konto authentifiziert ist, können Sie die Snyk CLI verwenden. Führen wir den Befehl snyk container test <repository>:<tag> aus, um das lokal erstellte Image zu testen, das in unserem lokalen Docker-Daemon verfügbar ist:

Der Befehl snyk container test erstellt eine Liste der im Image installierten Komponenten, sendet diese Liste an den Snyk-Service und gibt eine Liste der Schwachstellen in unserem Image zurück.
Der Image-Scan hat insgesamt 193 Schwachstellen gefunden:

Führen Sie den Befehl snyk container monitor <repository>:<tag> aus, um das Image zu überwachen:

Der Befehl snyk container monitor erstellt eine Liste der im Image installierten Komponenten, sendet diese Liste an den Snyk-Service und gibt einen Link zum Snyk-Service unter http://app.snyk.io/org/{YOUR-ACCOUNT}/ zurück, wo Sie die Ergebnisse sehen können:

Die 3 wichtigsten Tipps zur Containerisierung
Wir haben viele überzeugende Gründe gesehen, warum Entwickler und DevOps-Engineers .NET-Anwendungen containerisieren sollten. Container sind jedoch keine magische Lösung für alle Probleme. Wir müssen weiterhin einige Best Practices umsetzen, die Klarheit über Container schaffen und zeigen, wie wir das Beste aus ihnen herausholen.
Entgegen dem weit verbreiteten Irrglauben, dass Container nur für .NET-Core-Apps gedacht sind, haben wir gesehen, dass Container auch .NET Framework 4.x-Anwendungen Vorteile bieten.
Bei der Größe von Container-Images gilt: Je kleiner, desto besser. Wie wir gesehen haben, sind einige Basis-Images klein genug, um nur das zu enthalten, was unsere App zum Ausführen benötigt. So wird auch die Angriffsfläche des Containers verkleinert.
Abschließend haben wir gesehen, wie sich Sicherheitsrisiken durch das Scannen von Container-Images und deren Aktualisierung verringern lassen. Die Befehle Snyk container test und Snyk container monitor sind wertvolle Tools, mit denen wir unsere .NET-Container auf Schwachstellen scannen und kontinuierlich überwachen können. Bei richtiger Anwendung kann die Containerisierung unseren Apps unabhängig von ihrem Alter zusätzliche Sicherheit und Flexibilität bieten.
Container-Sicherheit mit Fokus auf Entwickler
Snyk findet und behebt automatisch Schwachstellen in Container-Images und Kubernetes-Workloads.
