Comment accéder au port hôte à partir du conteneur Docker

281

J'ai un conteneur docker exécutant jenkins. Dans le cadre du processus de génération, j'ai besoin d'accéder à un serveur Web exécuté localement sur la machine hôte. Existe-t-il un moyen d'exposer le serveur Web hôte (qui peut être configuré pour s'exécuter sur un port) au conteneur jenkins?

EDIT: J'exécute Docker nativement sur une machine Linux.

METTRE À JOUR:

En plus de la réponse @larsks ci-dessous, pour obtenir l'adresse IP de l'adresse IP hôte de la machine hôte, je fais ce qui suit:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Tri Nguyen
la source
Utiliser un commentaire car c'est une réponse terrible, mais je pense que vous pouvez généralement y accéder sur 172.17.1.78 - sauf s'il s'agit d'une configuration boot2docker.
CashIsClay
@CashIsClay J'ai essayé cela, et j'ai toujours cette erreurcurl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen
Vous n'avez pas précisé; exécutez-vous boot2docker ou exécutez-vous Docker nativement sous Linux?
larsks
@larsks désolé, je viens de mettre à jour la question - je l'exécute nativement sous Linux.
Tri Nguyen

Réponses:

206

Lorsque vous exécutez Docker en mode natif sur Linux, vous pouvez accéder aux services hôtes à l'aide de l'adresse IP de l' docker0interface. De l'intérieur du conteneur, ce sera votre itinéraire par défaut.

Par exemple, sur mon système:

$ ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::f4d2:49ff:fedd:28a0/64 scope link 
       valid_lft forever preferred_lft forever

Et à l'intérieur d'un conteneur:

# ip route show
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  src 172.17.0.4 

Il est assez facile d'extraire cette adresse IP à l'aide d'un simple script shell:

#!/bin/sh

hostip=$(ip route show | awk '/default/ {print $3}')
echo $hostip

Vous devrez peut-être modifier les iptablesrègles de votre hôte pour autoriser les connexions à partir des conteneurs Docker. Quelque chose comme ça fera l'affaire:

# iptables -A INPUT -i docker0 -j ACCEPT

Cela permettrait d'accéder à tous les ports de l'hôte à partir des conteneurs Docker. Notez que:

  • Les règles iptables sont ordonnées, et cette règle peut ou non faire la bonne chose selon les autres règles qui la précèdent.

  • vous ne pourrez accéder qu'aux services hôtes qui (a) écoutent INADDR_ANY(aka 0.0.0.0) ou qui écoutent explicitement sur l' docker0interface.

larsks
la source
1
Merci. Obtenir l'adresse IP était tout ce dont j'avais besoin, sans modifier les iptables :)
Tri Nguyen
7
Que diriez-vous de Docker pour MAC? AFAIK il n'y a pas de réseau docker0 disponible pour "Docker pour MAC". Dans ce cas, comment puis-je me connecter à l'hôte à partir du conteneur?
Vijay
17
Si vous utilisez Docker pour MAC v 17.06 ou supérieur, utilisez simplement docker.for.mac.localhostau lieu de localhostou 127.0.0.1. Voici le doc .
merito
1
J'ai utilisé le nom d'hôte de mon hôte au lieu d'obtenir l'adresse IP (commande hostname sur l'hôte)
Marek F
313

Pour macOS et Windows

Docker v 18.03 et supérieur (depuis le 21 mars 2018)

Utilisez votre adresse IP interne ou connectez-vous au nom DNS spécial host.docker.internalqui se résoudra en adresse IP interne utilisée par l'hôte.

Prise en charge Linux en attente https://github.com/docker/for-linux/issues/264

MacOS avec les versions antérieures de Docker

Docker pour Mac v 17.12 à v 18.02

Identique à ci-dessus mais à utiliser à la docker.for.mac.host.internalplace.

Docker pour Mac v 17.06 à v 17.11

Identique à ci-dessus mais à utiliser à la docker.for.mac.localhostplace.

Docker pour Mac 17.05 et versions antérieures

Pour accéder à la machine hôte à partir du conteneur Docker, vous devez attacher un alias IP à votre interface réseau. Vous pouvez lier l'adresse IP de votre choix, assurez-vous simplement de ne pas l'utiliser pour autre chose.

sudo ifconfig lo0 alias 123.123.123.123/24

Assurez-vous ensuite que votre serveur écoute l'IP mentionnée ci-dessus ou 0.0.0.0. S'il écoute sur localhost, 127.0.0.1il n'acceptera pas la connexion.

Il vous suffit ensuite de pointer votre conteneur Docker vers cette IP et vous pourrez accéder à la machine hôte!

Pour tester, vous pouvez exécuter quelque chose comme curl -X GET 123.123.123.123:3000 à l'intérieur du conteneur.

L'alias se réinitialise à chaque redémarrage, créez donc un script de démarrage si nécessaire.

Solution et plus de documentation ici: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Janne Annala
la source
J'ai testé cela avec succès. Pas besoin de désactiver le pare
alvaro g
15
À partir du 17.06, sortie en juin 2017, leur recommandation est de se connecter au nom DNS spécial Mac uniquement docker.for.mac.localhostqui se résoudra en adresse IP interne utilisée par l'hôte! ... Je l'ai testé et ça fonctionne! :)
Kyr
Spot on answer pour les utilisateurs de Mac comme moi. Merci. Une chose que je ne comprends pas, c'est pourquoi ip route show | awk '/default/ {print $3}'donner une IP et en docker.for.mac.localhostest une autre.
dmmd
8
changé en host.docker.internal docs.docker.com/docker-for-mac/networking/…
Snowball
1
Recommander l'utilisation host.docker.internaldans le conteneur docker et la configuration 127.0.0.1 host.docker.internaldans le fichier hosts .
junlin
86

