Docker-Grundlagen für .NET-Entwickler

Docker: Einstieg in die Welt der Container
Keine Kommentare

Container und Docker sind in aller Munde. Doch die Einstiegshürde in die Docker-Welt ist nicht ganz niedrig. Was ist Docker genau, wie funktioniert es, welche Probleme löst es eigentlich und wie kann ich es als .NET-Entwickler heute schon einsetzen? All diesen Fragen geht dieser Artikel nach, sodass Sie als .NET-Entwickler das nötige Grundlagen-Wissen haben und mit Docker richtig durchstarten können.

In den vergangen Jahren ist die Beliebtheit von Docker stetig gestiegen. Applikationen werden in einem Container ausgeführt und können somit auf beliebigen Umgebungen „angedockt“ werden. Das klingt ja ganz spannend. Aber warum sollte das einen .NET Entwickler interessieren, ist das denn überhaupt wichtig für den Entwicklungsalltag? Wieso ist Docker auch für uns eine solche Revolution?

Warum Docker?

Bei Docker geht es primär um das Verteilen von Anwendungen und Diensten, das sogenannte Deployment. Doch wie wurde das eigentlich früher gemacht? Nehmen wir an, ein Entwickler will seine frisch erstellte .NET-Webapplikation einer Kollegin zum lokalen Testen geben. Ganz früher war das noch so, dass die Kollegin zur Installation eine Anleitung mit Voraussetzungen und ggfs. auch manuelle Skripts bekam. Da stand dann z. B. in der Anleitung, dass Windows als Betriebssystem benötigt wird, dass das .NET Framework in einer bestimmten Version installiert sein muss, dass zur Ausführung eine bestimmte Datenbank benötigt wird usw. Es wird also für die Kollegin ein mühsames und aufwändiges Unterfangen, die Webapplikation lokal zum Laufen zu bekommen.

Im Laufe der Jahre hat sich das Ganze vereinfacht, indem man mit virtuellen Maschinen gearbeitet hat – ja, viele Entwickler machen das natürlich auch heute noch. In der virtuellen Maschine wird die .NET-Webapplikation mit allen Abhängigkeiten installiert. Jetzt kann der Entwickler seiner Kollegin einfach die virtuelle Maschine geben, die Kollegin startet sie und hat somit eine lauffähige Applikation, die sie jetzt innerhalb der VM testen kann – ein deutlicher Fortschritt. Doch auch hier gibt es noch einen kleinen Haken: Die eigentliche Anwendung ist in der Praxis meist nur ein paar Megabytes groß, wenn überhaupt. Zur Größe der Anwendung kommen noch ein paar Abhängigkeiten, wie ein Webserver und eine Datenbank. Doch die virtuelle Maschine selbst beinhaltet ja noch das Gastbetriebssystem, und das nimmt üblicherweise gleich mal einige Gigabytes in Anspruch. Dass man der Kollegin die beispielsweise 100 GB große virtuelle Maschine für eine Anwendung geben muss, die mit ihren Abhängigkeiten nur einen Bruchteil dieser Größe hat, ist natürlich nicht ideal. Geht man sogar noch davon aus, dass man verschiedene Applikationen mit unterschiedlichen Abhängigkeiten hat, dann braucht es vielleicht sogar mehrere virtuelle Maschinen, um für jede Applikation eine zu haben. Wer kennt es nicht, dass die Festplattengröße des Entwicklungsrechners da oft ein Problem darstellt. Neben dem Datenvolumen einer VM ist auch das Startverhalten ein Kritikpunkt. Es dauert meist eine kleine Weile, bis das Betriebssystem hochgefahren ist. Doch warum brauche ich eigentlich überhaupt ein zusätzliches Betriebssystem, wenn die Kollegin auf ihrem Rechner doch schon eins hat? Das ist eine sehr gute Frage, und das ist der Punkt, an dem Docker ins Spiel kommt.

Über Images und Container

