Warum zwei Container auf demselben Host sich standardmäßig nicht erreichen

Starten Sie Postgres und Ihre API mit einem einfachen docker run, und die API kommt nicht an die Datenbank heran, obwohl beide Container auf derselben Maschine laufen.

1
2
docker run -d --name db postgres:16
docker run -d --name api myapp

Beide landen auf der Standard-Bridge von Docker, auf der jeder Docker-Container landet, solange Sie nichts anderes angeben. Sie bekommen eine IP-Adresse. Eine Möglichkeit, sich gegenseitig über den Namen zu finden, fehlt jedoch — die API müsste also die interne IP der Datenbank fest eintragen, eine Adresse, die sich bei jedem Neustart des Containers ändert.

Ein Docker-Netzwerk löst das. Es ist ein virtueller Switch, an den sich Container anschließen, mit eigenem IP-Bereich und eigenem DNS. Legen Sie eines an, und Containernamen werden zu Hostnamen — Sie entscheiden, wer wen sehen kann.

Welcher Netzwerktreiber: Bridge, Host oder Overlay

Docker bringt mehrere eingebaute Netzwerktreiber mit. Drei davon decken fast alles ab, was Sie auf einem einzelnen Host oder in einem Swarm brauchen:

TreiberUmfangContainer bekommen eigene IPNamensbasiertes DNSTypischer Einsatz
bridge (Standard)ein Hostjanein (nur IP)was Sie ohne --network-Flag bekommen
bridge (user-defined)ein Hostjajadie Standardwahl für lokale Entwicklung und Single-Host-Deployments
hostein Hostnein — teilt den Netzwerk-Stack des Hostsn/aDienste mit hohem Durchsatz, Tools, die die reale Netzwerksicht des Hosts brauchen
overlaymehrere Hosts (Swarm)jajaDienste, die über mehr als einen Docker-Host verteilt sind

Es gibt auch none, das einem Container nur ein Loopback-Interface gibt und sonst nichts. Nützlich für einen Batch-Job, der im Netzwerk nichts zu suchen hat.

Jeder frische docker run landet auf der Standard-Bridge. Sie funktioniert, routet aber nur nach IP; das alte --link-Flag existierte, um das Fehlen von Namen zu überbrücken, und Docker stuft es inzwischen als veraltet ein. Eine user-defined Bridge erledigt denselben Job mit eingebautem DNS. Das ist die, die Sie wollen.

Ein Docker-Netzwerk anlegen und Container daran anhängen

1
docker network create app-net

Das ist eine private Bridge mit eigenem Subnetz, isoliert von der Standard-Bridge und von jedem anderen user-defined Netzwerk, das Sie anlegen. Container beim Start anhängen:

1
2
docker run -d --name db --network app-net -e POSTGRES_PASSWORD=devpass postgres:16
docker run -d --name api --network app-net -p 8080:8080 myapp

Innerhalb von api funktioniert die Verbindung zu db:5432 problemlos. Keine IP-Suche, kein Jonglieren mit Umgebungsvariablen: Der eingebaute DNS-Server von Docker löst den Containernamen db zu seiner aktuellen Adresse in app-net auf und aktualisiert diese Zuordnung, wenn der Container mit einer neuen IP neu startet. Docker Compose läuft über denselben Mechanismus. docker compose up legt ein Netzwerk für den Stack an, und jeder Dienst erreicht die anderen über den Service-Namen.

Sie können auch einen bereits laufenden Container anhängen, ohne ihn neu zu starten:

1
2
docker network connect app-net some-container
docker network disconnect app-net some-container

Ein Container kann gleichzeitig in mehreren Netzwerken hängen. So geben Sie einem Dienst ein öffentlich erreichbares Netzwerk und ein privates, das nur mit den Backend-Containern dahinter geteilt wird.

Warum ein veröffentlichter Port zwei Container nicht miteinander verbindet

-p 8080:8080 mappt Port 8080 des Hosts auf den des Containers, sodass Sie die API im Browser über localhost:8080 erreichen. Das sagt nichts darüber aus, ob api db erreichen kann. Traffic zwischen Containern im selben user-defined Netzwerk bleibt intern, veröffentlichte Ports hin oder her.

Veröffentlichen Sie nur die Ports, die etwas außerhalb des Docker-Hosts erreichen muss. Eine Datenbank, die nur mit der API spricht, braucht kein -p 5432:5432: Lassen Sie sie unveröffentlicht, dann kann sich weder der Host noch das Internet direkt damit verbinden. api erreicht sie trotzdem, weil beide app-net teilen.

