我试图创建一个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网络中运行:

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

有一个方便的HAProxy包装器。

docker run -it -p LOCALPORT:PROXYPORT --rm --link TARGET_CONTAINER:EZNAME -e "BACKEND_HOST=EZNAME" -e "BACKEND_PORT=PROXYPORT" demandbase/docker-tcp-proxy

这将为目标容器创建一个HAProxy。容易peasy。

我不得不处理同样的问题,并且能够在不停止任何正在运行的容器的情况下解决它。这是截至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,并且响应反向沿着相同的路径传播。

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