在活动的Docker容器上公开端口


408

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

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


1
您可以为容器提供可路由的IP,因此不需要端口映射。
马特

Answers:


331

您不能通过Docker进行此操作,但是可以从主机访问容器的未暴露端口。

如果您有一个在其端口8000上运行的容器,则可以运行

wget http://container_ip:8000

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

docker ps

docker inspect container_name | grep IPAddress

在内部,Docker在运行映像时会掏空以调用iptables,因此也许可以对此进行一些修改。

在本地主机的端口8001上暴露容器的端口8000:

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

解决这个问题的一种方法是,使用所需的端口映射设置另一个容器,并比较iptables-save命令的输出(尽管我不得不删除一些其他选项,这些选项会迫使流量通过docker传递)代理)。

注意:这是在颠覆docker,因此应意识到它很可能会产生蓝烟

要么

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

要么

在0.6.5中,您可以使用LINKs功能来建立一个与现有容器通信的新容器,并附加一些中继到该容器的-p标志?(我尚未使用LINK)

要么

与码头工人0.11?您可以使用docker run --net host ..将容器直接连接到主机的网络接口(即,net没有命名空间),从而使您在容器中打开的所有端口都暴露出来。


6
看来这至少不适用于docker 1.3.0。使用-p运行docker时会创建DOCKER DNAT规则,但手动添加它似乎不允许连接。奇怪的是,在容器运行时删除规则似乎也无法阻止其工作……
silasdavis,2014年

4
谢谢。我感到一种安全感,即未暴露的端口很安全。
seanmcl 2014年

自动化并使用jq + sed可能会有用: CONTAINER_IP=$(docker inspect container_name | jq .[0].NetworkSettings.IPAddress | sed -r 's/\"([^\"]+)\"/\1/''])'); iptables -t nat -A DOCKER -p tcp --dport 8001 -j DNAT --to-destination ${CONTAINER_IP}:8000
ericson.cepeda 2015年

5
@ ericson.cepeda除了可以调用之外jqsed您还可以使用以下-f选项docker inspectCONTAINER_IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' container_name)
pwes

对于那些喜欢kmkeen.com/jshon的人 jshon -e 0 -e NetworkSettings -e Networks -e bridge -e IPAddress -u
slf

138

这就是我要做的:

  • 提交活动容器。
  • 在端口打开的情况下,使用新映像再次运行容器(建议安装共享卷并同时打开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

31
我的问题的关键部分是,无需重新启动容器就可以做到这一点...转移到新的容器可能会保留文件,但会有效地杀死所有正在运行的进程,这类似于在物理机上重新启动。我需要这样做而不会发生这种情况。不过谢谢你!
reberhardt 2014年

好的。我会比较更多,尽管开始并行实例。由于两者都在运行(旧的和新的),一旦完成必要的迁移,代理到新容器可能会更容易。
bosky101'2014-2-22

是的,并行实例和反向代理是我喜欢Docker的一些主要原因。但是,在这种情况下,我需要将所有正在运行的进程保留在可能已通过SSH启动的容器中。尽管可执行文件将在提交映像和启动并行实例时保留,但可执行文件本身不会启动,RAM中的任何内容都会丢失。
reberhardt 2014年

6
你为什么跑sudo docker而不是跑docker
Thiago Figueiro'3

这个技巧对我很有帮助,因为我在一个缓慢的网络中安装了大量的软件包。通过运行,docker commit我准备再次测试应用程序,而不用花费数小时来重新安装所有内容。
gustavohenke '16

49

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

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

工作实例

启动侦听端口80但公开其内部端口80 的Web服务(哎呀!):

# 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转发给pastebin:80,并将其转发到,并且响应的路径相反。


7
很聪明!但是,最好使用它verb/socat:alpine,因为它的映像仅占5%的占用空间(除非您遇到libc或DNS不兼容问题)。
jpaugh

3
还有alpine/socat
Jamby,

3
极好的答案。简单,一线便能完成任务,而不会受到任何恶意攻击。
布鲁诺·布兰特

2
这对我来说效果很好,谢谢!自从我在创建网络的docker组合中启动了未公开的容器以来,我不得不添加--net myfoldername_defaultverb/socat启动命令中。
emazzotta

35

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

2
iptables hack应该从主机而不是docker容器运行。它实质上是将对主机上某些端口的请求转发到适当的Docker容器端口中。它根本不是特定于docker的,您可以对完全不同的主机执行此操作。您可以将代码格式添加到Docker文件中吗?似乎很有用。
AusIV 2015年

1
2016年2月:运行Docker 1.9.1,这是唯一对我成功的解决方案。IPTables解决方案均无效。值得建议使用与FROM数据库容器相同的基本映像,以有效利用资源。
神剑

4
您可能需要尝试apline / socat。它预装了socat,并接受socat选项作为命令,因此您根本不需要编写Dockerfile。
trkoch

35

这是另一个想法。使用SSH进行端口转发;当您的Docker主机是VM时,这具有在OS X(可能还有Windows)中工作的好处。

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

4
就我而言,正在运行的docker映像没有二进制ssh
Radu Toader

我是否需要在计算机上创建SSH密钥并将其放入Docker容器中?
octavian

@octavian ssh-keygen -t rsa在容器内。将发布密钥添加到主机上的authorized_keys
Luke W

8

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

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

由于我想从外部访问MySQL数据库(通过SSH隧道从Sequel Pro中访问),因此我将使用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

您可以直接使用动词/ socat或alpine / socat图像。参见动词/
索卡特-pjotr_dolphin

7

要添加到可接受的答案 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:这些规则和临时规则仅会持续到重新启动容器为止


这对我有用...但是后来有一些Cavat。根据注释2,如果容器重新启动,此操作将停止,然后容器可能位于其他IP上。但是更重要的是,docker对这个iptable条目一无所知,因此当您稍后完全重新启动该服务并让docker正确执行该操作时,不会将其删除。结果是多个完全相同的iptable条目,这导致它失败,几乎没有错误或没有原因的指示。一旦制定了额外的规则,问题就消失了。换句话说,进行任何更改后,请非常仔细地查看IP表。
安东尼

5

您可以使用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

这对我有用,但进行了一些小的更正...此命令有效:sudo ssh -NfL 25252:172.17.0.50:22 $ USER @ localhost
Alexander Guo

3

如果某人没有答案,请检查您的目标容器是否已在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)

现在socat,在网络中的一个容器中运行$NET_NAME以桥接到$ALIASed容器的暴露(但未发布)端口:

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

2

您可以使用诸如Weave Net之类的覆盖网络,该网络将为每个容器分配一个唯一的IP地址,并将所有端口隐式公开给网络的每个容器部分。

Weave还提供主机网络集成。默认情况下,它是禁用的,但是,如果您还想从主机访问容器IP地址(及其所有端口),则只需运行run即可weave expose

全面披露:我在Weaveworks工作。


2

有一个方便的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。十分简单。



1

首先阅读里卡多的回复。这对我有用。

但是,存在一种情况,如果正在使用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


0

不可能进行实时端口映射,但是可以通过多种方式为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

pipework还可以将真实接口映射到容器中,或者在较旧版本的Docker中设置子接口

路由IP

如果您控制网络,则可以将其他网络路由到Docker主机以在容器中使用。

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

共享主机接口

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.