Anwendungen mit Docker transportabel machen

Seite 3: Netzwerkzugriff

Inhaltsverzeichnis

Der Versuch, das Image als Grundlage für einen neuen Container zu verwenden, der eine Node.js-Anwendung ausführt, schlägt allerdings fehl:

$ docker run goloroden/nodejs node -e "console.log('Hello Node.js');"
Unable to find image 'goloroden/nodejs' (tag: latest) locally

Grund ist die fehlende Angabe eines Tags beim Aufruf des run-Befehls: Fehlt es, verwendet Docker automatisch den Wert latest, der für das neu erzeugte Image allerdings nicht existiert. Als Ausweg bietet sich an, das entsprechende Tag zusätzlich von Hand zu erzeugen:

$ docker commit bf43dab1247c goloroden/nodejs:latest
c126f8d31ead2889163f6dd72c887272c4b7205bd0cfccf59165cfbb59176efa

Ein erneuter run-Aufruf bringt im Anschluss das gewünschte Ergebnis:

$ docker run goloroden/nodejs node -e "console.log('Hello Node.js');"
Hello Node.js

Während des Experimentierens entstehen rasch zahlreiche Images und Container, die nicht dauerhaft aufzuheben sind. Um Erstere zu entfernen, kennt Docker das rmi-Kommando, das als Parameter die Angabe eines Images erwartet, wobei sich wahlweise dessen Name oder ID angeben lässt:

$ docker rmi <image>

Das Entfernen von Containern erfolgt prinzipiell analog, statt rmi ist lediglich rm zu verwenden:

$ docker rm <container>

Docker prüft vor dem Entfernen eines Images, dass kein Container auf es verweist. Da der Befehl ps lediglich jene Container anzeigt, die derzeit ausgeführt werden, kann es sich schwierig gestalten, die ID von älteren zu ermitteln.

Abhilfe schafft der Parameter -a, der ps anweist, Container unabhängig von ihrem Status anzuzeigen. Kombiniert man das mit der Unix-Anweisung grep, lassen sich leicht alle aufspüren, die auf einem bestimmten Image basieren:

$ docker ps -a | grep <image>

Entscheidet man schließlich, ein Image in der öffentlichen Registry von Docker zu veröffentlichen, dient dazu das Kommando push, das den Namen des Images als Parameter erwartet:

$ docker push goloroden/nodejs

Unter Umständen ist die Verwendung einer Registry zwar gewünscht, nicht jedoch die damit verbundene Öffentlichkeit. Das gilt insbesondere für Images, die für den privaten Gebrauch entwickelt werden, beispielsweise innerhalb von Unternehmen. In dem Fall lässt sich mit verhältnismäßig geringem Aufwand eine private Registry betreiben. Der erforderliche Code und die dazugehörige Anleitung stehen auf GitHub zur Verfügung.

Alternativ kann man auf eine in der Cloud gehostete private Registry ausweichen, in der sich idealerweise gezielte Zugriffsrechte für Entwickler und Teams vergeben lassen. Derartige Dienste, beispielsweise Quay.io, sind in der Regel jedoch kostenpflichtig.

Kaum einfacher könnte in Docker der tatsächliche Zugriff auf eine alternative Registry gelöst sein. Dem Namen des Images sind dazu lediglich Host und Port der Registry voranzustellen, wie die folgende Anweisung zeigt:

$ docker push 192.168.0.100:5000/goloroden/nodejs

Die Dokumentation von Docker enthält eine ausführliche Beschreibung der Struktur der Registry und der API, über die die Client-Komponente mit der Registry kommuniziert.

Docker schottet jeden Container vom Netzwerk ab. Daher lässt sich über den Aufruf

$ docker run goloroden/nodejs node -e 
"require('http').createServer(function (req, res)
{res.end('Hello Node.js'); }).listen(3000);"

zwar ein mit Node.js arbeitender Webserver starten, allerdings ist der Zugriff darauf von außerhalb des Containers nicht möglich. In dem man zusätzlich den Parameter -p und den internen Port des Containers angibt, lässt sich das leicht ändern:

$ docker run -p 3000 goloroden/nodejs node -e 
"require('http').createServer(function (req, res)
{res.end('Hello Node.js'); }).listen(3000);"

Docker kümmert sich daraufhin um die Einrichtung einer Portweiterleitung zum Wirt. Um den zugewiesenen Port zu ermitteln, genügt ein Blick in die PORTS-Spalte des ps-Kommandos:

$ docker ps
CONTAINER ID ... PORTS ...
854b1876bbb2 ... 0.0.0.0:49153->3000/tcp ...

Ruft man den Wirt nun unter Angabe des Ports 49153 in einem Webbrowser auf, erhält man die entsprechende Ausgabe des Webservers. Will man an Stelle des von Docker zufällig ausgewählten Ports eine Vorgabe machen, ist sie zusätzlich im Aufruf anzugeben:

$ docker run -p 5000:3000 goloroden/nodejs node -e
"require('http').createServer(function (req, res)
{res.end('Hello Node.js'); }).listen(3000);"

Anschließend besteht eine Port-Weiterleitung des internen Ports 3000 auf den Port 5000 des Wirts. Darüber hinaus lässt sich eine IP-Adresse angeben, sodass man den Container gezielt an eine der Netzwerkkarten des Wirts binden kann.

Innerhalb des Containers ist beim Zugriff auf das Netzwerk darauf zu achten, dass die Netzwerkschnittstelle einen kurzen Moment für die Initialisierung benötigt. Daher kann es vorkommen, dass die Anwendung innerhalb des Containers bereits läuft, bevor das Netzwerk verfügbar ist. Es gilt also, gegebenenfalls beim Anwendungsstart auf die Verfügbarkeit von "eth0" zu warten.