Windows- und Linux-basierte Docker-Container auf Windows nutzen (Teil 2 von 2)

Seite 2: Images aus Containern erstellen

Inhaltsverzeichnis

Der Nutzer kann aus einem Container, den er nach Belieben konfiguriert hat, ein neues Docker-Image erstellen. Dazu dient der Befehl docker commit, bei dem der Name eines Containers und der neue Name für das Image anzugeben sind, zum Beispiel

docker commit Webserver WebserverImage:v1.0

Alternativ per PowerShell-Commandlets:

Get-Container Webserver | ConvertTo-ContainerImage -Repository ↵ 
WebserverImage -Tag v1.0

Dabei ist zu beachten, dass der Container mit docker stop Webserver beziehungsweise Stop-Container Webserver zu stoppen ist, da Windows das Erstellen von Images aus laufenden Containern nicht unterstützt. Außerdem darf der Image-Name keine Großbuchstaben enthalten. Das neue Image ist dann per Get-ContainerImage sichtbar. Per docker history WebserverImage kann man sich ansehen, worauf das neue Image basiert und wie viel Speicherplatz es im Vergleich zum vorherigen Image benötigt. Wenn man kein Tag angibt, vergibt Docker automatisch das Tag "Latest".

Wer in Visual Studio 2017 ein neues .NET-Core-Projekt anlegt, erhält direkt die Option, das Projekt in Docker zu hosten (s. Abb. 1). Egal, ob man das gleich nutzt oder später mit "Add/Docker Support" hinzufügt, man erlebt in beiden Fällen, dass Visual Studio keinen Windows-basierten Container anlegt, sondern einen Linux-Container (siehe Abbildung 2) mit dem Basis-Image "microsoft/aspnetcore:1.1", das trotz des ähnlichen Namens wie "microsoft/aspnet" ein Linux-Image ist.

Beim Anlegen eines .NET-Core-Projekts bietet Visual Studio 2017 die Docker-Unterstützung direkt an (Abb. 1).

Docker-Dateien in einem ASP.NET-Core-Projekt mit Unterstützung für Linux-Container (Abb. 2).


Wer versucht, das Docker-Image nun zu übersetzen, erhält (zumindest in der aktuellen Version, die sich aber angesichts der agilen Entwicklung bei Microsoft täglich ändern kann) zunächst auf die Fehlermeldung "client version 1.22 is too old. Minimum supported API version is 1.24". Hier kann man Visual Studio zufrieden stellen, indem man in allen .yml-Dateien den Versionsnummerneintrag zu Dateibeginn (s. Abb. 2) von 2.0 auf 2.1 erhöht. Sehr viel weiter kommt man im Übersetzungsvorgang aber auch damit nicht, da als Nächstes die Meldung "Operating system Linux cannot be used on this platform" erscheint. Für das Erzeugen des Containers muss man im Docker-Symbol in der Taskleiste "Switch zu Linux Containers" wählen. Laufende Windows-Container werden dadurch nicht beeinträchtigt; sie laufen weiter, lassen sich aber vorerst nicht mehr administrieren. Der Versuch, nun einen Windows-Container herunterzuladen, führt zum nichtssagenden Fehler "Unknown Blob". Ob man gerade mit Linux- oder Windows-Containern arbeitet, sieht man auch beim Aufruf von docker version im Eintrag Server:OS/Arch:

Ausgabe von docker version bei aktivierten Linux- (links) und Windows-Containern (rechts) (Abb. 3).


Außerdem müssen Entwickler für den Betrieb von Linux-Containern im Docker-Taskleistensymbol unter dem Kontextmenüeintrag "Settings/Shared Drives" das Systemlaufwerk (c:) und gegebenenfalls das Laufwerk, auf dem sich der Programmcode befindet, für die Moby-Linux-VM freigeben.

Beim Debugging eines Linux-Containers mit .NET Core legt Visual Studio ein Portmapping von Port 80 im Container auf einen dynamisch zugewiesenen Port (z.B. 32768) im Host an. Auch ein solches Portmapping kann man von der Konsole aus beim Start eines Containers definieren. In der PowerShell ist das aber bislang sehr umständlich, siehe nachfolgendes Listing (vgl. GitHub-Issue):

# Start eines Containers mit Port-Mapping
$image = "dockerwebdemo:latest"
$port = 12340
$containername = "Webserver$port"
Get-container | out-null # sicherstellen, dass Docker.DotNet.dll geladen!
$pb = New-Object Docker.DotNet.Models.PortBinding
$pb.HostPort = $port
$hostConfig = New-Object Docker.DotNet.Models.HostConfig
$hostConfig.PortBindings = [System.Collections.Generic.Dictionary[string, ↵
System.Collections.Generic.iList[Docker.DotNet.Models.PortBinding]]]::new()
$hostConfig.PortBindings.Add("80/tcp",[System.Collections.Generic.List ↵
[Docker.DotNet.Models.PortBinding]]::new ↵
([Docker.DotNet.Models.PortBinding[]]@($pb)))
"Anlegen eines neuen Containers $containername auf Basis von Image $image, ↵
Webserver erreichbar unter http://localhost:$port..."
$c = New-Container dockerwebdemo:latest -HostConfiguration $hostConfig -Name ↵
$containername
"Start des Containers $containername..."
$c | Start-Container

An der Stelle ist docker.exe wieder im Vorteil, wo sich das Mapping einfach via docker run --detach -p 12345:80 --name Webserver12345 dockerwebdemo:latest erledigen lässt.

Wenn man nun .NET Core in einem Windows-Container hosten möchte, muss man im Dockerfile das Basis-Image bei FROM auf microsoft/aspnetcore:1.1.2-nanoserver ändern, und die Versionseinträge der .yml-Dateien auf 2.1 setzen. Die Containererstellung im Release-Modus funktioniert dann zwar, Unterstützung für das Debugging von Windows-Containern gibt es allerdings erst ab Visual Studio 2015 Update 3, das derzeit noch in der Preview-Phase ist.