Dall'interno di un contenitore Docker, come faccio a connettermi al localhost della macchina?

Quindi ho un Nginx in esecuzione all'interno di un contenitore docker, ho un mysql in esecuzione su localhost, voglio connettermi al MySql dall'interno del mio Nginx. Il MySql è in esecuzione su localhost e non espone una porta al mondo esterno, quindi è legato a localhost, non legato all'indirizzo ip della macchina.

C'è un modo per connettersi a questo MySql o a qualsiasi altro programma su localhost dall'interno di questo contenitore docker?

Questa domanda è diversa da "Come ottenere l'indirizzo IP dell'host docker dall'interno di un contenitore docker" a causa del fatto che l'indirizzo IP dell'host docker potrebbe essere l'IP pubblico o l'IP privato della rete che potrebbe o meno essere raggiungibile dall'interno del contenitore docker (intendo IP pubblico se ospitato su AWS o qualcosa del genere). Anche se hai l'indirizzo IP dell'host docker non significa che puoi connetterti all'host docker dall'interno del contenitore dato quell'indirizzo IP poiché la tua rete Docker potrebbe essere overlay, host, bridge, macvlan, nessuno ecc che limita la raggiungibilità di quell'indirizzo IP.

Soluzione

Modifica: Se state usando Docker-for-mac o Docker-for-Windows 18.03+, connettetevi semplicemente al vostro servizio mysql usando l'host host.docker.internal.

A partire da Docker 18.09.3, questo non funziona su Docker-for-Linux. Una correzione è stata presentata l'8 marzo 2019 e si spera che venga unita alla base del codice. Fino ad allora, un workaround è quello di utilizzare un contenitore come descritto in qoomon'risposta.


TLDR

Usa --network="host" nel tuo comando docker run, quindi 127.0.0.1 nel tuo contenitore docker punterà al tuo host docker.

Nota: Questa modalità funziona solo su Docker per Linux, secondo la documentazione.


Nota sulle modalità di rete dei container docker

Docker offre diverse modalità di rete quando si eseguono i container. A seconda della modalità scelta ci si connette al database MySQL in esecuzione sull'host docker in modo diverso.

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

Docker crea un bridge chiamato docker0 per impostazione predefinita. Sia l'host docker che i contenitori docker hanno un indirizzo IP su quel ponte.

Sull'host Docker, digitate sudo ip addr show docker0 e avrete un output simile a questo:

[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

Quindi qui il mio host docker ha l'indirizzo IP 172.17.42.1 sull'interfaccia di rete docker0.

Ora avvia un nuovo contenitore e crea una shell su di esso: docker run --rm -it ubuntu:trusty bash e all'interno del contenitore digitate ip addr show eth0 per scoprire come è impostata la sua interfaccia di rete principale:

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

Qui il mio contenitore ha l'indirizzo IP 172.17.1.192. Ora guarda la tabella di routing:

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

Quindi l'indirizzo IP dell'host docker 172.17.42.1 è impostato come percorso predefinito ed è accessibile dal tuo contenitore.

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"

In alternativa si può eseguire un contenitore docker con impostazioni di rete impostate su host. Tale contenitore condividerà lo stack di rete con l'host docker e dal punto di vista del contenitore, localhost (o 127.0.0.1) si riferirà all'host docker.

Siate consapevoli che qualsiasi porta aperta nel vostro contenitore docker verrebbe aperta sull'host docker. E questo senza richiedere l'opzione -p o -P docker run.

Configurazione IP sul mio host docker:

[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

e da un contenitore docker in modalità host:

[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

Come potete vedere sia l'host docker che il contenitore docker condividono la stessa identica interfaccia di rete e come tale hanno lo stesso indirizzo IP.


Connessione a MySQL dai container

modalità bridge

Per accedere a MySQL in esecuzione sull'host docker dai container in modalità ponte, è necessario assicurarsi che il servizio MySQL sia in ascolto per le connessioni sull'indirizzo IP 172.17.42.1.

Per farlo, assicurati di avere o bind-address = 172.17.42.1 o bind-address = 0.0.0.0 nel tuo file di configurazione di MySQL (my.cnf).

Se hai bisogno di impostare una variabile d'ambiente con l'indirizzo IP del gateway, puoi eseguire il seguente codice in un contenitore :

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

poi nella tua applicazione, usa la variabile d'ambiente DOCKER_HOST_IP per aprire la connessione a MySQL.

Nota: se usi bind-address = 0.0.0.0 il tuo server MySQL ascolterà le connessioni su tutte le interfacce di rete. Questo significa che il tuo server MySQL potrebbe essere raggiunto da Internet; assicurati di impostare le regole del firewall di conseguenza.

Nota 2: se usi bind-address = 172.17.42.1 il tuo server MySQL non ascolterà le connessioni fatte a 127.0.0.1. I processi in esecuzione sull'host docker che vorrebbero connettersi a MySQL dovrebbero usare l'indirizzo IP 172.17.42.1.

modalità host

Per accedere a MySQL in esecuzione sull'host docker dai container in modalità host, puoi mantenere bind-address = 127.0.0.1 nella tua configurazione di MySQL e tutto quello che devi fare è connetterti a 127.0.0.1 dai tuoi container:

[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>

nota: Usare mysql -h 127.0.0.1 e non mysql -h localhost; altrimenti il client MySQL cercherà di connettersi usando un socket unix.

Commentari (28)

Questo ha funzionato per me su uno stack NGINX/PHP-FPM senza toccare alcun codice o rete dove l'applicazione si aspetta solo di potersi connettere a localhost.

Montare mysqld.sock dall'host all'interno del contenitore.

Trova la posizione del file mysql.sock sull'host che esegue mysql: netstat -ln | awk '/mysql(.*)?\.sock/ { print $9'

Montare quel file dove ci si aspetta di trovarlo nel docker: docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock.

Possibili posizioni di 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
Commentari (7)

Non sono d'accordo con la risposta di Thomasleveil.

Fare il bind di mysql a 172.17.42.1 impedirà ad altri programmi che usano il database sull'host di raggiungerlo. Questo funzionerà solo se tutti gli utenti del tuo database sono dockerizzati.

Fare mysql bind a 0.0.0.0 aprirà il db al mondo esterno, che non è solo una cosa molto brutta da fare, ma anche contraria a ciò che l'autore della domanda originale vuole fare. Dice esplicitamente "Il MySql è in esecuzione su localhost e non espone una porta al mondo esterno, quindi è legato su localhost"

Per rispondere al commento di ivant

" "Perché non legare mysql anche a docker0?

Questo non è possibile. La documentazione di mysql/mariadb dice esplicitamente che non è possibile legarsi a più interfacce. È possibile solo legarsi a 0, 1, o a tutte le interfacce.

Come conclusione, NON ho trovato alcun modo per raggiungere il database (solo localhost) sull'host da un contenitore docker. Questo sembra sicuramente un modello molto molto comune, ma non so come farlo.

Commentari (4)