Wie verbinde ich mich innerhalb eines Docker-Containers mit dem localhost des Rechners?

Ich habe also ein Nginx, das in einem Docker-Container läuft, ich habe ein MySql, das auf localhost läuft, ich möchte mich mit dem MySql von meinem Nginx aus verbinden. Die MySql läuft auf localhost und nicht einen Port für die Außenwelt, so seine gebunden auf localhost, nicht auf die IP-Adresse der Maschine gebunden.

Gibt es eine Möglichkeit, eine Verbindung zu diesem MySql oder ein anderes Programm auf localhost aus diesem Docker-Container?

Diese Frage unterscheidet sich von "Wie erhalte ich die IP-Adresse des Docker-Hosts von innerhalb eines Docker-Containers" aufgrund der Tatsache, dass die IP-Adresse des Docker-Hosts die öffentliche IP oder die private IP im Netzwerk sein könnte, die von innerhalb des Docker-Containers erreichbar sein kann oder nicht (ich meine die öffentliche IP, wenn sie bei AWS oder so gehostet wird). Selbst wenn Sie die IP-Adresse des Docker-Hosts haben, bedeutet dies nicht, dass Sie eine Verbindung zum Docker-Host von innerhalb des Containers mit dieser IP-Adresse herstellen können, da Ihr Docker-Netzwerk Overlay, Host, Bridge, Macvlan, none usw. sein kann, was die Erreichbarkeit dieser IP-Adresse einschränkt.

Lösung