Utilisez --net="host"dans votre docker runcommande, puis localhostdans votre conteneur Docker pointera vers votre hôte Docker.

samthebest
la source
15
Ne fonctionnera pas pour ceux qui utilisent Dpcker pour Windows / Docker pour Mac pour autant que je sache, car les conteneurs s'exécutent dans les environnements virtualisés: Hyper-V (Windows) / xhyve (Mac)
ninjaboy
6
CE! Voilà la réponse!
user3751385
15
Pour mémoire: dans Docker Componse, network_mode: "host"
jbarros
Cela ne fonctionne pas pour moi sur Docker pour Mac version 19.03.1 sur le client et le serveur. J'aimerais que ça marche, mais ce n'est pas le cas.
terre battue
1
Même si je suis sur Mac, cela fonctionne lorsque vous voulez communiquer entre deux conteneurs Dockers et que je transforme toutes les applications en image
Docker, cela
28

Solution avec docker-compose: pour accéder au service basé sur l'hôte, vous pouvez utiliser le network_modeparamètre https://docs.docker.com/compose/compose-file/#network_mode

version: '3'
services:
  jenkins:
    network_mode: host

EDIT 2020-04-27: recommandé pour une utilisation uniquement dans un environnement de développement local.

vovan
la source
Alors comment accéder à jenkins? Il semble que la redirection de port ne fonctionne pas si vous utilisez le mode réseau hôte
Jeff Tian
1
c'est une solution très risquée et pas du tout recommandée. nous ne devons PAS ouvrir notre réseau hôte aux conteneurs, sauf si cela est explicitement nécessaire
Fatemeh Majd
12

J'ai créé un conteneur Docker pour faire exactement cela https://github.com/qoomon/docker-host

Vous pouvez ensuite simplement utiliser le nom de conteneur DNS pour accéder au système hôte, par exemple curl http://dockerhost:9200

qoomon
la source
Voilà une solution intelligente. Êtes-vous au courant de quelque chose qui utilise cela avec beaucoup de trafic? Il peut y avoir des frais généraux pour proxy de tout le trafic via ce conteneur.
bcoughlan
3
Oui, cela fonctionne très bien, à peine pas de surcharge, car il fonctionne uniquement sur le périphérique de bouclage
qoomon
11

Nous avons constaté qu'une solution plus simple à toutes ces ordures de réseautage consiste à simplement utiliser la socket de domaine pour le service. Si vous essayez de vous connecter à l'hôte de toute façon, montez simplement la prise en tant que volume et vous êtes sur la bonne voie. Pour postgresql, c'était aussi simple que:

docker run -v /var/run/postgresql:/var/run/postgresql

Ensuite, nous venons de configurer notre connexion à la base de données pour utiliser le socket au lieu du réseau. Littéralement aussi simple que cela.

