web-dev-qa-db-fra.com

Exposer un port sur un conteneur Docker actif

J'essaie de créer un conteneur Docker qui se comporte comme une machine virtuelle complète. Je sais que je peux utiliser l'instruction EXPOSE dans un fichier Docker pour exposer un port et que je peux utiliser l'indicateur -p avec docker run pour attribuer des ports, mais une fois qu'un conteneur est en cours d'exécution, existe-t-il une commande pour ouvrir/mapper des ports supplémentaires en direct?

Par exemple, supposons que j'ai un conteneur Docker qui exécute sshd. Quelqu'un d'autre utilisant le conteneur ssh est dans et installe httpd. Existe-t-il un moyen d'exposer le port 80 du conteneur et de le mapper sur le port 8080 de l'hôte, afin que les utilisateurs puissent visiter le serveur Web exécuté dans le conteneur sans le redémarrer?

349
reberhardt

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

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

wget http://container_ip:8000

Pour obtenir l'adresse IP du conteneur, exécutez les 2 commandes suivantes:

docker ps

docker inspect container_name | grep IPAddress

En interne, Docker demande à appeler iptables lorsque vous exécutez une image. Par conséquent, certaines variations peuvent fonctionner.

pour exposer le port 8000 du conteneur sur le port 8001 de votre hôte local:

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

Vous pouvez notamment résoudre ce problème en configurant un autre conteneur avec le mappage de port souhaité et en comparant le résultat de la commande iptables-save (mais j’ai dû supprimer certaines des autres options qui forcent le trafic à passer par le proxy docker).

NOTE: ceci est en train de renverser le docker, alors vous devriez le faire en sachant qu'il peut bien créer de la fumée bleue

OU 

Une autre alternative consiste à rechercher l'option (nouvelle? Post 0.6.6?) -P, qui utilisera des ports d'hôte aléatoires, puis les connectera.

OR