Mit Docker lassen sich alle Abhängigkeiten einer Anwendung in einem sogenannten Docker Image abbilden. Aus einem Image lässt sich dann eine Instanz erzeugen, die als Container bezeichnet wird. Um eine Applikation mit allen Abhängigkeiten an eine Kollegin zu geben, wird ein Docker Image bereitgestellt. Die Kollegin kann das Docker Image dann in Form eines Containers lokal auf ihrem Rechner ausführen. Doch was ist jetzt genau der Unterschied zwischen einem laufenden Docker-Container und einer laufenden virtuellen Maschine? Der große Unterschied ist, dass der Docker-Container im Gegensatz zur virtuellen Maschine kein eigenes Betriebssystem hat. Anstelle eines Gastsystems wie bei einer virtuellen Maschine, wird bei einem Docker-Container direkt das Betriebssystem des Hosts genutzt, das sogenannte Host-OS. Abbildung 1 verdeutlicht den Unterschied zwischen einer virtuellen Maschine und einem Docker-Container.

Abb. 1: Virtuelle Maschine vs. Docker Container

Abb. 1: Virtuelle Maschine vs. Docker Container

Wie Abbildung 1 zeigt, benötigt eine virtuelle Maschine den sogenannten Hypervisor, der die Ressourcen des Hosts bereitstellt. Auch ein Docker-Container benötigt etwas ähnliches, die sogenannte Docker Engine. Die Docker Engine stellt den Zugriff auf den Kernel des Host-Betriebssystems sicher und ist in der Lage, Container zu erstellen, zu starten und zu stoppen. Aufgrund der Tatsache, dass ein Docker Image und somit auch ein daraus erstellter Docker-Container kein eigenes Betriebssystem mit sich bringt, sondern auf dem Host-Betriebssystem aufgesetzt wird, ist ein Docker Image sehr viel kleiner als eine virtuelle Maschine. Neben der Größe ist das schnelle Starten eines Docker-Containers ein weiterer großer Vorteil gegenüber einer virtuellen Maschine. Beim Starten eines Containers muss nicht ein ganzes Betriebssystem hochgefahren werden. Somit geht das Starten und Stoppen eines Containers ungewohnt schnell, es gleicht dem Starten und Stoppen eines Prozesses.

Um also Docker-Container ausführen zu können, wird die Docker Engine benötigt. Auf jedem Rechner, auf dem eine Docker Engine installiert ist, lassen sich Docker-Container ausführen. Es ist also an der Zeit, die Docker Engine auch auf dem eigenen Entwicklungsrechner zu installieren.

Die Docker Engine installieren

Auf der Docker-Homepage lässt sich die Docker Community Edition (CE) herunterladen. Dabei gibt es verschiedenste Versionen für unterschiedliche Betriebssysteme wie Mac OS, Windows und Linux. In diesem Artikel wird Windows als Betriebssystem genutzt, somit wird die Docker Community Edition für Windows heruntergeladen. Der Download erfordert das Erstellen eines kostenlosen Docker-Accounts. Ein solcher Account wird als Docker ID bezeichnet. Ist die ca. 380 MB große Installationsdatei für die Docker Community Edition heruntergeladen, kann die Installation losgehen. Bei der Installation kann man nicht viel falsch machen. Es lässt sich klassisch auswählen, ob eine Desktopverknüpfung erstellt werden soll Daneben ist die einzig weitere Auswahlmöglichkeit bei der Docker-Installation eine Checkbox mit dem Titel
Use Windows Containers instead of Linux Containers. Die Checkbox ist standardmäßig nicht selektiert, womit Linux-Container verwendet werden. Dieser Wert lässt sich auch nach der Installation noch ändern. Also einfach OK klicken und Docker wird installiert.

Die Version prüfen

Nach der Installation ist Docker auf der Kommandozeile verfügbar und es kann losgehen. Einfach ein Powershell-Fenster öffnen und folgenden Befehl eingeben: docker version.

