Así que tengo un Nginx que se ejecuta dentro de un contenedor docker, tengo un mysql que se ejecuta en localhost, quiero conectarme a la MySql desde dentro de mi Nginx. El MySql se está ejecutando en localhost y no exponer un puerto al mundo exterior, por lo que su vinculado en localhost, no vinculado a la dirección ip de la máquina.
¿Hay alguna manera de conectarse a este MySql o cualquier otro programa en localhost desde dentro de este contenedor docker?
Esta pregunta es diferente de "Cómo obtener la dirección IP del host docker desde dentro de un contenedor docker" debido al hecho de que la dirección IP del host docker podría ser la IP pública o la IP privada en la red que puede o no puede ser alcanzable desde dentro del contenedor docker (me refiero a la IP pública si está alojado en AWS o algo). Incluso si usted tiene la dirección IP del host docker no significa que usted puede conectarse al host docker desde dentro del contenedor dada esa dirección IP como su red Docker puede ser overlay, host, puente, macvlan, ninguno etc que restringe la alcanzabilidad de esa dirección IP.
Edición: Si está utilizando Docker-for-mac o Docker-for-Windows 18.03+, sólo tiene que conectarse a su servicio mysql utilizando el host host.docker.internal
.
A partir de Docker 18.09.3, esto no funciona en Docker-for-Linux. El 8 de marzo de 2019 se ha enviado una corrección y se espera que se incorpore al código base. Hasta entonces, una solución es utilizar un contenedor como se describe en la respuesta de qoomon.
Utilice --network="host"
en su comando docker run
, entonces 127.0.0.1
en su contenedor docker apuntará a su docker host.
Nota: Este modo sólo funciona en Docker para Linux, según la documentación.
Docker ofrece diferentes modos de red al ejecutar contenedores. Dependiendo del modo que elijas te conectarías a tu base de datos MySQL que se ejecuta en el host docker de forma diferente.
Docker crea un puente llamado docker0
por defecto. Tanto el host docker como los contenedores docker tienen una dirección IP en ese puente.
En el host Docker, escribe sudo ip addr show docker0
tendrás una salida parecida:
[vagrant@docker:~] $ sudo ip addr show docker0
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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
Así que aquí mi docker host tiene la dirección IP 172.17.42.1
en la interfaz de red docker0
.
Ahora inicie un nuevo contenedor y obtenga un shell en él: docker run --rm -it ubuntu:trusty bash
y dentro del contenedor escribe ip addr show eth0
para descubrir cómo está configurada su interfaz de red principal:
root@e77f6a1b3740:/# ip addr show eth0
863: eth0: <BROADCAST,UP,LOWER_UP> 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
Aquí mi contenedor tiene la dirección IP 172.17.1.192
. Ahora mira la tabla de enrutamiento:
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
Así que la dirección IP del host docker 172.17.42.1
se establece como la ruta por defecto y es accesible desde su contenedor.
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
Alternativamente, puede ejecutar un contenedor docker con configuración de red establecida en host
. Dicho contenedor compartirá la pila de red con el host docker y desde el punto de vista del contenedor, localhost
(o 127.0.0.1
) se referirá al host docker.
Ten en cuenta que cualquier puerto abierto en tu contenedor docker se abrirá en el host docker. Y esto sin requerir la opción -p
o -P
docker run
.
Configuración de la IP en mi docker host:
[vagrant@docker:~] $ ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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
y desde un contenedor docker en modo host:
[vagrant@docker:~] $ docker run --rm -it --network=host ubuntu:trusty ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> 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
Como puedes ver, tanto el docker host como el contenedor docker comparten exactamente la misma interfaz de red y como tal tienen la misma dirección IP.
Para acceder a MySQL que se ejecuta en el host docker desde los contenedores en modo puente, es necesario asegurarse de que el servicio MySQL está escuchando conexiones en la dirección IP 172.17.42.1
.
Para ello, asegúrese de que tiene o bien dirección-bind = 172.17.42.1
o dirección-bind = 0.0.0
en su archivo de configuración de MySQL (my.cnf).
Si necesita establecer una variable de entorno con la dirección IP de la puerta de enlace, puede ejecutar el siguiente código en un contenedor :
export DOCKER_HOST_IP=$(route -n | awk '/UG[ \t]/{print $2}')
luego en su aplicación, utilice la variable de entorno DOCKER_HOST_IP
para abrir la conexión a MySQL.
Nota: si utiliza dirección de enlace = 0.0.0.0
su servidor MySQL escuchará conexiones en todas las interfaces de red. Esto significa que su servidor MySQL podría ser alcanzado desde Internet; asegúrese de configurar las reglas del firewall en consecuencia.
Nota 2: si utiliza bind-address = 172.17.42.1
su servidor MySQL no escuchará las conexiones realizadas a 127.0.0.1
. Los procesos que se ejecuten en el host docker que quieran conectarse a MySQL tendrán que utilizar la dirección IP 172.17.42.1
.
Para acceder a MySQL corriendo en el docker host desde contenedores en modo host, puede mantener bind-address = 127.0.0.1
en su configuración de MySQL y todo lo que necesita hacer es conectarse a 127.0.0.1
desde sus contenedores:
[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: Utilice mysql -h 127.0.0.1
y no mysql -h localhost
; de lo contrario el cliente MySQL intentará conectarse utilizando un socket unix.
Esto me funcionó en una pila NGINX/PHP-FPM sin tocar ningún código o red donde la aplicación sólo espera poder conectarse a localhost
.
Montar mysqld.sock
desde el host al interior del contenedor.
Encuentra la ubicación del archivo mysql.sock en el host que ejecuta mysql:
netstat -ln | awk '/mysql(.*)?\Nsock/ { print $9 }'
Monta ese archivo donde se espera en el docker:
docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock
Posibles ubicaciones de 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
No estoy de acuerdo con la respuesta de Thomasleveil.
Hacer que mysql se vincule a 172.17.42.1 evitará que otros programas que usen la base de datos en el host puedan alcanzarla. Esto sólo funcionará si todos sus usuarios de la base de datos son dockerizados.
Hacer que mysql se vincule a 0.0.0.0 abrirá la base de datos al mundo exterior, lo cual no sólo es algo muy malo, sino que también es contrario a lo que el autor de la pregunta original quiere hacer. Él dice explícitamente "El MySql se está ejecutando en localhost y no exponer un puerto al mundo exterior, por lo que su vinculado en localhost"
Para responder al comentario de ivant
"¿Por qué no enlazar mysql a docker0 también?"
Esto no es posible. La documentación de mysql/mariadb dice explícitamente que no es posible enlazar a varias interfaces. Sólo se puede enlazar a 0, 1, o todas las interfaces.
Como conclusión, NO he encontrado ninguna forma de alcanzar la base de datos (sólo localhost) en el host desde un contenedor docker. Definitivamente parece un patrón muy muy común, pero no sé cómo hacerlo.