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

Connexion à un service sur un hôte Docker à partir d'un conteneur Docker

Lorsque vous utilisez le mode de mise en réseau par défaut bridge , vous devez faire en sorte qu'un service de l'hôte Docker écoute (également) docker0.

11 août 2022
Dans Docker
post main image
https://www.pexels.com/@vladbagacian/

Si vous avez un problème avec Docker et que vous effectuez une recherche sur Internet, vous tomberez presque certainement sur la question suivante : Comment puis-je me connecter à localhost ? Ce que les gens veulent dire, c'est : Comment puis-je me connecter à un service sur l'hôte Docker à partir d'un conteneur Docker . Lorsque j'ai commencé à utiliser Docker , j'ai également eu du mal à résoudre ce problème.

J'ai utilisé Linux, Ubuntu, et quand finalement host.docker.internal a été disponible pour Linux beaucoup de gens ont pensé que tous leurs problèmes étaient terminés. Mais rien n'a changé. Le problème reste le même.

Il existe de nombreux articles sur Internet à ce sujet, et celui-ci en est un autre. Je ne parlerai que du mode réseau par défaut bridge , et non du mode 'network=host'.

Outils utilisés

Dans cet article, j'utilise quelques outils en ligne de commande, que vous voudrez peut-être installer. Pour obtenir la dernière version, ou les ajouter au dépôt sur votre machine :

sudo apt update

netcat

Utilisé pour créer un service d'écoute.

sudo apt install netcat

netstat

Utilisé pour afficher les listeners sur l'hôte.

sudo install net-tools

nmap

Utilisé pour scanner (pour les ports ouverts).

sudo apt install nmap

Services d'écoute et mise en réseau

Créons un service d'écoute dans une fenêtre de terminal, pour écouter le port 2345 sur localhost, où localhost est 127.0.0.1. Nous ajoutons l'option -k (keep open), pour empêcher netcat de se terminer lorsque vous vous déconnectez :

netcat -k -l 127.0.0.1 2345

Vérifiez que nous avons un service netcat en écoute :

sudo netstat -tunlp

Ou, si vous avez une très longue liste :

sudo netstat -tunlp | grep 2345

Résultat :

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

Cela signifie que netcat est en écoute sur IP address 127.0.0.1 sur le port 2345.

Démarrez maintenant un conteneur Busybox Docker dans une autre fenêtre de terminal :

docker run -it --add-host=host.docker.internal:host-gateway --rm busybox

Et ensuite, dans le conteneur, essayez d'accéder à notre service d'écoute :

telnet host.docker.internal 2345

Résultat :

telnet: can't connect to remote host (172.17.0.1): Connection refused

Si telnet ne répond pas, vérifiez l'UFW.

Il peut également arriver que telnet se bloque et réponde après un temps très long avec :

telnet: can't connect to remote host (172.17.0.1): Connection timed out

Dans ce cas, la demande de Docker est probablement bloquée par UFW (Uncomplicated Firewall). Vous pouvez vérifier si UFW bloque la demande en regardant les entrées UFW dans /var/log/syslog. Dans une autre fenêtre de terminal, tapez ce qui suit :

tail -f /var/log/syslog | grep UFW

Dans le conteneur Docker , relancez la commande telnet . Si l'UFW bloque votre demande, vous verrez des messages comme :

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

L'information importante ici est :

SRC=172.17.0.2   <--- SOURCE
DST=172.17.0.1   <--- DESTINATION
SPT=35690        <--- SOURCE PORT
DPT=2345         <--- DESTINATION PORT

Notez que le port 2345 est bloqué par UFW. Vous pouvez faire l'une des deux choses suivantes :

  • Désactiver UFW pendant ces tests, ou bien
  • Ajouter une règle à UFW pour autoriser l'accès au port 2345 :
    sudo ufw allow from 172.17.0.0/16 to any port 2345.
    Cela permet aux réseaux 172.17.x.y d'accéder au port 2345.

Pour le moment, je vous suggère de désactiver l'UFW. Après avoir fait cela, telnet devrait répondre comme mentionné précédemment :

telnet: can't connect to remote host (172.17.0.1): Connection refused

Ok, alors pourquoi la connexion est refusée ?

Pour cela, vous devez comprendre quelques principes de base du réseau. Le service d'écoute dit qu'il écoute 'Local Address' : 127.0.0.1:2345. Et telnet dans le conteneur Docker dit : can't connect to remote host (172.17.0.1).

Notez que 127.0.0.1 et 172.17.0.1 sont sur des réseaux différents. Cela signifie qu'une connexion ne peut jamais être établie !

