Exposer un port sur un conteneur Docker

j'essaie de créer un conteneur Docker qui agit comme une machine virtuelle complète. Je sais que je peux utiliser L'instruction exposer à l'intérieur d'un fichier Dockerfile pour exposer un port, et je peux utiliser le drapeau -p avec docker run pour assigner des ports, mais une fois qu'un conteneur est réellement en cours d'exécution, y a-t-il une commande pour ouvrir/map des ports supplémentaires en direct?

Par exemple, disons que j'ai un Panneau contenant sshd. Quelqu'un d'autre utilise le conteneur ssh et installe httpd. Y a-t-il un moyen d'exposer le port 80 sur le conteneur et de le faire correspondre au port 8080 sur l'hôte, de sorte que les gens puissent visiter le serveur web qui tourne dans le conteneur, sans le redémarrer?

330
demandé sur Peter Mortensen 2013-11-11 06:00:56

13 réponses

vous ne pouvez pas faire cela via Docker, mais vous pouvez accéder au port non-exposé du conteneur depuis la machine hôte.

si vous avez un conteneur qui fonctionne avec quelque chose sur son port 8000, vous pouvez exécuter ""

wget http://container_ip:8000

pour obtenir l'adresse ip des conteneurs, lancez les 2 commandes:

docker ps

docker inspect container_name | grep IPAddress

en interne, le Docker se décompose pour appeler iptables lorsque vous exécutez une image, donc peut-être qu'une variation sur ceci fonctionnera.

pour exposer le port du conteneur 8000 sur votre port local 8001:

 iptables -t nat -A  DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000

une façon de résoudre ce problème, est de configurer un autre conteneur avec le mappage du port que vous voulez, et de comparer la sortie de la commande iptables-save (bien que, j'ai dû supprimer certaines des autres options qui forcent le trafic à passer par le mandataire docker).

NOTE: c'est subvertir docker, donc devrait être fait avec le conscience qu'il peut bien créer la fumée bleue

ou

une autre alternative, est de regarder le (nouveau? post 0.6.6?)- P option - qui utilisera des ports d'hôte aléatoires,puis les câblera.

ou

avec 0.6.5, vous pouvez utiliser la fonctionnalité LINKs pour faire apparaître un nouveau conteneur qui parle à l'existant, avec un Relay supplémentaire à ce conteneurs-drapeaux p? (Je n'ai pas utilisé des Liens encore)

ou

avec docker 0.11? vous pouvez utiliser docker run --net host .. pour attacher votre conteneur directement aux interfaces réseau de l'hôte (c'est-à-dire que net n'est pas espacé par un nom) et ainsi tous les ports que vous ouvrez dans le conteneur sont exposés.

281
répondu SvenDowideit 2015-12-27 23:25:42

voilà ce que je ferais:

  • Commettre le live conteneur.
  • exécuter le conteneur à nouveau avec la nouvelle image, avec les ports ouverts (je recommande le montage d'un volume partagé et l'ouverture du port ssh ainsi)
sudo docker ps 
sudo docker commit <containerid> <foo/live>
sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash
123
répondu bosky101 2015-07-22 18:24:52

Voici une autre idée. Utilisez SSH pour faire la redirection du port; cela a l'avantage de fonctionner aussi dans OS X (et probablement Windows) lorsque votre hôte Docker est une VM.

docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>
31
répondu Scott Carlson 2017-03-12 14:25:02

les hacks IPtables ne fonctionnent pas, du moins sur Docker 1.4.1.

la meilleure façon serait de faire fonctionner un autre conteneur avec le port exposé et de relayer avec socat. C'est ce que j'ai fait pour (Temporairement) me connecter à la base de données avec SQLPlus:

docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus

Dockerfile:

FROM debian:7

RUN apt-get update && \
    apt-get -y install socat && \
    apt-get clean

USER nobody

CMD socat -dddd TCP-LISTEN:1521,reuseaddr,fork TCP:db:1521
25
répondu Ricardo Branco 2015-03-20 16:32:51

bien que vous ne pouvez pas exposer un nouveau port d'un conteneur existant , vous pouvez commencer un nouveau conteneur dans le même réseau de dockers et de l'obtenir pour acheminer le trafic vers le conteneur d'origine.

