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