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

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


当前回答

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

其他回答

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

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

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

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

请先阅读里卡多的回答。这对我很管用。

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

Docker网络ls

然后追加如下内容 Docker运行-d——name sqlplus——link db:db -p 1521:1521 sqlplus——net network_name

虽然不能公开现有容器的新端口,但可以在同一个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,并且响应反向沿着相同的路径传播。

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