# docker run \
  --rm \
  -p $PORT:1234 \
  verb/socat \
    TCP-LISTEN:1234,fork \
    TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT

Exemple Travaillé

lancer un web-service qui écoute sur le port 80, mais ne pas exposer son port 80 interne (oops!):

# docker run -ti mkodockx/docker-pastebin   # Forgot to expose PORT 80!

trouver son réseau Docker IP:

# docker inspect 63256f72142a | grep IPAddress
                    "IPAddress": "172.17.0.2",

lancer verb/socat avec le port 8080 exposé, et l'amener à transmettre le trafic TCP au port 80 de cette IP:

# docker run --rm -p 8080:1234 verb/socat TCP-LISTEN:1234,fork TCP-CONNECT:172.17.0.2:80

vous pouvez maintenant accéder à pastebin sur http://localhost:8080 / , et vos demandes vont à socat:1234 qui la transmet à pastebin:80 , et la réponse parcourt le même chemin à l'envers.

23
répondu RobM 2017-02-06 15:37:02

j'ai dû faire face à ce même problème et j'ai pu le résoudre sans arrêter aucun de mes conteneurs en marche. Il s'agit d'une solution à jour à partir de février 2016, en utilisant Docker 1.9.1. Quoi qu'il en soit, cette réponse est une version détaillée de la réponse de @ricardo-branco, mais plus en profondeur pour les nouveaux utilisateurs.

dans mon scénario, j'ai voulu me connecter temporairement à MySQL en cours d'exécution dans un conteneur, et comme d'autres conteneurs d'application y sont liés, l'arrêt, la reconfiguration, et relancer le conteneur de la base de données a été un échec.

comme j'aimerais accéder à la base de données MySQL en externe (à partir de Sequel Pro via SSH tunneling), je vais utiliser le port 33306 sur la machine hôte. (Pas 3306 , juste au cas où il y aurait une instance MySQL externe.)

Environ une heure de peaufinage iptables révélées vaines, même si:

étape par Étape, voici ce que j'ai fait:

mkdir db-expose-33306
cd db-expose-33306
vim Dockerfile

Edit dockerfile , en plaçant ceci à l'intérieur:

# Exposes port 3306 on linked "db" container, to be accessible at host:33306
FROM ubuntu:latest # (Recommended to use the same base as the DB container)

RUN apt-get update && \
    apt-get -y install socat && \
    apt-get clean

USER nobody
EXPOSE 33306

CMD socat -dddd TCP-LISTEN:33306,reuseaddr,fork TCP:db:3306

puis construisez l'image:

docker build -t your-namespace/db-expose-33306 .

ensuite, lancez-le, en le reliant à votre conteneur en cours d'exécution. (Utilisez -d au lieu de -rm pour le garder en arrière-plan jusqu'à ce qu'il soit explicitement arrêté et supprimé. Je veux qu'il tourne temporairement dans ce cas.)

docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306  your-namespace/db-expose-33306
7
répondu Excalibur 2017-03-12 14:53:34

pour ajouter à la solution réponse acceptée iptables , j'ai dû exécuter deux commandes supplémentaires sur l'hôte pour l'ouvrir au monde extérieur.

HOST> iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.17.0.2:443
HOST> iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source 172.17.0.2 --destination 172.17.0.2 --dport https
HOST> iptables -A DOCKER -j ACCEPT -p tcp --destination 172.17.0.2 --dport https

Note: j'ouvrais port https (443), mon IP interne docker était 172.17.0.2

Note 2: ces règles et temporair es ne dureront que jusqu'à ce que le conteneur soit redémarré "151990920

4
répondu dstj 2017-09-06 20:20:09

vous pouvez utiliser SSH pour créer un tunnel et exposer votre conteneur dans votre hôte.

vous pouvez le faire dans les deux sens, du conteneur à l'hôte et de l'hôte au conteneur. Mais vous avez besoin d'un outil SSH comme OpenSSH dans les deux (client dans l'un et serveur dans l'autre).

Par exemple, dans le conteneur, vous pouvez faire

