In einem Multi-Container-Setup kommunizieren die in den Containern ausgeführten Dienste über ein gemeinsames Netzwerk miteinander. Im selben Setup interagieren einige Container auch mit der Außenwelt.
Diese interne und externe Kommunikation wird mit exponierten bzw. veröffentlichten Ports in Docker abgewickelt.
In diesem Tutorial werde ich den Umgang mit Ports in Docker besprechen. Ich werde weiter auf den Unterschied zwischen dem Freigeben und Veröffentlichen von Ports eingehen, warum sie verwendet werden und wie sie verwendet werden.
Offenlegen vs. Veröffentlichen eines Ports in Docker
Es gibt zwei Möglichkeiten, Ports in Docker zu behandeln:Offenlegen der Ports und Veröffentlichen der Ports.
Einen Port verfügbar machen bedeutet einfach, anderen mitzuteilen, auf welchem Port die containerisierte Anwendung lauschen wird, oder Verbindungen zu akzeptieren. Dies dient der Kommunikation mit anderen Containern, nicht mit der Außenwelt.
Veröffentlichen eines Ports ist eher wie das Mapping der Ports eines Containers mit den Ports des Hosts. Auf diese Weise kann der Container mit externen Systemen, der realen Welt, dem Internet kommunizieren.
Diese Grafiken helfen Ihnen beim Verständnis.
Sehen Sie, wie der Port 80 des SERVER-Containers auf den Port 80 des Hostsystems abgebildet wird? Auf diese Weise kann der Container über die öffentliche IP-Adresse des Hostsystems mit der Außenwelt kommunizieren.
Auf die exponierten Ports hingegen kann von außerhalb der Containerwelt nicht direkt zugegriffen werden.
Zur Erinnerung:
- Offengelegte Ports werden für die interne Containerkommunikation innerhalb der Containerwelt verwendet.
- Veröffentlichte Ports werden für die Kommunikation mit Systemen außerhalb der Containerwelt verwendet.
Ports in Docker verfügbar machen
Zunächst einmal ist das Freilegen eines Ports nicht unbedingt erforderlich. Wieso den? Weil die meisten Docker-Images, die Sie in Ihrem Setup verwenden, bereits einen Standardport in ihrer Konfiguration offengelegt haben.
Beispielsweise kann eine containerisierte Frontend-Anwendung mit einer MariaDB-Datenbank kommunizieren, indem sie einfach die IP des Containers und den Port angibt, auf dem MariaDB Verbindungen akzeptiert (Standard 3306).
Das Offenlegen hilft in einer Situation, in der die Standardwerte nicht verwendet werden, wie in diesem Fall, wenn MariaDB keine Verbindungen auf Port 3306 akzeptieren würde, sollte ein alternativer Port durch Offenlegen erwähnt werden.
Es gibt zwei Möglichkeiten, wie Sie einen Port verfügbar machen können:
- Mit dem
EXPOSE
Dockerfile-Anweisung. - Mit
--expose
mit docker CLI oderexpose
Geben Sie docker-compose ein.
Zeit, tiefer in beide einzutauchen.
Methode 1:Ports über Dockerfile verfügbar machen
Sie können Ihrer Docker-Datei eine einfache Anweisung hinzufügen, um andere wissen zu lassen, an welchem Port Ihre Anwendung Verbindungen akzeptiert.
Was Sie über diese Anleitung wissen müssen, ist Folgendes:-
EXPOSE
nicht Fügen Sie dem resultierenden Docker-Image zusätzliche Ebenen hinzu. Es fügt nur Metadaten hinzu.EXPOSE
ist eine Art der Dokumentation Ihr Anwendungsport. Der einzige Effekt ist die Lesbarkeit oder das Verständnis der Anwendung.
Sie können sehen, wie Expose mit einem einfachen Container-Image funktioniert, das ich nur für diesen Zweck erstellt habe. Dieses Bild macht nichts .
Ziehen Sie das Bild.
docker pull debdutdeb/expose-demo:v1
Dieses Image legt insgesamt vier Ports offen, listen Sie das Image mit dem folgenden Befehl auf.
docker image ls --filter=reference=debdutdeb/expose-demo:v1
Sehen Sie sich die SIZE
an Spalte, es wird 0 Bytes sagen.
➟ docker image ls --filter=reference=debdutdeb/expose-demo:v1
REPOSITORY TAG IMAGE ID CREATED SIZE
debdutdeb/expose-demo v1 ad3d8ffa9bfe N/A 0B
Der Grund ist einfach, es gibt keine Ebenen in diesem Bild, alle hinzugefügten Belichtungsanweisungen waren Metadaten, keine echten Ebenen.
Sie können die Anzahl der verfügbaren Ebenen auch mit dem folgenden Befehl abrufen:-
docker image inspect -f '{{len .RootFS.Layers}}' debdutdeb/expose-demo:v1
Sie sollten eine Ausgabe wie diese sehen:-
➟ docker image inspect -f '{{len .RootFS.Layers}}' debdutdeb/expose-demo:v1
0
Wie ich bereits sagte, werden die exponierten Ports als Bildmetadaten hinzugefügt, um uns mitzuteilen, welche Ports die Anwendung verwendet.
Wie sehen Sie diese Informationen, ohne sich eine Dockerfile anzusehen? Sie müssen das Bild überprüfen. Um genauer zu sein, siehe den folgenden Befehl. Sie können einen ähnlichen Befehl für jedes Image verwenden, um die exponierten Ports aufzulisten.
Ich verwende mein Beispielbild zur Demonstration.
docker image inspect -f \
'{{range $exposed, $_ := .Config.ExposedPorts}}{{printf "%s\n" $exposed}}{{end}}' \
debdutdeb/expose-demo:v1
Beispielausgabe:
➟ docker image inspect -f '{{range $exposed, $_ := .Config.ExposedPorts}}{{printf "%s\n" $exposed}}{{end}}' debdutdeb/expose-demo:v1
443/tcp
80/tcp
8080/tcp
9090/tcp
Sie können dieses Image, das einige Ports offenlegt, auch mit debdutdeb/noexpose-demo:v1
vergleichen Bild, das keine Ports offenlegt. Führen Sie denselben Befehlssatz auch für dieses Bild aus und bemerken Sie den Unterschied.
Methode 2:Verfügbarmachen von Ports über CLI oder docker-compose
Manchmal scheuen Anwendungsentwickler davor zurück, ein zusätzliches EXPOSE
einzufügen Anleitung in ihrer Dockerfile.
Um sicherzustellen, dass andere Container (über die Docker-API) den verwendeten Port problemlos erkennen können, können Sie in einem solchen Fall mehrere Ports nach dem Build als Teil des Bereitstellungsprozesses verfügbar machen.
Wählen Sie entweder die imperative Methode, d. h. die CLI, oder die deklarative Methode, d. h. Dateien erstellen.
CLI-Methode
Bei dieser Methode müssen Sie beim Erstellen eines Containers lediglich --expose
verwenden Option (so oft wie nötig) mit der Portnummer und optional dem Protokoll mit einem /
. Hier ist ein Beispiel:-
docker container run \
--expose 80 \
--expose 90 \
--expose 70/udp \
-d --name port-expose busybox:latest sleep 1d
Ich verwende die busybox
Bild, das standardmäßig keine Ports verfügbar macht.
Dateimethode erstellen
Wenn Sie eine Compose-Datei verwenden, können Sie ein Array expose
hinzufügen in der Dienstdefinition. Sie können die vorherige Bereitstellung wie folgt in eine Compose-Datei konvertieren:-
version: "3.7"
services:
PortExpose:
image: busybox
command: sleep 1d
container_name: port-expose
expose:
- 80
- 90
- 70/udp
Sobald Sie den Container ausgeführt haben, können Sie ihn genau wie zuvor überprüfen, um zu wissen, welche Ports verfügbar gemacht werden. Der Befehl sieht ähnlich aus.
docker container inspect -f \
'{{range $exposed, $_ := .NetworkSettings.Ports}}
{{printf "%s\n" $exposed}}{{end}}' \
port-expose
Beispielausgabe:-
➟ docker container inspect -f '{{range $exposed, $_ := .NetworkSettings.Ports}}{{printf "%s\n" $exposed}}{{end}}' port-expose
70/udp
80/tcp
90/tcp
Veröffentlichen eines Ports in Docker
Port-Publishing ist ein Synonym für Port-Forwarding, bei dem die Anfragen einer eingehenden Verbindung auf einem öffentlichen Port an den Port des Containers weitergeleitet werden.
In ähnlicher Weise werden die vom Container über seinen Port gesendeten Antworten an den Client gesendet, indem der Datenverkehr an den angegebenen Port im Portbereich des Hosts weitergeleitet wird.
Es gibt zwei Möglichkeiten, einen Port zu veröffentlichen, eine über die CLI und eine andere mithilfe einer Compose-Datei. Beide Methoden haben auch eine lange Syntax und eine kurze Syntax.
Methode 1:Ports per Docker-Befehl veröffentlichen
Die beiden Syntaxen lauten wie folgt:
-p [optional_host_ip]:[host_port]:[container_port]/[optional_protocol]
--publish target=[container_port],published=[optional_host_ip]:[host_port],protocol=[optional_protocol]
Für die optionale Host-IP können Sie jede IP-Adresse verwenden, die mit einer der NICs verknüpft ist. Wenn die IP weggelassen wird, bindet Docker den Port mit allen verfügbaren IP-Adressen.
Sie werden den ersten am häufigsten verwenden. Der zweite ist besser lesbar. Sehen wir uns ein Beispiel mit einem Nginx-Container an. Führen Sie den folgenden Befehl aus:-
docker container run --rm --name nginx \
--publish target=80,published=127.0.0.1:8081,protocol=tcp \
-d nginx
Mit diesem Befehl binde ich einfach den Port 80 des Containers an den Port 8081 meines Hosts auf localhost. Wenn Sie jetzt zu http://localhost:8081 gehen, sehen Sie, dass nginx ausgeführt wird.
Der vorherige Befehl kann einfach so in die kürzere Form umgewandelt werden
docker container run --rm --name nginx \
-p 80:127.0.0.1:8081/tcp -d nginx
Obwohl es kürzer ist, ist es schwieriger zu lesen.
Sie können auch -p
verwenden oder --publish
mehrmals, um mehrere Ports zu veröffentlichen.
Methode 2:Veröffentlichen eines Ports über eine Compose-Datei
Um einen Port mit einer Compose-Datei zu veröffentlichen, benötigen Sie ein Array namens ports
in der Dienstdefinition. Dieses Array kann eine Liste von Strings sein, die der kurzen Syntax der CLI ähneln, oder Sie können eine Liste von Objekten verwenden, die der langen Syntax ähnelt.
Wenn ich die vorherige nginx-Bereitstellung mithilfe einer Compose-Datei mit einem Array von Zeichenfolgen für den Ports-Abschnitt konvertieren würde, würde es wie folgt aussehen:-
version: "3.7"
services:
Nginx:
image: nginx
container_name: nginx
ports:
- 80:127.0.0.1:8081/tcp
Lassen Sie mich auch zeigen, wie die Array-of-Objects-Syntax verwendet wird.
version: "3.7"
services:
Nginx:
image: nginx
container_name: nginx
ports:
- target: 80
published: 127.0.0.1:8081
protocol: tcp
Um die Liste aller veröffentlichten Ports zu sehen, können Sie den Container wie folgt untersuchen:
docker container inspect -f '{{range $container, $host := .NetworkSettings.Ports}}{{printf "%s -> %s\n" $container $host}}{{end}}' nginx
Wenn Sie es ausführen, sehen Sie die folgende Ausgabe:-
➟ docker container inspect -f '{{range $container, $host := .NetworkSettings.Ports}}{{printf "%s -> %s\n" $container $host}}{{end}}' nginx
80/tcp -> [{127.0.0.1 8081}]
Es gibt eine andere, einfachere Möglichkeit, die veröffentlichten Ports aufzulisten, indem Sie den docker container port
verwenden Befehl.
docker container port nginx
Beispielausgabe:-
➟ docker container port nginx
80/tcp -> 127.0.0.1:8081
Wann soll ein Port offengelegt und wann veröffentlicht werden?
Das ist eine berechtigte Frage. Ausstellen und Publizieren sollen keine Konkurrenten sein. Jeder dient einem anderen Zweck. Wenn Sie der Entwickler eines Images sind, legen Sie die Ports offen, damit der Benutzer besser sicher sein kann, wo er eine Verbindung herstellen muss. Wenn Sie andererseits ein Image verwenden und es der Außenwelt zur Verfügung stellen müssen, werden Sie die erforderlichen Ports veröffentlichen.
Ich hoffe, dieser Artikel war hilfreich für Sie. Wenn Sie Fragen haben, lassen Sie es mich in den Kommentaren unten wissen.