Depuis l'intérieur d'un conteneur Docker, comment puis-je me connecter à l'hôte local de la machine ?

J'ai donc un Nginx fonctionnant dans un conteneur docker, j'ai un MySql fonctionnant sur localhost, je veux me connecter au MySql depuis mon Nginx. Le MySql fonctionne sur localhost et n'expose pas de port au monde extérieur, il est donc lié à localhost et non à l'adresse IP de la machine.

Existe-t-il un moyen de se connecter à ce MySql ou à tout autre programme sur localhost depuis ce conteneur docker ?

Cette question est différente de "Comment obtenir l'adresse IP de l'hôte du docker depuis l'intérieur d'un conteneur de docker&quot ; car l'adresse IP de l'hôte du docker peut être l'IP publique ou l'IP privée du réseau qui peut ou non être accessible depuis le conteneur de docker (je veux dire l'IP publique si elle est hébergée chez AWS ou autre). Même si vous avez l'adresse IP de l'hôte Docker, cela ne signifie pas que vous pouvez vous connecter à l'hôte Docker depuis le conteneur avec cette adresse IP, car votre réseau Docker peut être superposé, hôte, pont, macvlan, aucun, etc., ce qui limite l'accessibilité de cette adresse IP.

Solution

Edit: Si vous utilisez Docker-for-mac ou Docker-for-Windows 18.03+, connectez-vous simplement à votre service mysql en utilisant l'hôte host.docker.internal.

A partir de Docker 18.09.3, cela ne fonctionne pas sur Docker-for-Linux. Un fix a été soumis le 8 mars 2019 et sera, nous l'espérons, intégré à la base de code. En attendant, une solution de contournement consiste à utiliser un conteneur comme décrit dans la réponse de qoomon'1.


TLDR

Utilisez --network="host" dans votre commande docker run, alors 127.0.0.1 dans votre conteneur docker pointera vers votre hôte docker.

Remarque : Ce mode ne fonctionne que sur Docker pour Linux, [selon la documentation][2].


Note sur les modes de mise en réseau des conteneurs Docker