$ yum install -y openssh openssh-server.x86_64
service sshd restart
Stopping sshd:                                             [FAILED]
Generating SSH2 RSA host key:                              [  OK  ]
Generating SSH1 RSA host key:                              [  OK  ]
Generating SSH2 DSA host key:                              [  OK  ]
Starting sshd:                                             [  OK  ]
$ passwd # You need to set a root password..

vous pouvez trouver l'adresse IP du conteneur à partir de cette ligne (dans le conteneur):

$ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*//g'
172.17.0.2

alors dans l'hôte, vous pouvez juste faire:

sudo ssh -NfL 80:0.0.0.0:80 root@172.17.0.2
2
répondu ton 2017-03-12 14:27:29

vous pouvez utiliser un réseau de superposition comme Weave Net , qui assignera une adresse IP unique à chaque conteneur et exposera implicitement tous les ports à chaque partie conteneur du réseau.

Tisser fournit également 151960920" réseau de l'hôte de l'intégration . Il est désactivé par défaut mais, si vous voulez également accéder aux adresses IP du conteneur (et tous ses ports) à partir de l'hôte, vous pouvez exécuter simplement weave expose .

divulgation Complète: je travaille à Weaveworks.

2
répondu fons 2017-03-12 14:29:13

il y a un emballage HAProxy pratique.

docker run -it -p LOCALPORT:PROXYPORT --rm --link TARGET_CONTAINER:EZNAME -e "BACKEND_HOST=EZNAME" -e "BACKEND_PORT=PROXYPORT" demandbase/docker-tcp-proxy

cela crée un HAProxy au conteneur cible. facile comme bonjour.

1
répondu kwerle 2017-08-08 23:37:38

Lire Ricardo 's réponse en premier. Cela a fonctionné pour moi.

cependant, il existe un scénario où cela ne fonctionnera pas si le conteneur en cours d'exécution a été lancé en utilisant docker-composer. C'est parce que docker-composer (j'exécute docker 1.17) crée un nouveau réseau. La façon d'aborder ce scénario serait

docker network ls

ajouter le texte suivant docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name

1
répondu ice.nicer 2017-08-18 15:06:12

il n'est pas possible de cartographier les ports en direct, mais il y a plusieurs façons de donner à un conteneur Docker ce qui équivaut à une véritable interface comme une machine virtuelle.

Macvlan Interfaces

Docker comprend maintenant un Macvlan pilote de réseau . Cela fixe un réseau Docker à une interface "monde réel" et vous permet d'assigner que les adresses de réseaux directement au conteneur (comme une machine virtuelle mode.)

docker network create \
    -d macvlan \
    --subnet=172.16.86.0/24 \
    --gateway=172.16.86.1  \
    -o parent=eth0 pub_net

pipework peut aussi carte une réelle interface dans un conteneur ou configuration d'une sous-interface dans les anciennes versions de Docker.

Routage IP

si vous avez le contrôle du réseau vous pouvez acheminer des réseaux supplémentaires à votre Docker hôte pour une utilisation dans les conteneurs.

puis vous assignez ce réseau à la conteneurs et configurer votre hôte Docker pour acheminer les paquets via le réseau docker.

Partagée d'accueil de l'interface

l'option --net host permet de partager l'interface hôte dans un conteneur, mais ce n'est probablement pas une bonne configuration pour exécuter plusieurs conteneurs sur le même hôte en raison de la nature partagée.

0
répondu Matt 2017-05-23 11:47:15

dans le cas où aucune réponse ne fonctionne pour quelqu'un-vérifiez si votre conteneur cible est déjà en cours d'exécution dans le réseau docker:

docker inspect my-target-container | grep NetworkMode
        "NetworkMode": "my-network-name",

si oui, vous devez exécuter le conteneur proxy dans le même réseau.

docker inspect my-target-container | grep -A2 Aliases
                "Aliases": [
                    "my-alias",
                    "23ea4ea42e34a"

commande finale:

docker run --net my-network-name --name my-new-proxy \
-d -p 8080:1234 alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:my-alias:80
0
répondu ktalik 2018-04-18 15:17:35