Extrait de l'article 'How to Connect to Localhost Within a Docker Container', voir les liens ci-dessous :

L'un des inconvénients de cette approche est que vous ne pourrez peut-être pas vous connecter aux services qui se lient directement à localhost. Vous devez vous assurer que vos services écoutent les connexions sur votre IP Docker bridge , ainsi que sur localhost et 127.0.0.1. Sinon, vous verrez apparaître une connexion refusée ou des erreurs similaires dans votre conteneur.

Exactement. Cela signifie que notre service d'écoute ne doit pas seulement écouter 127.0.0.1 mais aussi 172.17.0.1.
Redémarrons notre service d'écoute, et faisons en sorte qu'il écoute 172.17.0.1 :

netcat -l 172.17.0.1 2345

Démarrez à nouveau telnet dans le conteneur Docker :

telnet host.docker.internal 2345

Résultat :

Connected to host.docker.internal

Bingo ! Cela signifie que le service sur l'hôte Docker doit également écouter docker0. Vous pouvez taper du texte et il sera répercuté par netcat.

Nous avons également appris que host.docker.internal n'est PAS équivalent à localhost.

host.docker.internal est juste un nom pour docker0. Docker0 est une interface virtuelle, une bridge, créée par Docker, et host.docker.internal est le nom (de domaine) de la docker0 bridge. Tous les conteneurs Docker sont connectés par défaut au docker0 bridge . Bien que la IP address de la docker0 soit souvent la 172.17.0.1, elle peut être n'importe quoi dans la plage privée définie par la RFC 1918. C'est pourquoi il ne faut pas utiliser la IP address mais se référer à la docker0 comme host.docker.internal.

      +-------------+        +-------------+
      | container A |        | container B |
      +-------------+        +-------------+
             |                      |
             |                      |
         +------------------------------+ 
    +----|           docker0            |---+
    |    +------------------------------+   |
    |                                       |
    |                  Host                 |
    |                                       |
    +---------------------------------------+
                         |
                      network
                         

Encore une fois, comment puis-je me connecter à un service sur localhost à partir d'un conteneur Docker ?

Lorsque le service d'écoute était à l'écoute sur 127.0.0.1 , nous ne pouvions pas l'atteindre depuis notre conteneur docker. Mais lorsque le service d'écoute était à l'écoute sur 172.17.0.1 , nous pouvions nous connecter depuis notre conteneur Docker.

En d'autres termes, la cause du problème est que le service sur l'hôte n'écoute PAS sur 172.17.0.1.

Dans Linux, la plupart des services peuvent écouter toutes les interfaces, tous les réseaux, en utilisant l'adresse '0.0.0.0'. Nous pouvons lancer netcat avec ou sans IP address '0.0.0.0', c'est la même chose :

netcat -l 2345

ou,

netcat -l 0.0.0.0 2345

Vérification des ports d'écoute

sudo netstat -tunlp | grep 2345

Résultat :

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' signifie qu'il écoutera tous les réseaux, y compris 127.0.0.1 et 172.17.0.1.

Démarrez telnet dans le conteneur :

telnet host.docker.internal 2345

Résultat :

Connected to host.docker.internal

Comme prévu, la connexion a réussi.

Bien, mais que dois-je faire pour me connecter à un service sur localhost à partir d'un conteneur Docker ?

Il s'agit de s'assurer que le service sur localhost écoute (également) 172.17.0.1, ou docker0. Sur Ubuntu, la IP address de docker0 est 172.17.0.1, mais Docker dit qu'elle peut aussi être choisie au hasard, voir aussi ci-dessus.

Cela signifie que redémarrer Docker peut donner un docker0 IP address différent, peut-être pas aujourd'hui, mais peut-être dans quelques mois, ou l'année prochaine( ?). Comme nous ne pouvons pas nous fier à la docker0 IP address, la seule autre option est de faire en sorte que votre service sur l'hôte écoute la '0.0.0.0'. Il est possible de définir les bridge IP address et subnet, mais cela dépasse le cadre de cet article.

Pour accéder à MySQL sur l'hôte Docker depuis un conteneur, vous pouvez modifier une ligne dans /etc/mysql/my.cnf :
From :

bind-address = 127.0.0.1

A :

bind-address = 0.0.0.0

Notez qu'il existe des solutions sur Internet qui font que le service sur l'hôte Docker écoute sur l'hôte docker0 IP address. Ils comptent sur le fait que cette IP address ne changera pas. A moins que vous ne sachiez ce que vous faites, je ne peux pas vous le recommander.

Créer un réseau Docker distinct pour le service sur l'hôte

