在活动的Docker容器上暴露端口

我试图创建一个Docker容器,就像一个完整的虚拟机。我知道我可以在Dockerfile中使用EXPOSE指令来公开一个端口,并且我可以使用-p标志和docker run来分配端口,但是一旦容器实际运行,是否有命令打开/映射其他端口?

例如,假设我有一个运行sshd的Docker容器。其他人使用容器ssh并安装httpd。有没有办法公开容器上的80端口,并将其映射到主机上的8080端口,以便人们可以访问容器中运行的web服务器,而无需重新启动它?

514025 次浏览

你不能通过Docker来做到这一点,但是你可以从主机上访问容器未暴露的端口。

如果您有一个在端口8000上运行某些内容的容器,您可以运行

wget http://container_ip:8000

要获取容器的IP地址,运行以下2条命令:

docker ps
docker inspect container_name | grep IPAddress

在内部,当你运行一个图像时,Docker外壳会调用iptables,所以这可能会有一些变化。

将容器的端口8000暴露在本地主机的端口8001上:

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

解决这个问题的一种方法是用你想要的端口映射设置另一个容器,并比较iptables-save命令的输出(尽管,我不得不删除一些强制通信通过docker代理的其他选项)。

注意:这是在颠覆docker,所以应该注意到它可能会产生蓝烟。