mlissner
la source
C'est une excellente solution. Bon travail!
un nerd payé le
2
Pour info, nous avons rencontré un gros problème avec ceci: Docker pour Mac ne prend pas en charge les sockets en tant que volumes montés. Cela s'est déroulé jusqu'à ce qu'une personne Mac l'ait essayé. :(
mlissner
Merci! Fonctionné comme un charme sur Linux!
Marcelo Cardoso
7

J'ai exploré les différentes solutions et je trouve que c'est la solution la moins hacky:

  1. Définissez une adresse IP statique pour l'IP de passerelle de pont.
  2. Ajoutez l'IP de la passerelle comme entrée supplémentaire dans la extra_hostsdirective.

Le seul inconvénient est que si vous avez plusieurs réseaux ou projets faisant cela, vous devez vous assurer que leur plage d'adresses IP n'entre pas en conflit.

Voici un exemple de Docker Compose:

version: '2.3'

services:
  redis:
    image: "redis"
    extra_hosts:
      - "dockerhost:172.20.0.1"

networks:
  default:
    ipam:
      driver: default
      config:
      - subnet: 172.20.0.0/16
        gateway: 172.20.0.1

Vous pouvez ensuite accéder aux ports de l'hôte depuis l'intérieur du conteneur en utilisant le nom d'hôte "dockerhost".

bcoughlan
la source
3

Vous pouvez accéder au serveur Web local qui s'exécute sur votre machine hôte de deux manières.

  1. Approche 1 avec IP publique

    Utilisez l'adresse IP publique de la machine hôte pour accéder au serveur Web dans le conteneur Docker Jenkins.

  2. Approche 2 avec le réseau hôte

    Utilisez "--net host" pour ajouter le conteneur Docker Jenkins sur la pile réseau de l'hôte. Les conteneurs qui sont déployés sur la pile de l'hôte ont un accès complet à l'interface hôte. Vous pouvez accéder au serveur Web local dans le conteneur Docker avec une adresse IP privée de la machine hôte.

NETWORK ID          NAME                      DRIVER              SCOPE
b3554ea51ca3        bridge                    bridge              local
2f0d6d6fdd88        host                      host                local
b9c2a4bc23b2        none                      null                local

Démarrez un conteneur avec le réseau hôte Eg: docker run --net host -it ubuntuet exécutez-le ifconfigpour répertorier toutes les adresses IP réseau disponibles accessibles à partir du conteneur Docker.

Par exemple: J'ai démarré un serveur nginx sur ma machine hôte locale et je peux accéder aux URL du site Web nginx à partir du conteneur Docker Ubuntu.

docker run --net host -it ubuntu

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
a604f7af5e36        ubuntu              "/bin/bash"         22 seconds ago      Up 20 seconds                           ubuntu_server

Accès au serveur Web Nginx (exécuté sur la machine hôte locale) à partir du conteneur Docker Ubuntu avec l'adresse IP du réseau privé.

root@linuxkit-025000000001:/# curl 192.168.x.x -I
HTTP/1.1 200 OK
Server: nginx/1.15.10
Date: Tue, 09 Apr 2019 05:12:12 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 Mar 2019 14:04:38 GMT
Connection: keep-alive
ETag: "5c9a3176-264"
Accept-Ranges: bytes
Sreenivasa Reddy
la source
3

Pour docker-composeutiliser la mise en réseau de ponts pour créer un réseau privé entre les conteneurs, la solution acceptée docker0ne fonctionne pas car l'interface de sortie des conteneurs ne l'est pas, docker0mais c'est plutôt un identifiant d'interface généré de manière aléatoire, tel que:

$ ifconfig

br-02d7f5ba5a51: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255

Malheureusement, cet identifiant aléatoire n'est pas prévisible et changera à chaque fois que la composition doit recréer le réseau (par exemple lors d'un redémarrage de l'hôte). Ma solution consiste à créer le réseau privé dans un sous-réseau connu et à configurer iptablespour accepter cette plage:

Composez l'extrait de fichier:

version: "3.7"

services:
  mongodb:
    image: mongo:4.2.2
    networks:
    - mynet
    # rest of service config and other services removed for clarity

networks:
  mynet:
    name: mynet
    ipam:
      driver: default
      config:
      - subnet: "192.168.32.0/20"

Vous pouvez modifier le sous-réseau si votre environnement l'exige. J'ai arbitrairement sélectionné 192.168.32.0/20en utilisant docker network inspectpour voir ce qui était créé par défaut.

Configurer iptables sur l'hôte pour autoriser le sous-réseau privé comme source:

$ iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT

C'est le plus simple possible iptables règle . Vous pouvez souhaiter ajouter d'autres restrictions, par exemple par port de destination. N'oubliez pas de conserver vos règles iptables lorsque vous êtes satisfait qu'ils fonctionnent.

Cette approche a l'avantage d'être répétable et donc automatisable. J'utilise le templatemodule ansible pour déployer mon fichier de composition avec substitution de variables, puis j'utilise les modules iptableset shellpour configurer et conserver les règles de pare-feu, respectivement.

Andy Brown
la source
J'ai vu plusieurs réponses suggérant iptables -A INPUT -i docker0 -j ACCEPT, mais cela ne m'a pas aidé, alors que la iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPTsuggestion ici a résolu mon problème.
Terry Brown
1

C'est une vieille question et a eu beaucoup de réponses, mais aucune de ces réponses ne correspondait assez bien à mon contexte. Dans mon cas, les conteneurs sont très légers et ne contiennent aucun des outils de mise en réseau nécessaires pour extraire l'adresse IP de l'hôte à l'intérieur du conteneur.

De plus, en utilisant le --net="host" approche est une approche très approximative qui n'est pas applicable lorsque l'on veut avoir une configuration de réseau bien isolée avec plusieurs conteneurs.

Donc, mon approche consiste à extraire l'adresse des hôtes du côté de l'hôte, puis à la transmettre au conteneur avec le --add-hostparamètre:

$ docker run --add-host=docker-host:`ip addr show docker0 | grep -Po 'inet \K[\d.]+'` image_name

ou, enregistrez l'adresse IP de l'hôte dans une variable d'environnement et utilisez la variable plus tard:

$ DOCKERIP=`ip addr show docker0 | grep -Po 'inet \K[\d.]+'`
$ docker run --add-host=docker-host:$DOCKERIP image_name

Et puis le docker-hostest ajouté au fichier hosts du conteneur, et vous pouvez l'utiliser dans vos chaînes de connexion à la base de données ou vos URL d'API.

Vendredi
la source
0

Lorsque vous avez deux images docker "déjà" créées et que vous souhaitez mettre deux conteneurs pour communiquer l'un avec l'autre.

Pour cela, vous pouvez facilement exécuter chaque conteneur avec son propre nom et utiliser l'indicateur --link pour permettre la communication entre eux. Cependant, vous n'obtenez pas cela lors de la construction du docker.

Lorsque vous êtes dans un scénario comme moi et que c'est votre

docker build -t "centos7/someApp" someApp/ 

Ça casse quand vous essayez de

curl http://172.17.0.1:localPort/fileIWouldLikeToDownload.tar.gz > dump.tar.gz

et vous êtes bloqué sur "curl / wget" ne renvoyant aucune "route vers l'hôte".

La raison en est la sécurité qui est mise en place par docker qui, par défaut, interdit la communication d'un conteneur vers l'hôte ou d'autres conteneurs exécutés sur votre hôte. C'était assez surprenant pour moi, je dois dire, que vous vous attendriez à ce que l'écosystème de machines docker fonctionnant sur une machine locale puisse parfaitement accéder les uns aux autres sans trop d'obstacles.

L'explication est décrite en détail dans la documentation suivante.

http://www.dedoimedo.com/computers/docker-networking.html

Deux solutions rapides sont données pour vous aider à vous déplacer en réduisant la sécurité du réseau.

L'alternative la plus simple consiste simplement à désactiver le pare-feu ou à tout autoriser. Cela signifie exécuter la commande nécessaire, qui pourrait être systemctl stop firewalld, iptables -F ou équivalent.

J'espère que ces informations vous aideront.

99Sono
la source
3
Tout comme une note, --linkest maintenant obsolète
Mr.Budris
0

Pour moi (Windows 10, Docker Engine v19.03.8), c'était un mélange de https://stackoverflow.com/a/43541732/7924573 et https://stackoverflow.com/a/50866007/7924573 .

  1. changez l'hôte / ip en host.docker.internal
    par exemple: LOGGER_URL = " http: //host.docker.internal: 8085 / log "
  2. définissez le mode_réseau sur pont (si vous souhaitez conserver la redirection de port; si vous n'utilisez pas l' hôte ):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge ou alternativement: utilisez --net="bridge"si vous n'utilisez pas docker-compose (similaire à https://stackoverflow.com/a/48806927/7924573 )
    Comme indiqué dans les réponses précédentes: Cela ne doit être utilisé que dans un environnement de développement local .
    Pour plus d'informations, consultez: https://docs.docker.com/compose/compose-file/#network_mode et https://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
tschomacker
la source