Vous pouvez isoler le service sur l'hôte en créant un réseau Docker pour lui, par exemple :

docker network create --driver=bridge --subnet=172.17.33.0/24 --gateway=172.17.33.10 my_subnet

Démarrez le service sur l'hôte :

netcat -k -l 172.17.33.10 2345

Puis démarrez le conteneur Docker :

docker run -it --add-host=my_host:172.17.33.10 --network=my_subnet --rm busybox

Et accéder au service depuis le conteneur Docker :

telnet my_host 2345

Accéder à un service sur l'hôte Docker en utilisant un Unix socket

Enfin, il existe un moyen totalement différent d'accéder à un service sur l'hôte Docker , mais cela dépend de la prise en charge de ce service. Certains services utilisent un 'unix socket' ou un fichier. Par exemple, MySQL utilise le fichier :

/run/mysqld/mysqld.sock

Cela signifie que nous pouvons également accéder à MySQL depuis un conteneur en utilisant le mappage de volume, dans Docker-Compose :

    volumes:
      # connect to mysql via unix socket 
      - /var/run/mysqld:/var/run/mysqld

Cela fonctionne bien. J'ai des applications sur l'hôte et des applications dans le conteneur Docker , toutes utilisant MariaDb sur l'hôte.

Docker IP addresses et règles UFW

Ce point est important car vous utilisez probablement l'UFW pour contrôler l'accès depuis et vers la machine. Comme mentionné ci-dessus, chaque fois qu'un conteneur est créé, la IP address peut changer et certaines règles UFW ne fonctionneront plus !

Par défaut, Docker attribue un IP address à un conteneur provenant de l'une des plages de réseaux suivantes pour les réseaux bridge :

  • 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

Cela signifie que si nous avons spécifié une règle UFW avec '172.17.0.0/16', cette règle ne fonctionnera plus si Docker assigne la fois suivante un IP address du réseau '192.168.0.0/16'.

Une façon de résoudre ce problème est de spécifier nous-mêmes les pools d'adresses par défaut pour les réseaux Docker , comme je l'ai décrit dans le post 'Docker containers suddenly using 192.168.0.0/16 instead of 172.17.0.0/16: services lost', voir les liens ci-dessous.

Nous pouvons spécifier des pools d'adresses par défaut pour nos réseaux Docker en créant le fichier (il n'était pas là) :

/etc/docker/daemon.json

Cet exemple indique à Docker de n'utiliser que IP address du réseau 172.17.0.0/16 :

{
	"default-address-pools":
	[
		{"base":"172.17.0.0/16","size":24}
	]
}

Analysez toujours vos ports après avoir effectué des modifications !

Après avoir effectué des changements, scannez toujours les ports de votre machine et du serveur de production. En particulier, lorsque vous utilisez Docker , vous devez vous assurer qu'aucun port n'a été ouvert par erreur !

Si nous voulons le faire nous-mêmes, nous pouvons utiliser la commande nmap . Le problème est que les résultats dépendent de la façon dont vous vous connectez à Internet. Vous devez vérifier la firewall sur votre machine et la firewall sur votre point d'accès Internet. De plus, le firewall de votre fournisseur d'accès Internet peut bloquer les connexions. La seule façon d'analyser les ports ouverts de votre serveur de production est d'utiliser un autre serveur (VPS) sur le même réseau, ou sur Internet, qui n'a pas de restrictions de sortie.

sudo apt install nmap

Un exemple de cette commande pour scanner les ports 80-10000 :

nmap -p 80-10000 www.example.com

Vous pouvez également utiliser le IP address de votre machine de développement, et exécuter nmap sur cette machine, mais j'ai constaté que les résultats ne sont pas corrects.

nmap -p 80-10000 <your-development-machine-ip-address>

Il a montré que le port 0.0.0.0:2345 était ouvert. L'analyse à partir d'un autre ordinateur de mon réseau local n'a pas montré cela.

Résumé

Dans cet article, j'ai expliqué comment vous pouvez vous connecter à un service sur un hôte Docker à partir d'un conteneur Docker . Il y a des limitations mais la plupart du temps, c'est possible. Faites attention aux Docker assignées, elles peuvent changer à chaque fois qu'un conteneur est créé. Et vérifiez toujours les ports ouverts de vos systèmes après avoir effectué des modifications !

Liens / crédits

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

En savoir plus...

Docker

Laissez un commentaire

Commentez anonymement ou connectez-vous pour commenter.

Commentaires (1)

Laissez une réponse

Répondez de manière anonyme ou connectez-vous pour répondre.

avatar

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