另一种选择是查看(new?- p选项-它将使用随机的主机端口,然后将它们连接起来。

在0.6.5中,你可以使用LINKs特性来启动一个与现有容器对话的新容器,并附加一些对该容器的-p标志的中继?(我还没用过LINKs。)

docker 0.11?你可以使用docker run --net host ..将你的容器直接连接到主机的网络接口(即,net没有命名空间),因此你在容器中打开的所有端口是公开的。

我会这么做:

  • 提交活动容器。
  • 使用新映像再次运行容器,并打开端口(我建议挂载一个共享卷并打开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

IPtables黑客不能工作,至少在Docker 1.4.1上是这样。

最好的方法是使用暴露的端口运行另一个容器,并使用socat进行中继。下面是我使用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

这是另一个想法。使用SSH协议进行端口转发;当你的Docker主机是一个VM时,这样做的好处是也可以在OS X(可能还有Windows)中工作。

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

您可以使用SSH创建隧道并在主机中公开容器。

可以采用两种方式,从容器到主机或从主机到容器。但是你需要一个SSH工具,比如OpenSSH(一个是客户端,另一个是服务器)。

例如,在容器中,可以这样做

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

你可以从这一行(在容器中)找到容器的IP地址:

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

然后在主机中,你可以这样做:

sudo ssh -NfL 80:0.0.0.0:80 root@172.17.0.2

你可以使用像编织网这样的覆盖网络,它将为每个容器分配一个唯一的IP地址,并隐式地将所有端口暴露给网络的每个容器部分。

Weave还提供了主机网络集成。它在默认情况下是禁用的,但是,如果你也想从主机访问容器IP地址(及其所有端口),你可以简单地运行weave expose

坦白说,我在纺织工场工作。

我不得不处理同样的问题,并且能够在不停止任何正在运行的容器的情况下解决它。这是截至2016年2月的最新解决方案,使用Docker 1.9.1。不管怎样,这个答案是@ricardo-branco的答案的详细版本,但对新用户来说更有深度。

在我的场景中,我想临时连接到运行在容器中的MySQL,由于其他应用程序容器都链接到它,停止、重新配置和重新运行数据库容器是不可能的。

由于我想从外部访问MySQL数据库(从Sequel Pro通过SSH隧道),我将在主机上使用端口33306。(不是3306,只是以防有一个外部MySQL实例正在运行。)

大约一个小时的调整iptables证明是徒劳的,尽管:

一步一步地,我是这样做的:

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

编辑dockerfile,把它放在里面:

# 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

然后构建图像:

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

然后运行它,链接到正在运行的容器。(使用-d代替-rm将其保留在后台,直到显式停止和删除。在这种情况下,我只希望它暂时运行。)

docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306  your-namespace/db-expose-33306

虽然不能公开现有的容器的新端口,但可以在同一个Docker网络中启动一个新容器,并让它将流量转发到原始容器。

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

工作的例子

启动一个监听端口80的web服务,但公开其内部端口80(哎呀!):

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

查找其Docker网络IP:

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

以暴露端口8080启动verb/socat,并让它将TCP流量转发到该IP的端口80:

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

你现在可以访问http://localhost:8080/上的pastebin,你的请求会被送到socat:1234,它会把它转发给pastebin:80,并且响应会反向沿着相同的路径传播。

不可能进行实时端口映射,但有多种方法可以给Docker容器提供类似虚拟机的真实接口。

Macvlan接口

Docker现在包含Macvlan网络驱动。这将Docker网络附加到一个“真实世界”接口上,并允许你直接将网络地址分配给容器(就像虚拟机桥接模式)。

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

pipework也可以映射一个真实的接口到容器中,或者在旧版本的Docker中设置子接口

IP的路由

如果你可以控制网络,你可以路由附加网络到你的Docker主机在容器中使用。

然后将该网络分配给容器,并设置Docker主机通过Docker网络路由数据包。

共享主机接口

--net host选项允许主机接口被共享到一个容器中,但由于共享的性质,这可能不是在一个主机上运行多个容器的好设置。

为了添加到接受的答案 iptables解决方案中,我必须在主机上运行另外两个命令将其打开到外部世界。

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

注意:我正在打开端口https(443),我的docker内部IP是172.17.0.2

注2:这些规则是临时的,只会持续到容器重新启动

有一个方便的HAProxy包装器。

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

这将为目标容器创建一个HAProxy。容易peasy。

首先阅读里卡多的响应。这对我很管用。

但是,如果使用docker-compose启动正在运行的容器,则存在这样的情况。这是因为docker-compose(我正在运行docker 1.17)创建了一个新的网络。解决这种情况的方法是

docker network ls

然后追加以下内容 Docker运行-d——name sqlplus——link db:db -p 1521:1521 sqlplus——net network_name < /代码> < / p >

如果没有答案,检查你的目标容器是否已经在docker网络中运行:

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

将其保存到后面的变量$NET_NAME中:

NET_NAME=$(docker inspect --format '\{\{.HostConfig.NetworkMode}}' $CONTAINER)

如果是,您应该在同一网络中运行代理容器。

接下来查找容器的别名:

docker inspect $CONTAINER | grep -A2 Aliases
"Aliases": [
"my-alias",
"23ea4ea42e34a"

将其保存到后面的变量$ALIAS中:

ALIAS=$(docker inspect --format '\{\{index .NetworkSettings.Networks "'$NET_NAME'" "Aliases" 0}}' $CONTAINER)

现在在网络$NET_NAME中的容器中运行socat,以桥接到$ALIASed容器的暴露(但未发布)端口:

docker run \
--detach --name my-new-proxy \
--net $NET_NAME \
--publish 8080:1234 \
alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:$ALIAS:80

以下是一些解决方案:

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

运行容器时映射端口的解决方案。

Docker运行-d——net=host myvnc

这将自动将端口公开并映射到您的主机

基于Robm的回答,我创建了一个Docker映像和一个名为portcat的Bash脚本。

使用portcat,您可以轻松地将多个端口映射到现有的Docker容器。使用(可选)Bash脚本的示例:

curl -sL https://raw.githubusercontent.com/archan937/portcat/master/script/install | sudo bash
portcat my-awesome-container 3456 4444:8080

好了!Portcat正在映射:

  • 端口3456my-awesome-container:3456
  • 端口4444my-awesome-container:8080

请注意,Bash脚本是可选的,以下命令:

ipAddress=$(docker inspect my-awesome-container | grep IPAddress | grep -o '[0-9]\{1,3\}\(\.[0-9]\{1,3\}\)\{3\}' | head -n 1)
docker run -p 3456:3456 -p 4444:4444 --name=alpine-portcat -it pmelegend/portcat:latest $ipAddress 3456 4444:8080

我希望portcat对你们来说会派上用场。干杯!

Docker运行-i——expose=22 b5593e60c33b bash

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

这可能对你有帮助