在实时Docker容器上公开端口

时间:2013-11-11 02:00:56

标签: docker

我正在尝试创建一个充当虚拟机的Docker容器。我知道我可以使用Dockerfile中的EXPOSE指令来公开端口,我可以使用-p标志和docker run来分配端口,但是一旦容器实际运行,是否有命令打开/映射其他端口直播?

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

16 个答案:

答案 0 :(得分:301)

您不能通过Docker执行此操作,但可以从主机访问容器的未公开端口。

如果您的容器在其端口8000上运行某些东西,则可以运行

wget http://container_ip:8000

要获取容器的IP地址,请运行以下命令:

docker ps

docker inspect container_name | grep IPAddress

在内部,Docker会在你运行图像时弹出来调用iptables,所以可能会有一些变化。

在localhosts端口8001上公开容器的端口8000:

 iptables -t nat -A  DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000

你可以解决这个问题的另一种方法是使用你想要的端口映射设置另一个容器,并比较 iptables-save 命令的输出(但是,我必须删除一些其他强制流量通过docker代理的选项。)

注意:这是破坏码头工具,因此应该意识到它可能会产生蓝烟

OR

另一种选择是查看(新的?post 0.6.6?) - P选项 - 它将使用随机主机端口,然后将它们连接起来。

OR

0.6.5,您可以使用LINKs功能调出一个与现有容器对话的新容器,还有一些中继到该容器的-p标志? (我还没有使用LINK)

OR

与docker 0.11?您可以使用docker run --net host ..将容器直接连接到主机的网络接口(即,net不是名称间隔的),因此您在容器中打开的所有端口都会被暴露。

答案 1 :(得分:129)

这就是我要做的事情:

  • 提交实时容器。
  • 使用新图像再次运行容器,端口打开(我建议安装共享卷并打开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

答案 2 :(得分:32)

这是另一个想法。使用SSH进行端口转发;当您的Docker主机是VM时,这也有利于OS X(可能还有Windows)。

docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>

答案 3 :(得分:32)

虽然您无法公开现有容器的新端口,但您可以在同一个Docker网络中启动一个新容器,并将其转发到原始容器。

# docker run \
  --rm \
  -p $PORT:1234 \
  verb/socat \
    TCP-LISTEN:1234,fork \
    TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT

工作示例

启动侦听端口80的网络服务,但公开其内部端口80(oops!):

# docker run -ti mkodockx/docker-pastebin   # Forgot to expose PORT 80!

找到它的Docker网络IP:

# docker inspect 63256f72142a | grep IPAddress
                    "IPAddress": "172.17.0.2",

在端口8080暴露的情况下启动verb/socat,并让它将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,并将其转发到pastebin:80,并且响应会以相反的方式传递相同的路径。

答案 4 :(得分:30)

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

答案 5 :(得分:8)

我不得不处理同样的问题,并且能够在不停止任何正在运行的容器的情况下解决它。使用Docker 1.9.1,这是截至2016年2月的最新解决方案。无论如何,这个答案是@ ricardo-branco答案的详细版本,但对新用户来说更为深入。

在我的场景中,我想暂时连接到在容器中运行的MySQL,并且由于其他应用程序容器已链接到它,因此停止,重新配置和重新运行数据库容器是不可能的。

由于我想从外部访问MySQL数据库(从Sequel Pro通过SSH隧道),我将在主机上使用端口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

答案 6 :(得分:4)

要添加到accepted answer 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:这些规则和临时文件只会持续到容器重新启动

答案 7 :(得分:2)

您可以使用SSH创建隧道并在主机中公开您的容器。

您可以通过两种方式执行此操作,从容器到主机以及从主机到容器。但是你需要一个像OpenSSH这样的SSH工具(一个是客户端,另一个是服务器)。

例如,在容器中,您可以执行

$ yum install -y openssh openssh-server.x86_64
service sshd restart
Stopping sshd:                                             [FAILED]
Generating SSH2 RSA host key:                              [  OK  ]
Generating SSH1 RSA host key:                              [  OK  ]
Generating SSH2 DSA host key:                              [  OK  ]
Starting sshd:                                             [  OK  ]
$ passwd # You need to set a root password..

您可以从此行(在容器中)找到容器IP地址:

$ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*/\1/g'
172.17.0.2

然后在主持人中,您可以这样做:

sudo ssh -NfL 80:0.0.0.0:80 root@172.17.0.2

答案 8 :(得分:2)

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

Weave还提供了host network integration。默认情况下禁用它,但是,如果您还要从主机访问容器IP地址(及其所有端口),则只需运行weave expose

完全披露:我在Weaveworks工作。

答案 9 :(得分:1)

有一个方便的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。容易腻。

答案 10 :(得分:1)

首先阅读Ricardo的回复。这对我有用。

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

docker network ls

然后追加以下内容 docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name

答案 11 :(得分:1)

如果某人没有回答 - 请检查目标容器是否已在docker network中运行:

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)

现在在网络socat的容器中运行$NET_NAME以桥接到$ALIAS ed容器的公开(但未发布)端口:

docker run \
    --detach --name my-new-proxy \
    --net $NET_NAME \
    --publish 8080:1234 \
    alpine/socat TCP-LISTEN:1234,fork TCP-CONNECT:$ALIAS:80

答案 12 :(得分:1)

基于 Robm's answer,我创建了一个 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 正在映射:

  • 端口 3456my-awesome-container:3456
  • 端口 4444my-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 对你们有用。干杯!

答案 13 :(得分:0)

不可能进行实时端口映射,但有多种方法可以为Docker容器提供虚拟机所具有的真实接口。

Macvlan接口

Docker现在包含Macvlan network driver。这将Docker网络连接到一个真实的世界&#34;接口,允许您将网络地址直接分配给容器(如虚拟机桥接模式)。

docker network create \
    -d macvlan \
    --subnet=172.16.86.0/24 \
    --gateway=172.16.86.1  \
    -o parent=eth0 pub_net

pipework也可以map a real interface放入容器或旧版本的Docker中setup a sub interface

路由IP&#39;

如果您可以控制网络,则可以route additional networks到Docker主机以便在容器中使用。

然后将该网络分配给容器并设置Docker主机以通过docker网络路由数据包。

共享主机接口

--net host选项允许将主机接口共享到容器中,但由于共享特性,这可能不是在一台主机上运行多个容器的良好设置。

答案 14 :(得分:0)

以下是一些解决方案:

https://forums.docker.com/t/how-to-expose-port-on-running-container/3252/12

  

运行容器时映射端口的解决方案。

     

docker run -d --net = host myvnc

     

这将自动将端口公开并映射到您的主机

答案 15 :(得分:0)

我写了一篇博客文章,解释了如何根据需要以不同的方式访问容器的未发布端口:

  • 提交新映像并运行新容器,
  • 使用socat避免重新启动容器。

文章还简要介绍了端口映射的工作原理,公开和发布端口之间的区别以及socat是什么。

这里是链接:https://lmcaraig.com/accessing-an-unpublished-port-of-a-running-docker-container