Wann –network host sinnvoll ist und wann nicht

1
docker run --rm -d --network host --name my_nginx nginx

Mit --network host überspringt der Container seinen eigenen Netzwerk-Namespace und bindet sich direkt an die Interfaces des Hosts. Kein NAT, keine Bridge, kein Port-Mapping: -p und -P werden ignoriert, und Docker gibt eine Warnung aus, wenn Sie sie trotzdem übergeben. Das senkt einen kleinen Teil des Overheads pro Paket und gibt dem Container die reale Sicht auf die Netzwerk-Interfaces des Hosts — genau das, was Netzwerk-Monitoring-Tools brauchen und Software, die auf einen großen zusammenhängenden Port-Bereich angewiesen ist.

Dafür geht auch die Isolation verloren, die eine Bridge bietet. Der Container kann jeden freien Port des Hosts belegen und sieht alles, was im Netzwerk des Hosts passiert. Unter Linux ist das vollständig unterstützt. Docker Desktop für Mac und Windows verlangt eine explizite Aktivierung (verfügbar ab Docker Desktop 4.34), weil das Netzwerk der Docker-VM nicht das des Hosts ist. Windows-Container unterstützen es überhaupt nicht. Für die meisten Dienste erledigt eine user-defined Bridge mit veröffentlichten Ports denselben Job mit weniger Angriffsfläche.

Wann Sie ein Overlay-Netzwerk statt einer Bridge brauchen

Bridge- und Host-Netzwerke enden an der Grenze eines einzelnen Docker-Hosts. Betreiben Sie Docker Swarm über mehrere Maschinen, braucht ein Dienst auf Host A ein Overlay-Netzwerk, um einen Dienst auf Host B zu erreichen:

1
docker network create -d overlay --attachable app-overlay

Ein Overlay kapselt den Container-Traffic und routet ihn zwischen den Docker-Daemons jedes Swarm-Knotens, sodass sich Container auf verschiedenen physischen oder virtuellen Maschinen per Namen genauso auflösen lassen wie auf einer Bridge. Das --attachable-Flag erlaubt es auch eigenständigen Containern, beizutreten; ohne dieses Flag können sich nur Swarm-Dienste verbinden. Betreiben Sie keinen Swarm oder einen anderen Multi-Host-Orchestrator, überspringen Sie diesen Treiber. Ein Single-Host-Bridge-Netzwerk deckt Compose-Stacks und lokale Setups ab.

Ein Docker-Netzwerk prüfen und debuggen

1
docker network ls

Jedes Netzwerk auf dem Host, einschließlich bridge, host und none, die Docker immer anlegt.

1
docker network inspect app-net

Das Subnetz, das Gateway und jeden gerade angehängten Container mit seiner IP in diesem Netzwerk. Das ist der erste Ort, an dem Sie nachsehen sollten, wenn ein Container einen anderen nicht erreicht. Lesen Sie den Abschnitt Containers: Fehlt einer der beiden, hängt er nicht in diesem Netzwerk, und docker network connect bringt ihn dort hinein.

Um die DNS-Auflösung aus einem laufenden Container heraus zu testen:

1
docker exec api getent hosts db

Kommt eine IP zurück, funktioniert die Namensauflösung und das Problem liegt in der Anwendung — falscher Port oder der Dienst hört noch nicht. Kommt nichts zurück, hängen die beiden Container nicht im selben Netzwerk. Bestätigen Sie das mit docker network inspect, oder fragen Sie jeden Container, woran er angehängt ist:

1
docker inspect <container> --format '{{ .NetworkSettings.Networks }}'

Ein Netzwerk pro Stack, mit jedem Container darauf

Die Standard-Bridge bekommen Sie gratis. Darauf sollten Sie nicht aufbauen. Führen Sie docker network create <app>-net für jeden Anwendungs-Stack aus, hängen Sie jeden Container an, den dieser Stack braucht, und heben Sie --network host für den seltenen Dienst auf, der wirklich den Netzwerk-Stack des Hosts benötigt.

Sobald Sie mehr als ein paar Container von Hand verwalten, hören Sie damit auf. Docker Compose legt das Netzwerk an und hängt die Container für Sie an, aus einer einzigen YAML-Datei. Docker-Volumes decken die andere Hälfte ab: dass die Daten, die diese Container schreiben, einen Neustart überstehen.

Verwandte Artikel