Verbindung zu einem Dienst auf einem Docker -Host von einem Docker -Container aus
Wenn Sie den Standard-Netzwerkmodus bridge verwenden, müssen Sie einen Dienst auf dem Docker -Host (auch) auf docker0 hören lassen.
Wenn Sie ein Docker -Problem haben und im Internet suchen, werden Sie fast sicher über die Frage stolpern: Wie kann ich mich mit localhost verbinden? Gemeint ist: Wie kann ich mich von einem Docker -Container aus mit einem Dienst auf dem Docker -Host verbinden? Als ich anfing, Docker zu verwenden, hatte ich auch mit diesem Problem zu kämpfen.
Ich verwende Linux, Ubuntu, und als schließlich host.docker.internal für Linux verfügbar war, dachten viele Leute, dass alle ihre Probleme vorbei seien. Aber nichts hat sich geändert. Das Problem bleibt das gleiche.
Es gibt viele Beiträge im Internet zu diesem Thema, und dies ist ein weiterer. Ich werde nur auf den Standard-Netzwerkmodus bridge eingehen und nicht auf den Modus "network=host".
Verwendete Werkzeuge
In diesem Beitrag verwende ich ein paar Kommandozeilen-Tools, die Sie vielleicht installieren möchten. Holen Sie sich die neueste Version, oder fügen Sie sie dem Repository auf Ihrem Rechner hinzu:
sudo apt update
netcat
Wird zum Erstellen eines Listener-Dienstes verwendet.
sudo apt install netcat
netstat
Wird verwendet, um Listener auf dem Host anzuzeigen.
sudo install net-tools
nmap
Ermöglicht das Scannen (nach offenen) Ports.
sudo apt install nmap
Listener-Dienste und Netzwerke
Richten wir einen Listener-Dienst in einem Terminal-Fenster ein, der auf Port 2345 auf localhost hört, wobei localhost 127.0.0.1 ist. Wir fügen die Option -k (keep open) hinzu, um zu verhindern, dass netcat beim Trennen der Verbindung beendet wird:
netcat -k -l 127.0.0.1 2345
Überprüfen Sie, ob ein netcat -Dienst auf Sie hört:
sudo netstat -tunlp
Oder, wenn Sie eine sehr lange Liste haben:
sudo netstat -tunlp | grep 2345
Ergebnis:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 127.0.0.1:2345 0.0.0.0:* LISTEN 2961983/netcat
...
Das bedeutet, dass netcat auf IP address 127.0.0.1 auf Port 2345 hört.
Starten Sie nun einen Busybox Docker Container in einem anderen Terminalfenster:
docker run -it --add-host=host.docker.internal:host-gateway --rm busybox
Und dann versuchen Sie in dem Container auf unseren Listener-Dienst zuzugreifen:
telnet host.docker.internal 2345
Ergebnis:
telnet: can't connect to remote host (172.17.0.1): Connection refused
Wenn telnet nicht antwortet, UFW prüfen
Was auch passieren kann, ist, dass telnet sich aufhängt und nach einer sehr langen Zeit mit antwortet:
telnet: can't connect to remote host (172.17.0.1): Connection timed out
In diesem Fall wird die Anfrage von Docker wahrscheinlich von UFW (Uncomplicated Firewall) blockiert. Sie können überprüfen, ob UFW die Anfrage blockiert, indem Sie sich die UFW-Einträge in /var/log/syslog ansehen. Geben Sie in einem anderen Terminalfenster Folgendes ein:
tail -f /var/log/syslog | grep UFW
Starten Sie im Container Docker erneut den Befehl telnet . Wenn UFW Ihre Anfrage blockiert, werden Sie Meldungen wie diese sehen:
Aug 11 09:48:05 myra kernel: [247791.035929] [UFW BLOCK] IN=docker0 OUT= PHYSIN=vethe3b7837 MAC=02:42:80:b6:f4:ea:02:42:ac:11:00:02:08:00 SRC=172.17.0.2 DST=172.17.0.1 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=9274 DF PROTO=TCP SPT=35690 DPT=2345 WINDOW=64240 RES=0x00 SYN URGP=0
Die wichtige Information ist hier:
SRC=172.17.0.2 <--- SOURCE
DST=172.17.0.1 <--- DESTINATION
SPT=35690 <--- SOURCE PORT
DPT=2345 <--- DESTINATION PORT
Beachten Sie, dass der Port 2345 von UFW blockiert wird. Sie können hier eine von zwei Sachen tun:
- Deaktivieren Sie UFW während dieser Tests, oder
- Fügen Sie eine Regel zu UFW hinzu, die den Zugriff auf Port 2345 erlaubt:
sudo ufw allow from 172.17.0.0/16 to any port 2345.
Dies erlaubt Netzwerken 172.17.x.y den Zugriff auf Port 2345.
Für den Moment schlage ich vor, dass Sie UFW deaktivieren. Danach sollte telnet wie zuvor erwähnt reagieren:
telnet: can't connect to remote host (172.17.0.1): Connection refused
Ok, warum wird die Verbindung verweigert?
Hierfür müssen Sie einige Netzwerkgrundlagen verstehen. Der Listener-Dienst sagt, dass er auf 'Local Address' hört: 127.0.0.1:2345. Und telnet im Container Docker sagte: can't connect to remote host (172.17.0.1).
Beachten Sie, dass sich 127.0.0.1 und 172.17.0.1 in unterschiedlichen Netzen befinden. Das bedeutet, dass niemals eine Verbindung hergestellt werden kann!
Aus dem Beitrag 'How to Connect to Localhost Within a Docker Container', siehe Links unten:
Ganz genau. Das bedeutet, dass unser Listener-Dienst nicht nur auf 127.0.0.1 hören sollte, sondern auch auf 172.17.0.1.
Starten wir unseren Listener-Dienst neu und lassen ihn auf 172.17.0.1 hören:
netcat -l 172.17.0.1 2345
Starten Sie telnet erneut im Container Docker :
telnet host.docker.internal 2345
Ergebnis:
Connected to host.docker.internal
Bingo! Das bedeutet, dass der Dienst auf dem Docker -Host auch auf docker0 hören muss. Sie können einen Text eingeben und er wird von netcat wiedergegeben.
Was wir hier auch gelernt haben, ist, dass host.docker.internal NICHT äquivalent zu localhost ist
host.docker.internal ist nur ein Name für docker0. Docker0 ist eine virtuelle Schnittstelle, eine bridge, die von Docker erstellt wurde, und host.docker.internal ist der (Domänen-)Name der docker0 bridge. Alle Docker Container sind standardmäßig mit dem docker0 bridge verbunden. Obwohl der IP address von docker0 oft 172.17.0.1 ist, kann es alles aus dem privaten Bereich sein, der durch RFC 1918 definiert ist. Deshalb sollten wir nicht IP address verwenden, sondern uns auf docker0 als host.docker.internal beziehen.
+-------------+ +-------------+
| container A | | container B |
+-------------+ +-------------+
| |
| |
+------------------------------+
+----| docker0 |---+
| +------------------------------+ |
| |
| Host |
| |
+---------------------------------------+
|
network
Wie kann ich mich von einem Docker -Container aus mit einem Dienst auf localhost verbinden?
Als der Listener-Dienst auf 127.0.0.1 lauschte, konnten wir ihn von unserem Docker-Container aus nicht erreichen. Als der Listener-Dienst jedoch auf 172.17.0.1 lauschte, konnten wir von unserem Docker-Container aus eine Verbindung herstellen.
Mit anderen Worten, die Ursache des Problems ist, dass der Dienst auf dem Host NICHT auf 172.17.0.1 hört.
In Linux können die meisten Dienste so eingestellt werden, dass sie auf alle Schnittstellen und Netzwerke hören, indem wir die Adresse '0.0.0.0' verwenden. Wir können netcat mit oder ohne IP address '0.0.0.0' starten, es ist dasselbe:
netcat -l 2345
oder,
netcat -l 0.0.0.0 2345
Prüfen der abhörenden Ports
sudo netstat -tunlp | grep 2345
Ergebnis:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
...
tcp 0 0 0.0.0.0:2345 0.0.0.0:* LISTEN 1606615/netcat
...
Local Address '0.0.0.0' bedeutet, dass er alle Netze abhört, einschließlich 127.0.0.1 und 172.17.0.1.
Starten Sie telnet im Container:
telnet host.docker.internal 2345
Ergebnis:
Connected to host.docker.internal
Wie erwartet war die Verbindung erfolgreich.
Schön, aber was muss ich tun, um mich von einem Docker -Container aus mit einem Dienst auf localhost zu verbinden?
Es geht darum sicherzustellen, dass der Dienst auf localhost (auch) auf 172.17.0.1 oder docker0 hört. Auf Ubuntu ist der IP address von docker0 172.17.0.1, aber Docker sagt, dass es auch zufällig gewählt werden kann, siehe auch oben.
Das bedeutet, dass ein Neustart von Docker eine andere docker0 IP address ergeben kann, vielleicht nicht heute, aber vielleicht in ein paar Monaten oder im nächsten Jahr(?). Da wir uns nicht auf docker0 IP address verlassen können, besteht die einzige andere Möglichkeit darin, Ihren Dienst auf dem Host auf '0.0.0.0' hören zu lassen. Es ist möglich, die bridge IP address und subnet zu setzen, aber das würde den Rahmen dieses Beitrags sprengen.
Um auf MySQL auf dem Docker -Host von einem Container aus zuzugreifen, können Sie eine Zeile in /etc/mysql/my.cnf ändern:
From:
bind-address = 127.0.0.1
To:
bind-address = 0.0.0.0
Beachten Sie, dass es im Internet Lösungen gibt, die den Dienst auf dem Docker -Host auf den docker0 IP address hören lassen. Sie verlassen sich darauf, dass sich dieser IP address nicht ändern wird. Wenn Sie nicht wissen, was Sie tun, kann ich dies nicht empfehlen.
Erstellen Sie ein separates Docker -Netzwerk für den Dienst auf dem Host
Sie können den Dienst auf dem Host isolieren, indem Sie z. B. ein Docker -Netzwerk für ihn erstellen:
docker network create --driver=bridge --subnet=172.17.33.0/24 --gateway=172.17.33.10 my_subnet
Starten Sie den Dienst auf dem Host:
netcat -k -l 172.17.33.10 2345
Dann starten Sie den Docker Container:
docker run -it --add-host=my_host:172.17.33.10 --network=my_subnet --rm busybox
Und greifen Sie vom Docker -Container aus auf den Dienst zu:
telnet my_host 2345
Zugriff auf einen Dienst auf dem Docker -Host über einen Unix socket
Schließlich gibt es noch eine ganz andere Möglichkeit, auf einen Dienst auf dem Docker -Host zuzugreifen, aber das hängt davon ab, ob der Dienst dies unterstützt. Einige Dienste verwenden einen 'Unix-Socket' oder eine Datei. MySQL verwendet zum Beispiel die Datei:
/run/mysqld/mysqld.sock
Das bedeutet, dass wir auch von einem Container aus auf MySQL zugreifen können, indem wir das Volume Mapping in Docker-Compose verwenden:
volumes:
# connect to mysql via unix socket
- /var/run/mysqld:/var/run/mysqld
Dies funktioniert einwandfrei. Ich habe Anwendungen auf dem Host und Anwendungen im Docker -Container, die alle MariaDb auf dem Host verwenden.
Docker IP addresses und UFW-Regeln
Dies ist wichtig, weil Sie wahrscheinlich UFW verwenden, um den Zugriff auf und von der Maschine zu kontrollieren. Wie oben erwähnt, kann sich die IP address jedes Mal, wenn ein Container erstellt wird, ändern und einige UFW-Regeln funktionieren dann nicht mehr!
Standardmäßig weist Docker einem Container eine IP address aus einem der folgenden Netzwerkbereiche für bridge -Netzwerke zu:
- 172.17.0.0/16
- 172.18.0.0/16
- 172.19.0.0/16
- 172.20.0.0/14
- 172.24.0.0/14
- 172.28.0.0/14
- 192.168.0.0/16
Das bedeutet, wenn wir eine UFW-Regel mit '172.17.0.0/16' spezifiziert haben, wird diese Regel nicht mehr funktionieren, wenn Docker das nächste Mal einen IP address aus dem Netzwerk '192.168.0.0/16' zuweist.
Eine Möglichkeit, dieses Problem zu lösen, besteht darin, die Standard-Adresspools für Docker -Netzwerke selbst festzulegen, wie ich im Beitrag 'Docker containers suddenly using 192.168.0.0/16 instead of 172.17.0.0/16: services lost' beschrieben habe (siehe Links unten).
Wir können Standard-Adresspools für unsere Docker -Netzwerke angeben, indem wir die Datei erstellen (sie war nicht vorhanden):
/etc/docker/daemon.json
In diesem Beispiel wird Docker angewiesen, nur IP address aus dem Netzwerk 172.17.0.0/16 zu verwenden:
{
"default-address-pools":
[
{"base":"172.17.0.0/16","size":24}
]
}
Scannen Sie immer Ihre Ports, nachdem Sie Änderungen vorgenommen haben!
Scannen Sie nach Änderungen immer die Ports auf Ihrem Rechner und dem Produktionsserver. Besonders bei der Verwendung von Docker müssen wir sicherstellen, dass keine Ports versehentlich geöffnet wurden!
Wenn wir dies selbst tun wollen, können wir den Befehl nmap verwenden. Das Problem ist, dass die Ergebnisse davon abhängen, wie Sie sich mit dem Internet verbinden. Sie müssen die firewall auf Ihrem Rechner und die firewall auf Ihrem Internetzugangspunkt überprüfen. Außerdem kann die firewall Ihres Internet-Providers Verbindungen blockieren. Die einzige Möglichkeit, Ihren Produktionsserver auf offene Ports zu überprüfen, ist die Verwendung eines anderen Servers (VPS) im selben Netzwerk oder im Internet, der keine Beschränkungen für ausgehende Verbindungen hat.
sudo apt install nmap
Ein Beispiel für diesen Befehl zum Scannen der Ports 80-10000:
nmap -p 80-10000 www.example.com
Sie können auch den IP address Ihres Entwicklungsrechners verwenden und nmap auf diesem Rechner ausführen, aber ich habe festgestellt, dass die Ergebnisse nicht korrekt sind.
nmap -p 80-10000 <your-development-machine-ip-address>
Es wurde angezeigt, dass Port 0.0.0.0:2345 offen war. Beim Scannen von einem anderen Computer in meinem lokalen Netzwerk wurde dies nicht angezeigt.
Zusammenfassung
In diesem Beitrag habe ich beschrieben, wie man sich von einem Docker -Container aus mit einem Dienst auf einem Docker -Host verbinden kann. Es gibt zwar Einschränkungen, aber in den meisten Fällen ist es möglich. Achten Sie auf die Docker zugewiesenen IP addresses, diese können sich jedes Mal ändern, wenn ein Container erstellt wird. Und überprüfen Sie Ihre Systeme immer auf offene Ports, nachdem Sie Änderungen vorgenommen haben!
Links / Impressum
Change default docker0 bridge ip address
https://stackoverflow.com/questions/52225493/change-default-docker0-bridge-ip-address
Connecting Docker to localhost PostgreSQL on an Ubuntu 18.04 Host
https://gregtczap.com/blog/docker-postgres-ubuntu-localhost
Docker containers suddenly using 192.168.0.0/16 instead of 172.17.0.0/16: services lost
https://www.peterspython.com/en/blog/docker-containers-suddenly-using-192-168-0-0-16-instead-of-172-17-0-0-16-services-lost
From inside of a Docker container, how do I connect to the localhost of the machine?
https://stackoverflow.com/questions/24319662/from-inside-of-a-docker-container-how-do-i-connect-to-the-localhost-of-the-mach
How to Connect to Localhost Within a Docker Container
https://www.howtogeek.com/devops/how-to-connect-to-localhost-within-a-docker-container
What's the `docker0` there?
https://stackoverflow.com/questions/63948879/whats-the-docker0-there
Mehr erfahren
Docker
Neueste
- Verwendung von Ingress für den Zugriff auf RabbitMQ auf einem Microk8s -Cluster
- Einfache Videogalerie mit Flask, Jinja, Bootstrap und JQuery
- Grundlegende Auftragsplanung mit APScheduler
- Ein Datenbankschalter mit HAProxy und der HAProxy Runtime API
- Docker Swarm rolling updates
- Ausblenden der Primärschlüssel der Datenbank UUID Ihrer Webanwendung
Meistgesehen
- PyInstaller und Cython verwenden, um eine ausführbare Python-Datei zu erstellen
- Verkürzung der Seitenreaktionszeiten einer Flask SQLAlchemy Website
- Verwendung von Pythons pyOpenSSL zur Überprüfung von SSL-Zertifikaten, die von einem Host heruntergeladen wurden
- Verbindung zu einem Dienst auf einem Docker -Host von einem Docker -Container aus
- Verwendung von UUIDs anstelle von Integer Autoincrement Primary Keys mit SQLAlchemy und MariaDb
- SQLAlchemy: Verwendung von Cascade Deletes zum Löschen verwandter Objekte