我试图创建一个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

其他回答

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

如果没有答案,检查你的目标容器是否已经在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

我会这么做:

提交活动容器。 使用新映像再次运行容器,并打开端口(我建议挂载一个共享卷并打开ssh端口)

sudo docker ps 
sudo docker commit <containerid> <foo/live>
sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash

虽然不能公开现有容器的新端口,但可以在同一个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

这可能对你有帮助