Dieser Befehl zeigt die installierte Docker-Version an. Dabei werden eine Client- und eine Serverversion angezeigt (Abb. 2). Die Clientversion beschreibt die Version der Kommandozeile. Wird an der Kommandozeile lediglich docker eingegeben, werden die verfügbaren Befehle angezeigt. Die Serverversion beschreibt die Version der installierten Docker Engine, die zum Ausführen von Containern benötigt wird. Wie in Abbildung 2 zu sehen ist, läuft die Docker Engine auf Linux, das als Operating System (OS) angegeben ist.

Abb. 2: Die installierte Docker-Version

Abb. 2: Die installierte Docker-Version

Beim Installieren von Docker stand ja die Option Use Windows Containers Instead Of Linux Containers zur Verfügung. Docker wurde ursprünglich auf Linux aufbauend eingeführt, Windows-Container kamen erst später dazu. Da mit der Defaultinstallation Linux-Container verwendet werden bedeutet das, dass Images und daraus instanziierte Container Linux-basiert sind und somit den Linux-Kernel des Hostbetriebssystems verwenden. Doch wo ist dieser Linux-Kernel unter Windows?

Im Hintergrund hat die Docker-Installation eine virtuelle Maschine mit Linux installiert, damit sich auf einen Windows-Betriebssystem auch Linux-basierte Container starten lassen. Wird unter Windows der Hyper-V-Manager geöffnet, ist die von Docker installierte und genutzte Linux VM zu sehen, sie trägt den Namen MobyLinuxVM.

Im Icon Tray von Windows – das ist der Bereich rechts unten in der Taskbar links von der Uhr – ist nach einer erfolgreichen Installation und einem erfolgreichen Start der Docker Engine ein Docker-Icon zu sehen. Es repräsentiert die installierte Docker Engine und hat die Form des klassischen Walfischs mit Containern an Bord. Ein Rechtsklick auf dieses Icon zeigt ein Kontextmenü, das unter anderem einen Eintrag enthält, um von Linux-Containern auf Windows-Container umzusteigen.

Doch Linux-Container sind die übliche Variante. Ein Linux-Container kann zwar kein .NET Framework beinalten, da dieses nur unter Windows läuft. Aber .NET Core ist ein ideales Mittel, um .NET-Applikationen auch in einem Linux-Container auszuführen, da .NET Core ja eine modulare Cross-Platform-Implementierung von .NET ist, die unter Windows, Mac OS und Linux läuft. Jetzt dürfte auch klar sein, wieso die Cross-Platform-Fähigkeit von .NET Core so wichtig ist. Sie macht .NET Core zu einem wichtigen Framework für containerbasierte Applikationen, unabhängig von der Tatsache, ob es sich dabei um Linux- oder Windows-basierte Container handelt.

Bevor in diesem Artikel .NET Core zum Einsatz kommt, geht es im nächsten Abschnitt noch mit ein paar Grundlagen weiter. Es wird das erste Image geladen und davon ein Container instanziiert und gestartet.

Mit Docker loslegen

Mit dem Befehl docker info lassen sich die Anzahl installierter Images und instanziierter Container anzeigen. Natürlich gibt es nach der Installation von Docker noch keine Images und keine Container. Mit dem Befehl docker run hello-world wird das hello-world Image heruntergeladen und anschließend ein Container erzeugt und ausgeführt. Das hello-world Image enthält ein Programm, das auf der Konsole den Text „Hello World“ ausgibt. Das Programm beschreibt auch genau, was mit dem Befehl docker run hello-world alles ausgelöst wird:

  1. Der Docker Client kontaktiert die Docker Engine, auch als Docker Daemon bezeichnet.
  2. Die Docker Engine stellt fest, dass das hello-world Image lokal nicht verfügbar ist. Somit schaut sie auf dem Docker Hub nach diesem Image und lädt es von dort herunter.
  3. Die Docker Engine erstellt einen Container basierend auf dem heruntergeladenen Image. Der Container enthält die Anwendung und führt sie aus. Die Ausgabe „Hello World“ wird produziert.
  4. Die Docker Engine streamt die Ausgabe „Hello World“ an den Docker-Client, der die Ausgabe auf der Konsole anzeigt.

