angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

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.

11 August 2022
In Docker
post main image
https://www.pexels.com/@vladbagacian/

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:

Ein Nachteil dieses Ansatzes ist, dass Sie möglicherweise keine Verbindung zu Diensten herstellen können, die direkt an localhost gebunden sind. Sie müssen sicherstellen, dass Ihre Dienste auf Ihrer Docker bridge IP auf Verbindungen warten, ebenso wie auf localhost und 127.0.0.1. Andernfalls werden Sie in Ihrem Container "connection refused" oder ähnliche Fehler sehen.

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

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare (2)

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.

avatar

Спасибо! Мне очень помогла статья

avatar

thanks, only solution that actually worked!