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

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


当前回答

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

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

由于我想从外部(通过SSH隧道从Sequel Pro)访问MySQL数据库,我将使用主机上的端口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",

启动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,并且响应反向沿着相同的路径传播。

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

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

这可能对你有帮助

基于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能派上用场。干杯!

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

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

为了向已接受的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:这些规则是临时的,只会持续到容器重新启动