Wird der Befehl docker run hello-world nochmals ausgeführt, wird das hello-world Image lokal gefunden und anschließend die Schritte 3 und 4 ausgeführt. Das reine Herunterladen eines Images vom Docker Hub kann auch ohne das Ausführen eines Containers mit folgendem Befehl stattfinden: docker pull hello-world.

Die heruntergeladenen und lokal verfügbaren Images lassen sich mit dem Befehl docker images an der Konsole anzeigen. Nach mehrmaligem Ausführen des Befehls docker run hello-world lohnt sich ein Blick auf die erstellten Container. Einen groben Überblick über die Anzahl lokaler Images und Container gibt es mit dem Befehl docker info. Die laufenden Container lassen sich mit dem Befehl docker ps anzeigen. Doch Vorsicht: Das hello-world Image enthält eine Anwendung, die nicht dauerhaft läuft, wie das beispielsweise bei einem Webserver oder einer Datenbank der Fall ist. Die Anwendung gibt „Hello World“ aus und wird danach beendet. Somit befindet sich auch der Container nach dem Ausführen im Zustand Stopped. Weitere mögliche Zustände eines Containers sind Paused und Running. Mit dem Befehl docker ps lassen sich laufende Container anzeigen, also jene im Zustand Running. Um auch pausierte oder gestoppte Container anzuzeigen, muss der -a Parameter wie folgt mitgegeben werden: docker ps -a.

Der Befehl gibt auf der Konsole alle erstellten Container aus, unabhängig vom jeweiligen Zustand. Jetzt wird sichtbar, dass jeder Aufruf von docker run hello-world einen neuen Container erstellt (Abb. 3).

Abb. 3: Erstellte Container anzeigen

Abb. 3: Erstellte Container anzeigen

Wie Abbildung 3 zeigt, hat jeder Container eine Container-ID. Damit lässt sich der Container ansprechen und mit Befehlen wie docker start oder docker stop starten oder stoppen. Dabei muss nicht die komplette ID eingegeben werden. Die ersten Zeichen genügen, solange diese den Container eindeutig unter den verfügbaren lokalen Containern identifizieren. Folgender Befehl startet den Container mit der mit aa01 beginnenden Container-ID erneut. Der -a Parameter gibt an, dass die Ausgaben an die Konsole geleitet werden sollen, somit wird erneut der Text „Hello World“ ausgeben: docker start aa01 -a.

Ein Container lässt sich mit dem Befehl docker rm gefolgt von der Container-ID auch entfernen: docker rm aa01. Werden die Container erneut mit dem Befehl docker ps -a gelistet, taucht der soeben entfernte Container nicht mehr auf, er ist gelöscht. Auch Images lassen sich wieder entfernen. Werden Images mit dem Befehl docker images angezeigt, so haben sie analog zu Containern auch eine ID. Somit lässt sich ein Image, dessen ID mit e38b beginnt, wie folgt löschen: docker rmi e38b.

Wie dieser Abschnitt gezeigt hat, bleiben erstellte Container lokal erhalten und werden beim Beenden der Anwendung nicht einfach entfernt. Falls ein erstellter Container nach dem Beenden automatisch wieder entfernt werden soll, kann der Container des hello-world Images auch wie folgt mit dem –rm Parameter erstellt und ausgeführt werden:

docker run --rm hello-world

Jetzt wird der Container erstellt, ausgeführt und anschließend wieder entfernt. Somit gibt es keine lokalen Containerleichen.

Docker Hub

Im vorigen Abschnitt wurde das hello-world Image vom Docker Hub heruntergeladen. Ein Blick auf das Portal lohnt sich. Neben dem hello-world Image gibt es dort zahlreiche andere Images. So gibt es beispielsweise ein Image mit Ubuntu, eines mit Node.js, eines mit MySQL usw. Auch eine Suche nach Organisationen wie Oracle oder Microsoft lohnt sich. Die Images von Microsoft sind alle hier einsehbar. Da gibt es bspw. ein Image mit SQL Server auf Linux (microsoft/mssql-server-linux), mit dem sich ein Linux-Container erstellen lässt, in dem ein SQL Server läuft. Das bedeutet, dass Docker nicht nur spannend für das Deployment ist, sondern eben auch enorm hilfreich, um lokal die eigene Entwicklungs- und Testumgebung aufzusetzen. Anstatt klassisch einen SQL-Server auf dem lokalen Rechner zu installieren, lässt sich eine SQL-Server-Instanz in einem Docker-Container betreiben. Dieser Docker-Container kann dann nach Belieben gestoppt oder auch entfernt werden. Es lassen sich somit verschiedenste Versionen ohne Probleme parallel betreiben, da einzelne Docker-Container isoliert sind.

