我试图创建一个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网络中运行:
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 这将自动将端口公开并映射到您的主机
不可能进行实时端口映射,但有多种方法可以给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来做到这一点,但是你可以从主机上访问容器未暴露的端口。
如果您有一个在端口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没有命名空间),因此在容器中打开的所有端口都是公开的。
为了向已接受的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网络中启动一个新容器,并让它将流量转发到原始容器。
# 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,并且响应反向沿着相同的路径传播。