我试图创建一个Docker容器,就像一个完整的虚拟机。我知道我可以在Dockerfile中使用EXPOSE指令来公开一个端口,并且我可以使用-p标志与docker run一起分配端口,但是一旦容器实际运行,是否有命令打开/映射其他端口?
例如,假设我有一个运行sshd的Docker容器。其他人使用容器ssh并安装httpd。有没有办法公开容器上的80端口,并将其映射到主机上的8080端口,以便人们可以访问容器中运行的web服务器,而无需重新启动它?
我试图创建一个Docker容器,就像一个完整的虚拟机。我知道我可以在Dockerfile中使用EXPOSE指令来公开一个端口,并且我可以使用-p标志与docker run一起分配端口,但是一旦容器实际运行,是否有命令打开/映射其他端口?
例如,假设我有一个运行sshd的Docker容器。其他人使用容器ssh并安装httpd。有没有办法公开容器上的80端口,并将其映射到主机上的8080端口,以便人们可以访问容器中运行的web服务器,而无需重新启动它?
当前回答
不可能进行实时端口映射,但有多种方法可以给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
管道也可以将一个真实的接口映射到容器中,或者在旧版本的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:这些规则是临时的,只会持续到容器重新启动
不可能进行实时端口映射,但有多种方法可以给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
管道也可以将一个真实的接口映射到容器中,或者在旧版本的Docker中设置一个子接口。
IP的路由
如果你可以控制网络,你可以将额外的网络路由到Docker主机中,以供容器使用。
然后将该网络分配给容器,并设置Docker主机通过Docker网络路由数据包。
共享主机接口
——net host选项允许将主机接口共享到一个容器中,但由于共享的性质,这可能不是在一个主机上运行多个容器的良好设置。
虽然不能公开现有容器的新端口,但可以在同一个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,并且响应反向沿着相同的路径传播。
我不得不处理同样的问题,并且能够在不停止任何正在运行的容器的情况下解决它。这是截至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
您可以使用像Weave Net这样的覆盖网络,它将为每个容器分配唯一的IP地址,并隐式地将所有端口暴露给网络的每个容器部分。
Weave还提供主机网络集成。缺省情况下是禁用的,但是,如果您还想从主机访问容器IP地址(及其所有端口),您可以简单地运行weave expose。
坦白说,我在纺织工场工作。