Neben dem SQL Server Image hat Microsoft auch zahlreiche andere Images, darunter das beliebte Image microsoft/dotnet. Es ist das offizielle Image für .NET Core und ASP.NET Core, sowohl für Linux als auch für Windows. Mit folgendem Befehl lässt sich das Image in den lokalen Image-Speicher herunterladen: docker pull microsoft/dotnet.

Images und Tags

Images auf Docker Hub sind mit Tags versehen, die zum identifizieren verschiedener Versionen eines Images verwendet werden. Die Tags lassen sich auf Docker Hub einsehen. Wird ein Image mit dem Befehl docker pull heruntergeladen, wird standardmäßig die letzte Version genutzt, die den Tag latest trägt. Werden Images mit dem Befehl docker images angezeigt, kann der Tag auch angesehen werden. Von ein und demselben Image lassen sich natürlich unterschiedliche Versionen/Tags installieren. So wird bspw. das microsoft/dotnet Image mit dem Tag 2.1-sdk mit folgendem Befehl herunterladen: docker pull microsoft/dotnet:2.1-sdk.

Das SDK Image eignet sich zum Entwickeln. Es enthält das .NET Core CLI und ist ideal, um .NET-Core-Anwendungen zu erstellen und zu testen. D. h., selbst ohne .NET-Core-Installation auf dem lokalen Rechner lässt sich jetzt .NET Core verwenden. Neben dem SDK Image gibt es andere Image-Tags, die sehr gut im .NET Blog beschrieben sind. In diesem Artikel wird das SDK Image genutzt. Jetzt ist also das Image heruntergeladen, doch wie greift man auf einen von diesem Image erstellten Container zu?

Volume-Mappings

Ein einfacher Weg, um auf Dateien eines Containers zuzugreifen, ist über ein sogenanntes Volume-Mapping. Dabei lässt sich ein Ordner aus dem eigenen Rechner auf einen Ordner des Docker-Containers mappen. Mit folgendem Befehl wird ein Container gestartet, der den lokalen Ordner D:\Docker auf den Ordner /Docker des Linux-basierten Containers mappt: docker run -v D:\Docker:/Docker -it microsoft/dotnet:2.1-sdk.

Der v-Parameter ist für das Volume-Mapping, dabei wird zuerst der lokale Ordner angegeben, und dann, getrennt durch einem Doppelpunkt, der Ordner des Containers. Das i des -it-Parameters steht für Interactivity, damit sich Befehle im laufenden Container eingeben lassen. Das t steht für Teletypewriter, damit Ausgaben des Containers auch an der Console ankommen. Wird der Befehl ausgeführt, befindet man sich unmittelbar auf der Kommandozeile innerhalb des Containers. Da es ein Linux-Container ist, ist das die hochbeliebte Bash. Mit dem Befehl cd /Docker lässt sich jetzt in das gemappte Verzeichnis wechseln. Innerhalb des Containers lässt sich jetzt beispielsweise folgender Befehl ausführen, um eine neue .NET Core Console App mit dem Namen MyApp zu erstellen: dotnet new console –name MyApp.

Diese App taucht jetzt auch automatisch im gemappten Order D:\Docker auf. Im Container lässt sich mit dem Befehl cd MyApp in das Verzeichnis der App wechseln. Aus diesem Ordner lässt sich die App mit dem Befehl dotnet run ausführen, womit der Defaulttext „Hello World“ auf der Konsole ausgegeben wird. Mit dem Befehl exit wird die Bash und somit der Container verlassen, und man kehrt zurück auf die Kommandozeile des eigenen Betriebssystems.

