Transférer le port hôte vers le conteneur Docker

167

Est-il possible d'ouvrir des ports d'accès au conteneur Docker par l'hôte? Concrètement, j'ai MongoDB et RabbitMQ en cours d'exécution sur l'hôte et j'aimerais exécuter un processus dans un conteneur Docker pour écouter la file d'attente et (éventuellement) écrire dans la base de données.

Je sais que je peux transférer un port du conteneur vers l'hôte (via l'option -p) et avoir une connexion au monde extérieur (c'est-à-dire Internet) à partir du conteneur Docker, mais j'aimerais ne pas exposer les ports RabbitMQ et MongoDB de l'hôte au monde extérieur.

EDIT: quelques éclaircissements:

Starting Nmap 5.21 ( http://nmap.org ) at 2013-07-22 22:39 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00027s latency).
PORT     STATE SERVICE
6311/tcp open  unknown

joelkuiper@vps20528 ~ % docker run -i -t base /bin/bash
root@f043b4b235a7:/# apt-get install nmap
root@f043b4b235a7:/# nmap 172.16.42.1 -p 6311 # IP found via docker inspect -> gateway

Starting Nmap 6.00 ( http://nmap.org ) at 2013-07-22 20:43 UTC
Nmap scan report for 172.16.42.1
Host is up (0.000060s latency).
PORT     STATE    SERVICE
6311/tcp filtered unknown
MAC Address: E2:69:9C:11:42:65 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 13.31 seconds

J'ai dû faire cette astuce pour obtenir une connexion Internet avec le conteneur: mon pare-feu bloque les connexions réseau du conteneur docker vers l'extérieur

EDIT : Finalement, j'ai créé un pont personnalisé en utilisant la tuyauterie et en faisant écouter les services sur les IP du pont. J'ai choisi cette approche au lieu de laisser MongoDB et RabbitMQ écouter sur le pont docker car cela donne plus de flexibilité.

JoelKuiper
la source

Réponses:

54

Votre hôte docker expose un adaptateur à tous les conteneurs. En supposant que vous êtes sur ubuntu récent, vous pouvez exécuter

ip addr

Cela vous donnera une liste d'adaptateurs réseau, dont l'un ressemblera à quelque chose comme

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 22:23:6b:28:6b:e0 brd ff:ff:ff:ff:ff:ff
inet 172.17.42.1/16 scope global docker0
inet6 fe80::a402:65ff:fe86:bba6/64 scope link
   valid_lft forever preferred_lft forever

Vous devrez dire à rabbit / mongo de se lier à cette IP (172.17.42.1). Après cela, vous devriez pouvoir ouvrir des connexions à 172.17.42.1 à partir de vos conteneurs.

Seldo
la source
36
Comment le conteneur sait-il à quelle adresse IP envoyer des demandes? Je peux coder en dur la valeur (172.17.42.1 ici et sur mon banc d'essai, mais est-ce toujours vrai?), Mais cela semble aller à l'encontre des principes de docker de travailler avec n'importe quel hôte!
JP.
2
@Seldo: Cette interface a-t-elle besoin d'une configuration pour apparaître? J'utilise docker 1.7.1, et je n'ai que loet eth0.
mknecht
8
Est-il possible de faire cela d'une manière ou d'une autre, si l'hôte n'écoute que sur 127.0.0.1?
HansHarhoff
5
"Vous devrez dire à rabbit / mongo de se lier à cette IP (172.17.42.1). Après cela, vous devriez pouvoir ouvrir des connexions à 172.17.42.1 depuis vos conteneurs." Ce serait bien si vous
expliquiez
1
Comme @Novaterata l'a mentionné, quelqu'un peut-il expliquer ce processus?
keskinsaf le
122

Un moyen simple mais relativement peu sûr serait d'utiliser l' --net=hostoption de docker run.

Cette option permet au conteneur d'utiliser la pile réseau de l'hôte. Ensuite, vous pouvez vous connecter aux services exécutés sur l'hôte en utilisant simplement "localhost" comme nom d'hôte.

Ceci est plus facile à configurer car vous n'aurez pas à configurer le service pour accepter les connexions à partir de l'adresse IP de votre conteneur docker, et vous n'aurez pas à indiquer au conteneur docker une adresse IP ou un nom d'hôte spécifique auquel vous connecter, il suffit de un port.

Par exemple, vous pouvez le tester en exécutant la commande suivante, qui suppose que votre image est appelée my_image, que votre image inclut l' telnetutilitaire et que le service auquel vous souhaitez vous connecter est sur le port 25:

docker run --rm -i -t --net=host my_image telnet localhost 25

Si vous envisagez de le faire de cette façon, veuillez consulter la mise en garde concernant la sécurité sur cette page:

https://docs.docker.com/articles/networking/

Ça dit:

--net = host - Indique à Docker de ne pas placer le conteneur à l'intérieur d'une pile réseau distincte. En substance, ce choix indique à Docker de ne pas conteneuriser le réseau du conteneur! Alors que les processus de conteneur seront toujours confinés à leur propre système de fichiers, liste de processus et limites de ressources, une commande rapide ip addr vous montrera que, au niveau du réseau, ils vivent «à l'extérieur» dans l'hôte Docker principal et ont un accès complet à ses interfaces réseau . Notez que cela ne permet pas au conteneur de reconfigurer la pile du réseau hôte - cela nécessiterait --privileged = true - mais cela permet aux processus de conteneur d'ouvrir des ports à faible numéro comme tout autre processus racine. Il permet également au conteneur d'accéder à des services de réseau local comme le D-bus. Cela peut conduire les processus du conteneur à effectuer des actions inattendues telles que le redémarrage de votre ordinateur.

David Grayson
la source
12
Pour toute personne n'utilisant pas docker sous Linux (par exemple en utilisant une certaine virtualisation), cela ne fonctionnera pas, car l'hôte sera la VM contenant, pas le système d'exploitation hôte réel.
Sebastian Graf
13
En particulier, sur MacOS, ce n'est pas possible (sans quelques solutions de contournement): docs.docker.com/docker-for-mac/networking
...
15
Sur MacOS, --net=hostne fonctionne pas pour permettre à votre processus de conteneur de se connecter à votre machine hôte à l'aide de localhost. Au lieu de cela, demandez à votre conteneur de se connecter au nom d'hôte spécial MacOS uniquement docker.for.mac.host.internalau lieu de localhost. Aucun paramètre supplémentaire n'est nécessaire pour docker runque cela fonctionne. Vous pouvez le transmettre en tant que var d'environnement -esi vous souhaitez que votre plate-forme de conteneur reste indépendante. De cette façon, vous pouvez vous connecter à l'hôte nommé dans la variable d'environnement et passer docker.for.mac.host.internalsous MacOS et localhostLinux.
tul
18
le dernier nom d'hôte pour mac est host.docker.internal, voir doc
xysun
Même chose pour Windows docker run --rm -it --net=host postgres bashalorspsql -h host.docker.internal -U postgres
Leo Cavalcante
12

Vous pouvez également créer un tunnel ssh.

docker-compose.yml:

---

version: '2'

services:
  kibana:
    image: "kibana:4.5.1"
    links:
      - elasticsearch
    volumes:
      - ./config/kibana:/opt/kibana/config:ro

  elasticsearch:
    build:
      context: .
      dockerfile: ./docker/Dockerfile.tunnel
    entrypoint: ssh
    command: "-N elasticsearch -L 0.0.0.0:9200:localhost:9200"

docker/Dockerfile.tunnel:

FROM buildpack-deps:jessie

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive \
    apt-get -y install ssh && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY ./config/ssh/id_rsa /root/.ssh/id_rsa
COPY ./config/ssh/config /root/.ssh/config
COPY ./config/ssh/known_hosts /root/.ssh/known_hosts
RUN chmod 600 /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/config && \
    chown $USER:$USER -R /root/.ssh

config/ssh/config:

# Elasticsearch Server
Host elasticsearch
    HostName jump.host.czerasz.com
    User czerasz
    ForwardAgent yes
    IdentityFile ~/.ssh/id_rsa

De cette façon, le elasticsearcha un tunnel vers le serveur avec le service en cours d'exécution (Elasticsearch, MongoDB, PostgreSQL) et expose le port 9200 avec ce service.

Czerasz
la source
8
Vous mettez essentiellement la clé privée dans l'image Docker. Les secrets ne doivent jamais entrer dans une image Docker.
Teoh Han Hui
2
C'est la seule solution saine utilisable à ce jour.
helvete
5

J'ai eu un problème similaire pour accéder à un serveur LDAP à partir d'un conteneur docker. J'ai défini une adresse IP fixe pour le conteneur et ajouté une règle de pare-feu.

docker-compose.yml:

version: '2'
services:
  containerName:
    image: dockerImageName:latest
    extra_hosts:
      - "dockerhost:192.168.50.1"
    networks:
      my_net:
        ipv4_address: 192.168.50.2
networks:
  my_net:
    ipam:
      config:
      - subnet: 192.168.50.0/24

Règle iptables:

iptables -A INPUT -j ACCEPT -p tcp -s 192.168.50.2 -d $192.168.50.1 --dport portnumberOnHost

À l'intérieur de l'accès au conteneur dockerhost:portnumberOnHost

Arigion
la source
3

Si MongoDB et RabbitMQ sont en cours d'exécution sur l'hôte, le port devrait déjà être exposé car il ne se trouve pas dans Docker.

Vous n'avez pas besoin de l' -poption pour exposer les ports d'un conteneur à un hôte. Par défaut, tous les ports sont exposés. L' -poption vous permet d'exposer un port du conteneur à l'extérieur de l'hôte.

Donc, je suppose que vous n'en avez pas du tout besoin -pet que cela devrait fonctionner correctement :)

craquer
la source
1
Je le savais, mais il me semble qu'il me manque un peu d'informations: voir la récente modification, car je ne parviens pas à atteindre les ports de l'hôte.
JoelKuiper
2
Vous devez configurer rabbitmq et mongodb pour écouter également sur le pont et pas seulement sur votre interface réseau principale.
creack
13
@creack comment faire écouter rabbitmq et mongodb sur le pont?
Ryan Walls