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

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


当前回答

你不能通过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,所以应该注意到它可能会产生蓝烟。

OR

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

OR

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

OR

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

其他回答

您可以使用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

虽然不能公开现有容器的新端口,但可以在同一个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",

启动verb/socat,暴露端口8080,并让它将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, socat:1234将它转发到pastebin:80,并且响应反向沿着相同的路径传播。

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

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

你不能通过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,所以应该注意到它可能会产生蓝烟。

OR

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

OR

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

OR

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

基于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正在映射:

Port 3456到my-awesome-container:3456 端口4444到my-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能派上用场。干杯!