In den Containers wechseln

Mit dem Befehl docker ps -a lassen sich alle Container anzeigen. Der im vorigen Abschnitt erstellte Container mit der erstellten MyApp wird als gestoppt angezeigt. Er wurde beim Verlassen der Bash gestoppt. Mit der Angabe der Container-ID – im Folgenden ff90 – lässt sich der Container mit folgendem Befehl wieder starten: docker start ff90.

Eine erneute Eingabe von docker ps -a zeigt in der Spalte STATUS, dass der Container jetzt läuft. Nun lässt sich der Befehl docker exec verwenden, um Programme innerhalb des Containers auszuführen. So wird bspw. mit folgendem Befehl die erstellte MyApp von außen gestartet: docker exec ff90 dotnet run –project /Docker/MyApp.

Mit obigem Befehl wird .NET auf dem Container mit der ID ff90 ausgeführt. Dabei wird das Kommando run und auch der Pfad zum auszuführenden Projekt angegeben. Der Text „Hello World“ wird folglich an der Console ausgegeben.

Anstatt die Applikation einfach auszuführen, kann man bspw. auch wieder auf die Bash des Linux-Containers wechseln. Dazu wird wie folgt die Bash gestartet und darauf gewechselt: docker exec -it ff90 bash. Der -it-Parameter sorgt wieder dafür, dass eine interaktive Session von der Kommandozeile aus gestartet wird. Jetzt befindet man sich auf der Bash des Containers, kann das Dateisystem des Containers mit Befehlen wie cd und ls erforschen und kann natürlich mit dem dotnet-Befehl weiterhin mit .NET Core arbeiten.

Eigene Images erstellen

Bisher wurden nur bestehende Images ausgewählt. Doch wie lassen sich eigentlich eigene Images erstellen? Das ist am besten nachvollziehbar, indem auf dem Rechner lokal eine neue .NET Core Console App erstellt wird. Hier wird der Name WinDevApp vergeben. Die Main-Methode wird wie in Listing 1 dargestellt implementiert, damit der Text „Hello Windows Developer“ auf der Konsole ausgegeben wird.

namespace WinDevApp
{
  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Hello Windows Developer!");
    }
  }
}

So weit so gut. Wird das Projekt gebaut, wird im Ordner bin\Debug\netcoreapp2.1 die WinDevApp.dll erstellt. Jetzt sollte diese in ein Docker Image gepackt werden. Dazu wird auf Ebene der .csproj-Datei eine Datei mit dem Namen DOCKERFILE erstellt. Die Datei hat keine Dateierweiterung. In der aktuellen Version von Windows 10 lässt sich eine Datei ohne Dateierweiterung auch problemlos im Windows Explorer erstellen. In älteren Windows-Version ist dies nur über einen kleinen Trick möglich.

Das Dockerfile beschreibt den Inhalt eines Images, und es ist somit die Bauanleitung für Container. Listing 2 zeigt eine sehr einfache Variante eines Dockerfiles. Mit dem FROM-Befehl wird in der ersten Zeile das microsoft/dotnet Image mit dem Tag 2.1-sdk erweitert. Das ist der übliche Weg in Docker: Es wird auf einem bestehenden Image aufgebaut. Anschließend wird mit dem WORKDIR-Befehl in ein /app Verzeichnis gewechselt. Mit dem COPY-Befehl werden die lokalen Dateien aus dem Pfad bin\Debug\netcoreapp2.1 in den aktuellen Ordner kopiert, der mit einem Punkt angegeben wird. Der aktuelle Ordner ist der /app-Ordner, in den zuvor gewechselt wurde. In der letzten Zeile des DOCKERFILES wird als Einstiegspunkt das dotnet-Programm mit der WinDevApp.dll „angegeben“.

FROM microsoft/dotnet:2.1-sdk

WORKDIR /app
COPY /bin/Debug/netcoreapp2.1/ .

ENTRYPOINT ["dotnet", "WinDevApp.dll"]