Docker propose [différents modes de mise en réseau] (https://docs.docker.com/engine/reference/run/#network-settings) pour l'exécution des conteneurs. En fonction du mode choisi, vous vous connecterez différemment à votre base de données MySQL exécutée sur l'hôte Docker.

docker run --network="bridge&quot ; (par défaut)

Docker crée un pont nommé docker0 par défaut. L'hôte docker et les conteneurs docker ont tous deux une adresse IP sur ce pont.

Sur l'hôte Docker, tapez sudo ip addr show docker0, vous obtiendrez une sortie ressemblant à ceci :

[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

Donc ici mon hôte Docker a l'adresse IP 172.17.42.1 sur l'interface réseau docker0.

Maintenant, démarrez un nouveau conteneur et obtenez un shell sur celui-ci : docker run --rm -it ubuntu:trusty bash et dans le conteneur tapez ip addr show eth0 pour découvrir comment son interface réseau principale est configurée :

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

Ici, mon conteneur a l'adresse IP 172.17.1.192. Maintenant regardez la table de routage :

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

Donc l'adresse IP de l'hôte docker 172.17.42.1 est définie comme route par défaut et est accessible depuis votre conteneur.

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&quot ;

Vous pouvez également exécuter un conteneur docker avec [les paramètres réseau définis sur host] (http://docs.docker.com/engine/reference/run/#network-host). Un tel conteneur partagera la pile réseau avec l'hôte de docker et du point de vue du conteneur, localhost (ou 127.0.0.1) fera référence à l'hôte de docker.

Soyez conscient que tout port ouvert dans votre conteneur docker sera ouvert sur l'hôte docker. Et ceci sans avoir besoin de l'option [-p ou -P docker run] (https://docs.docker.com/engine/reference/run/#expose-incoming-ports).

Configuration IP sur mon hôte 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

et depuis un conteneur docker en mode hôte :

[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

Comme vous pouvez le constater, l'hôte docker et le conteneur docker partagent exactement la même interface réseau et ont donc la même adresse IP.


Connexion à MySQL à partir de conteneurs

mode pont

Pour accéder à MySQL fonctionnant sur l'hôte docker à partir de conteneurs en mode bridge, vous devez vous assurer que le service MySQL écoute les connexions sur l'adresse IP 172.17.42.1.

Pour ce faire, assurez-vous que vous avez soit bind-address = 172.17.42.1, soit bind-address = 0.0.0.0 dans votre fichier de configuration MySQL (my.cnf).

Si vous avez besoin de définir une variable d'environnement avec l'adresse IP de la passerelle, vous pouvez exécuter le code suivant dans un conteneur :

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

puis dans votre application, utilisez la variable d'environnement DOCKER_HOST_IP pour ouvrir la connexion à MySQL.

Note: si vous utilisez bind-address = 0.0.0.0 votre serveur MySQL écoutera les connexions sur toutes les interfaces réseau. Cela signifie que votre serveur MySQL peut être atteint depuis l'Internet ; assurez-vous de configurer les règles du pare-feu en conséquence.

Note 2: si vous utilisez bind-address = 172.17.42.1 votre serveur MySQL n'écoutera pas les connexions faites sur 127.0.0.1. Les processus exécutés sur l'hôte Docker qui voudraient se connecter à MySQL devraient utiliser l'adresse IP 172.17.42.1.

mode hôte

Pour accéder à MySQL fonctionnant sur l'hôte docker à partir de conteneurs en mode hôte, vous pouvez conserver bind-address = 127.0.0.1 dans votre configuration MySQL et tout ce que vous avez à faire est de vous connecter à 127.0.0.1 depuis vos conteneurs :

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

note: Utilisez mysql -h 127.0.0.1 et non mysql -h localhost ; sinon le client MySQL essaiera de se connecter en utilisant un socket unix.

[2] : https://docs.docker.com/network/host/

Commentaires (28)

Cela a fonctionné pour moi sur une pile NGINX/PHP-FPM sans toucher au code ou au réseau où l'application s'attend simplement à pouvoir se connecter à localhost.

Montez mysqld.sock de l'hôte à l'intérieur du conteneur.

Trouvez l'emplacement du fichier mysql.sock sur l'hôte exécutant mysql : `netstat -ln | awk '/mysql(.*)?.sock/ { print $9 }'``

Montez ce fichier à l'endroit où il est attendu dans le docker : docker run -v /hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock

Emplacements possibles 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
Commentaires (7)

Je ne suis pas d'accord avec la réponse de Thomasleveil.

Le fait de lier mysql à 172.17.42.1 empêchera les autres programmes utilisant la base de données sur l'hôte de l'atteindre. Cela ne fonctionnera que si tous les utilisateurs de votre base de données sont dockerisés.

Lier mysql à 0.0.0.0 ouvrira la base de données au monde extérieur, ce qui est non seulement une très mauvaise chose à faire, mais aussi contraire à ce que l'auteur de la question originale veut faire. Il dit explicitement : "Le MySql fonctionne sur localhost et n'expose pas de port au monde extérieur, il est donc lié à localhost" ;

Pour répondre au commentaire d'ivant

"Pourquoi ne pas lier mysql à docker0 également?&quot ;

Cela n'est pas possible. La documentation mysql/mariadb indique explicitement qu'il n'est pas possible de se lier à plusieurs interfaces. Vous ne pouvez vous lier qu'à 0, 1 ou toutes les interfaces.

En conclusion, je n'ai PAS trouvé de moyen d'atteindre la base de données (uniquement localhost) sur l'hôte à partir d'un conteneur docker. Cela semble être un modèle très très courant, mais je ne sais pas comment le faire.

Commentaires (4)