Bearbeiten: Wenn Sie [Docker-for-mac] (https://docs.docker.com/docker-for-mac/networking/#there-is-no-docker0-bridge-on-macos#i-want-to-connect-from-a-container-to-a-service-on-the-host) oder [Docker-for-Windows] (https://docs.docker.com/docker-for-windows/networking/#there-is-no-docker0-bridge-on-windows#i-want-to-connect-from-a-container-to-a-service-on-the-host) 18.03+ verwenden, verbinden Sie sich einfach mit Ihrem mysql-Dienst, indem Sie den Host host.docker.internal verwenden.

Ab Docker 18.09.3 funktioniert dies nicht auf Docker-for-Linux. Ein Fix wurde am 8. März 2019 eingereicht und wird hoffentlich in die Code-Basis integriert werden. Bis dahin besteht eine Abhilfe darin, einen Container wie in qoomons Antwort beschrieben zu verwenden.


TLDR

Verwenden Sie --network="host" in Ihrem docker run Befehl, dann zeigt 127.0.0.1 in Ihrem Docker Container auf Ihren Docker Host.

Hinweis: Dieser Modus funktioniert nur mit Docker für Linux, gemäß der Dokumentation.


Hinweis zu Docker-Container-Netzwerkmodi

Docker bietet [verschiedene Netzwerkmodi] (https://docs.docker.com/engine/reference/run/#network-settings), wenn Container ausgeführt werden. Je nachdem, welchen Modus Sie wählen, würden Sie sich mit Ihrer MySQL-Datenbank, die auf dem Docker-Host läuft, unterschiedlich verbinden.

docker run --network="bridge" (Standard)

Docker erstellt standardmäßig eine Bridge mit dem Namen docker0. Sowohl der Docker-Host als auch die Docker-Container haben eine IP-Adresse auf dieser Bridge.

Wenn Sie auf dem Docker-Host "sudo ip addr show docker0" eingeben, erhalten Sie eine Ausgabe, die wie folgt aussieht:

[vagrant@docker:~] $ sudo ip addr show docker0
4: docker0:  mtu 1500 qdisc noqueue state UP group default
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::5484:7aff:fefe:9799/64 scope link
       valid_lft forever preferred_lft forever

Hier hat mein Docker-Host also die IP-Adresse "172.17.42.1" auf der Netzwerkschnittstelle "docker0".

Starten Sie nun einen neuen Container und rufen Sie eine Shell auf: docker run --rm -it ubuntu:trusty bash und geben Sie innerhalb des Containers ip addr show eth0 ein, um herauszufinden, wie seine Hauptnetzwerkschnittstelle eingerichtet ist:

root@e77f6a1b3740:/# ip addr show eth0
863: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 66:32:13:f0:f1:e3 brd ff:ff:ff:ff:ff:ff
    inet 172.17.1.192/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::6432:13ff:fef0:f1e3/64 scope link
       valid_lft forever preferred_lft forever

Hier hat mein Container die IP-Adresse 172.17.1.192. Schauen Sie sich nun die Routing-Tabelle an:

root@e77f6a1b3740:/# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.42.1     0.0.0.0         UG    0      0        0 eth0
172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

Die IP-Adresse des Docker-Hosts 172.17.42.1 ist also als Standard-Route eingestellt und von Ihrem Container aus erreichbar.

root@e77f6a1b3740:/# ping 172.17.42.1
PING 172.17.42.1 (172.17.42.1) 56(84) bytes of data.
64 bytes from 172.17.42.1: icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from 172.17.42.1: icmp_seq=2 ttl=64 time=0.201 ms
64 bytes from 172.17.42.1: icmp_seq=3 ttl=64 time=0.116 ms

docker run --network="host"

Alternativ können Sie einen Docker-Container mit [Netzwerkeinstellungen auf host gesetzt] (http://docs.docker.com/engine/reference/run/#network-host) ausführen. Ein solcher Container teilt sich den Netzwerkstack mit dem Docker-Host und aus Sicht des Containers bezieht sich localhost (oder 127.0.0.1) auf den Docker-Host.

Seien Sie sich bewusst, dass jeder Port, der in Ihrem Docker-Container geöffnet wird, auch auf dem Docker-Host geöffnet wird. Und das, ohne dass die Option -p oder -P docker run erforderlich ist.

IP-Konfiguration auf meinem Docker-Host:

[vagrant@docker:~] $ ip addr show eth0
2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
       valid_lft forever preferred_lft forever

und von einem Docker-Container im Host-Modus:

[vagrant@docker:~] $ docker run --rm -it --network=host ubuntu:trusty ip addr show eth0
2: eth0:  mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
       valid_lft forever preferred_lft forever

Wie Sie sehen können, teilen sich sowohl der Docker-Host als auch der Docker-Container genau dieselbe Netzwerkschnittstelle und haben somit dieselbe IP-Adresse.


Verbindung zu MySQL von Containern aus

Bridge-Modus

Um von Containern im Bridge-Modus auf MySQL, das auf dem Docker-Host läuft, zuzugreifen, müssen Sie sicherstellen, dass der MySQL-Dienst auf der IP-Adresse 172.17.42.1 auf Verbindungen wartet.

Stellen Sie dazu sicher, dass Sie entweder bind-address = 172.17.42.1 oder bind-address = 0.0.0.0 in Ihrer MySQL-Konfigurationsdatei (my.cnf) haben.

Wenn Sie eine Umgebungsvariable mit der IP-Adresse des Gateways setzen müssen, können Sie den folgenden Code in einem Container ausführen:

export DOCKER_HOST_IP=$(route -n | awk '/UG[ \t]/{print $2}')

verwenden Sie dann in Ihrer Anwendung die Umgebungsvariable DOCKER_HOST_IP, um die Verbindung zu MySQL zu öffnen.

Hinweis: Wenn Sie bind-address = 0.0.0.0 verwenden, wird Ihr MySQL-Server an allen Netzwerkschnittstellen auf Verbindungen lauschen. Das bedeutet, dass Ihr MySQL-Server vom Internet aus erreicht werden kann; stellen Sie sicher, dass Sie Firewall-Regeln entsprechend einrichten.

Anmerkung 2: Wenn Sie bind-address = 172.17.42.1 verwenden, wird Ihr MySQL-Server nicht auf Verbindungen zu 127.0.0.1 lauschen. Prozesse, die auf dem Docker-Host laufen und sich mit MySQL verbinden wollen, müssen die IP-Adresse "172.17.42.1" verwenden.

Host-Modus

Um von Containern im Host-Modus auf MySQL zuzugreifen, die auf dem Docker-Host laufen, können Sie bind-address = 127.0.0.1 in Ihrer MySQL-Konfiguration beibehalten, und alles, was Sie tun müssen, ist, sich von Ihren Containern aus mit 127.0.0.1 zu verbinden:

[vagrant@docker:~] $ docker run --rm -it --network=host mysql mysql -h 127.0.0.1 -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 36
Server version: 5.5.41-0ubuntu0.14.04.1 (Ubuntu)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Anmerkung: Verwenden Sie mysql -h 127.0.0.1 und nicht mysql -h localhost; andernfalls würde der MySQL-Client versuchen, sich über einen Unix-Socket zu verbinden.

Kommentare (28)

Dies funktionierte bei mir auf einem NGINX/PHP-FPM-Stapel, ohne dass ich irgendeinen Code oder ein Netzwerk berührte, bei dem die Anwendung nur erwartet, dass sie sich mit "localhost" verbinden kann

Mount mysqld.sock vom Host in den Container.

Finden Sie den Ort der Datei mysql.sock auf dem Host, auf dem mysql läuft: netstat -ln | awk '/mysql(.*)?\.sock/ { print $9 }'

Hängen Sie diese Datei dort ein, wo sie im Docker erwartet wird: docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock

Mögliche Speicherorte von mysqld.sock:

/tmp/mysqld.sock
/var/run/mysqld/mysqld.sock 
/var/lib/mysql/mysql.sock
/Applications/MAMP/tmp/mysql/mysql.sock # if running via MAMP
Kommentare (7)

Ich bin mit der Antwort von Thomasleveil nicht einverstanden.

Wenn Sie mysql an 172.17.42.1 binden, verhindern Sie, dass andere Programme, die die Datenbank auf dem Host verwenden, sie erreichen können. Dies wird nur funktionieren, wenn alle Ihre Datenbankbenutzer gedockt sind.

Wenn Sie mysql an 0.0.0.0 binden, öffnen Sie die Datenbank für die Außenwelt, was nicht nur eine sehr schlechte Sache ist, sondern auch dem widerspricht, was der Autor der ursprünglichen Frage erreichen möchte. Er sagt ausdrücklich: "MySql läuft auf localhost und gibt keinen Port für die Außenwelt frei, also ist es an localhost gebunden.

Um auf den Kommentar von ivant zu antworten

"Warum nicht auch mysql an docker0 binden?"

Das ist nicht möglich. Die mysql/mariadb-Dokumentation sagt ausdrücklich, dass es nicht möglich ist, an mehrere Schnittstellen zu binden. Sie können nur an 0, 1 oder an alle Schnittstellen binden.

Abschließend kann ich sagen, dass ich KEINE Möglichkeit gefunden habe, die (nur localhost) Datenbank auf dem Host von einem Docker-Container aus zu erreichen. Das scheint auf jeden Fall ein sehr gängiges Muster zu sein, aber ich weiß nicht, wie man es macht.

Kommentare (4)