Mit dem Dockerfile lässt sich jetzt ein Image erstellen. Dazu wird eine Kommandozeile geöffnet und in das Projektverzeichnis gewechselt, in dem sich die DOCKERFILE-Datei befindet. Folgender Befehl baut jetzt ein neues Image namens windev:

docker build -t windev .

Mit dem Punkt am Ende wird der aktuelle Ordner angegeben. Der Befehl docker build liest das DOCKERFILE aus diesem Ordner aus und erstellt das Image namens windev. Wird jetzt der Befehl docker images eingegeben, taucht das neue Image mit dem Namen windev auf. Jetzt lässt sich von diesem neuen Image mit folgendem Befehl ein Container starten, ausführen und wieder entfernen: docker run –rm windev.

Der obige Befehl gibt den Text „Hello Windows Developer“ auf der Kommandozeile aus. Genau, was in der .NET Core Console App definiert wurde.

Das „DOCKERFILE“ optimieren

Anstatt wie in Listing 2 einfach nur den Output des Projekts in das Image zu kopieren, könnte im DOCKERFILE das Projekt selbst unter Verwendung des .NET Core CLI erstellt werden. Listing 3 zeigt ein einfaches DOCKERFILE, das genau das macht. Es wird mit dem COPY-Befehl alles aus dem lokalen Ordner kopiert (siehe Kasten: „Die wichtigsten Befehle“). Anschließend wird mit dem RUN-Befehl die .NET CLI ausgeführt und mit dem Befehl dotnet publish ein Release-Build im Ordner out veröffentlicht. Als ENTRYPOINT für den Container wird das dotnet-Programm mit der erstellten out/WinDevApp.dll angegeben. Das Erstellen des Images und Ausführen eines Containers erfolgt mit den im vorigen Abschnitt gezeigten Befehlen docker build und docker run.

FROM microsoft/dotnet:2.1-sdk

WORKDIR /app

# Alles kopieren
COPY . ./

# Alles im out Ordner bauen
RUN dotnet publish -c Release -o out
ENTRYPOINT ["dotnet", "out/WinDevApp.dll"]

Die wichtigsten Befehle

docker version
docker info
docker images
docker ps -a
docker run --rm <imageName>
docker exec –it <containerName> <command>
docker stop <containerName>
docker start <containerName>
docker rm <containerName>
docker rmi <imageName>

Ein erstelltes Docker Image ließe sich jetzt über den Befehl docker push in eine Image Registry wie den Docker Hub pushen, um das Image für die Allgemeinheit bereitzustellen. Alternativ könnte das Projekt mit dem DOCKERFILE bspw. auf GitHub hochgeladen werden und ein Continous Integration Prozess liest das DOCKERFILE aus, um daraus nach dem Committen von Code ein neues Image zu erstellen. Auf Docker Hub gibt es eine GitHub-Integration, die das automatisierte Bauen von Images mit Ressourcen eines GitHub Repositories erlaubt. Es gibt also zahlreiche Möglichkeiten, um das Bauen und Veröffentlichen von Images zu automatisieren. Zum Abschluss dieses Artikels werfen wir noch einen Blick auf die Docker-Unterstützung in Visual Studio.

Docker in Visual Studio

Visual Studio enthält eine integrierte Unterstützung für Docker. Wird eine neue ASP.NET-Core-Webapplikation erstellt, gibt es dort im Dialog eine Checkbox, um die Docker-Unterstützung zu aktivieren. Wurde diese Auswahl beim Erstellen des Projekts nicht getroffen, lässt sich das auch noch im Nachhinein aktivieren. Dazu genügt ein Rechtsklick auf das Projekt im Solution Explorer. Über das Kontextmenü und den Menüpunkt Add | Docker Support wird die Docker-Unterstützung hinzugefügt (Abb. 4). Unmittelbar nach einem Klick auf diesen Menüpunkt erscheint ein Dialog, der eine Auswahl zwischen Windows und Linux erlaubt. Darüber wird definiert, was für eine Art von Container erstellt wird.

Abb. 4: Docker Support in Visual Studio