avec 0.6.5, vous pourriez utiliser la fonctionnalité LINK pour afficher un nouveau conteneur qui communique avec le conteneur existant, avec quelques relais supplémentaires pour les indicateurs -p de ce conteneur? (Je n'ai pas encore utilisé LINKs)

OR

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 le réseau n'est pas à espacement de noms) et donc tous les ports que vous ouvrez dans le conteneur sont exposés.

296
SvenDowideit

Voici ce que je ferais:

  • Commit le conteneur live.
  • Exécutez à nouveau le conteneur avec la nouvelle image, avec les ports ouverts (je vous recommande de monter un volume partagé et d'ouvrir le port ssh)
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
129
bosky101

Voici une autre idée. Utilisez SSH pour effectuer le transfert de port. Cela présente également l'avantage de fonctionner sous OS X (et probablement Windows) lorsque votre hôte Docker est une machine virtuelle.

docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>
32
Scott Carlson

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

Le meilleur moyen serait d'exécuter un autre conteneur avec le port exposé et de le relayer avec socat. C'est ce que j'ai fait pour me connecter (temporairement) à 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
29
Ricardo Branco

Bien que vous ne puissiez pas exposer un nouveau port d'un conteneur existant, vous pouvez démarrer un nouveau conteneur dans le même réseau Docker et le faire transférer le trafic dans 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é

Lancez un service Web qui écoute sur le port 80, mais not n'exposez pas son port interne 80 (oups!):

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

Trouver son IP réseau Docker:

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

Lancez verb/socat avec le port 8080 exposé et demandez-lui de transférer le trafic TCP vers le port 80 de cette adresse 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 via http: // localhost: 8080/ , et vos demandes passent à socat:1234 qui les transfère au Pastebin:80 et la réponse suit le même chemin en sens inverse.

28
RobM

J'ai dû faire face au même problème et j'ai pu le résoudre sans arrêter aucun de mes conteneurs en cours d'exécution. Il s'agit d'une solution à jour depuis février 2016, 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, je souhaitais me connecter temporairement à MySQL qui s'exécutait dans un conteneur. Etant donné que d'autres conteneurs d'applications y étaient liés, arrêter, reconfigurer et réexécuter le conteneur de base de données était un démarrage.

Étant donné que je souhaite accéder à la base de données MySQL en externe (à partir de Sequel Pro via le tunneling SSH), je vais utiliser le port 33306 sur la machine hôte. (Pas 3306, juste au cas où une instance externe de MySQL serait en cours d'exécution.)

Environ une heure de peaufinage iptables s’est avéré infructueux, même si:

Pas à pas, voici ce que j'ai fait:

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

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

Puis exécutez-le, en vous connectant à votre conteneur en cours d'exécution. (Utilisez -d au lieu de -rm pour le conserver en arrière-plan jusqu'à ce qu'il soit explicitement arrêté et supprimé. Je souhaite seulement qu'il fonctionne 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
8
Excalibur

Pour ajouter à la solution réponse acceptéeiptables, je devais 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

Remarque: j'ouvrais le port https (443), l'adresse IP interne de mon menu fixe était 172.17.0.2.

Note 2: Ces règles et temporaries ne dureront que jusqu'au redémarrage du conteneur

4
dstj

Vous pouvez utiliser un réseau superposé tel que Weave Net , qui attribuera une adresse IP unique à chaque conteneur et exposera implicitement tous les ports à chaque conteneur du réseau.

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

Divulgation complète: je travaille chez Weaveworks.

2
fons

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

Vous pouvez le faire des deux manières, d'un conteneur à un hôte et d'un hôte à un conteneur. Mais vous avez besoin d'un outil SSH comme OpenSSH (client dans l'un et serveur dans un 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/^[^:]*:\([^ ]*\).*/\1/g'
172.17.0.2

Ensuite, dans l'hôte, vous pouvez simplement faire:

Sudo ssh -NfL 80:0.0.0.0:80 [email protected]
2
ton

Il existe un wrapper HAProxy très 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 dans le conteneur cible. peasy facile.

1
kwerle

Si aucune réponse ne fonctionne pour quelqu'un, vérifiez si votre conteneur cible est déjà en cours d'exécution sur le réseau Docker:

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

Si oui, vous devriez exécuter le conteneur de 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
1
ktalik

Lisez d'abord la réponse de Ricardo . Cela a fonctionné pour moi.

Toutefois, il existe un scénario dans lequel cela ne fonctionnera pas si le conteneur en cours d'exécution a été lancé à l'aide de docker-compose. Cela est dû au fait que docker-compose (j'utilise Docker 1.17) crée un nouveau réseau. La façon de traiter ce scénario serait

docker network ls

Puis ajoutez ce qui suit docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name

1
ice.nicer

Il n’est pas possible de mapper des ports en direct, mais il existe de nombreuses façons de donner à un conteneur Docker ce qui équivaut à une véritable interface comme le ferait une machine virtuelle. 

Interfaces Macvlan

Docker inclut maintenant un pilote de réseau Macvlan . Cela attache un réseau Docker à une interface "réelle" et vous permet d'affecter ces adresses de réseau directement au conteneur (comme un mode ponté de machines virtuelles).

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

pipework peut également mapper une interface réelle dans un conteneur ou configurer une sous-interface dans les anciennes versions de Docker.

IP de routage

Si vous avez le contrôle du réseau, vous pouvez router des réseaux supplémentaires vers votre hôte Docker pour une utilisation dans les conteneurs. 

Ensuite, vous affectez ce réseau aux conteneurs et configurez votre hôte Docker pour acheminer les paquets via le réseau Docker. 

Interface d'hôte partagé

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
Matt

Voici quelques solutions:

https://forums.docker.com/t/how-to-expose-port-on-running-container/3252/12

La solution pour mapper le port lors de l'exécution du conteneur.

docker run -d --net = Host myvnc

qui exposera et mappera automatiquement le port sur votre hôte

0
Ijaz Ahmad Khan