Abb. 4: Docker Support in Visual Studio

Wurde der Docker Support hinzugefügt und Linux als Plattform ausgewählt, enthält das ASP.NET-Core-Projekt ein DOCKERFILE mit dem in Listing 4 dargestellten Inhalt. Wie darin zu sehen ist, gibt es mehrere FROM-Anweisungen, die aufeinander aufbauen. Dies ist ein sogenannter Multi-Stage Build. Jede FROM-Anweisung hat am Ende einen Alias, der mit dem AS-Schlüsselwort angegeben wird. Darüber lässt sich das erstellte Image innerhalb des DOCKERFILES referenzieren.

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 61618
EXPOSE 44385

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY WinDevWebApp/WinDevWebApp.csproj WinDevWebApp/
RUN dotnet restore WinDevWebApp/WinDevWebApp.csproj
COPY . .
WORKDIR /src/WinDevWebApp
RUN dotnet build WinDevWebApp.csproj -c Release -o /app

FROM build AS publish
RUN dotnet publish WinDevWebApp.csproj -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WinDevWebApp.dll"]

Neben dem in Listing 4 dargestellten DOCKERFILE wurde mit dem Hinzufügen des Docker Supports auch eine Datei mit dem Namen docker-compose.yml erstellt. Der Inhalt dieser Datei ist in Listing 5 dargestellt. Wie darin zu erkennen ist, ist darin das Dockerfile der Applikation angegeben. Die docker-compose.yml könnte Referenzen zu weiteren Dockerfiles haben, wenn die Applikation beispielsweise komplexer ist und aus mehreren Containern besteht, die miteinander kommunizieren sollen. Im Beispiel von Listing 5 gibt es nur einen Container und ein Dockerfile.

version: '3.4'

services:
  windevwebapp:
    image: ${DOCKER_REGISTRY}windevwebapp
    build:
      context: .
      dockerfile: WinDevWebApp/Dockerfile

Neben den erstellten Dateien zeigt Visual Studio nach dem Hinzufügen des Docker Supports in der Toolbar zum Starten der App anstelle von Debug den Text Docker an (Abb. 5). Wird die ASP.NET Anwendung jetzt gestartet, werden im Hintergrund ein Docker Image und ein Container erstellt, gestartet und Visual Studio live mit der in diesem Container laufenden ASP.NET Core Anwendung verbunden.

Abb. 5: In Docker starten

Abb. 5: In Docker starten

Es lässt sich ganz normal debuggen und entwickeln, wie das auch lokal geschieht. Mit den Befehlen docker images und docker ps -a lässt sich das erstellte Image und auch der erstellte Container ansehen, damit man versteht, was im Hintergrund genau passiert.

Fazit

Dieser Artikel hat die Grundlagen zu Docker gezeigt. Neben dem Deployment für produktive Applikationen ist Docker ideal, um Entwicklungs- und Testumgebungen aufzusetzen. Auf dem Docker Hub gibt es Datenbanken und andere Services, die das Entwicklerherz höherschlagen lassen. Haben Entwickler erst einmal mit Docker angefangen, sind vermutlich die Tage gezählt, an denen sie SQL Server direkt auf ihrem Entwicklungsrechner installiert haben. Stattdessen läuft das Ganze in einem Container. Unterschiedliche Versionen lassen sich somit problemlos parallel installieren, starten, stoppen und auch wieder entfernen.

Wie der Artikel aufgezeigt hat, wird mit einem DOCKERFILE der Bauplan für ein Image und damit für einen Container beschrieben. Somit lassen sich Applikationen und Abhängigkeiten strukturiert abbilden und automatisiert in Form eines Containers laden. Diese Mächtigkeit von Docker macht virtuelle Maschinen weitestgehend überflüssig und bringt uns im 21. Jahrhundert endlich an den Punkt, an dem wir tatsächlich von echten Softwarekomponenten sprechen können. Es ist an der Zeit, sich mit dem Thema Docker auseinanderzusetzen, denn in Zukunft wird vermutlich kein Entwickler daran vorbeikommen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -