Docker中的“公开”和“发布”有什么区别?

我正在尝试使用Dockerfile,我想我理解了大部分逻辑。然而,在这种情况下,我看不出“公开”和“发布”端口之间的区别。

我首先看到的所有教程都在Dockerfile中包含EXPOSE命令:

...
EXPOSE 8080
...

然后他们从这个Dockerfile构建镜像:

$ docker build -t an_image - < Dockerfile

然后发布运行映像时与上述相同的端口:

$ docker run -d -p 8080 an_image

或使用发布所有端口

$ docker run -d -P an_image

在Dockerfile中暴露一个端口有什么意义,如果它无论如何都会发布的话?是否需要先暴露一个端口,然后没有再发布它?实际上,我想指定我在创建映像时将在Dockerfile中使用的所有端口,然后不再打扰它们,简单地运行它们:

$ docker run -d an_image

这可能吗?

273784 次浏览

基本上,你有三(四)个选项:

  1. 既不指定EXPOSE也不指定-p
  2. 仅指定EXPOSE
  3. 指定EXPOSE-p
  4. 仅指定隐式执行EXPOSE-p
  1. 如果您既未指定EXPOSE也未指定-p,则只能从容器本身的里面访问容器中的服务。

  2. 如果你EXPOSE一个端口,容器中的服务不能从Docker外部访问,而是从其他Docker容器内部访问。所以这对于容器间的通信很有好处。

  3. 如果您EXPOSE-p端口,容器中的服务可以从任何地方访问,甚至在Docker之外。

  4. 如果您执行-p,但不执行EXPOSE,Docker会执行隐式的EXPOSE。这是因为如果一个端口向公众开放,它也会自动向其他Docker容器开放。因此-p包括EXPOSE。这实际上与3相同)。

两者分开的原因是IMHO,因为:

  • 选择主机端口取决于主机,因此不属于Dockerfile(否则将取决于主机),
  • 如果容器中的服务可以从其他容器访问,通常就足够了。

留档明确指出:

EXPOSE指令公开用于链接的端口。

它还向您指出了如何链接容器,这基本上就是我谈到的容器间通信。

查看官方留档参考:https://docs.docker.com/engine/reference/builder/#expose

EXPOSE允许您定义私有(容器)和公共(主机)端口以在映像构建时公开,以便在容器运行<强>如果时使用-P运行容器。

$ docker help run
...
-P, --publish-all                    Publish all exposed ports to random ports
...

公共端口和协议是可选的,如果没有指定公共端口,docker会在主机上随机选择一个端口,在Dockerfile上暴露指定的容器端口。

一个好的做法是不指定公共端口,因为它限制每个主机只有一个容器(第二个容器会抛出一个已经在使用的端口)。

您可以在docker run 中使用-p来控制暴露的容器端口可连接的公共端口。

无论如何,如果您不使用EXPOSE(在docker运行时使用-P)或-p,则不会暴露任何端口。

如果你总是在docker run上使用-p,你不需要EXPOSE,但是如果你使用EXPOSE,你的docker run 命令可能更简单,如果你不关心主机上暴露的端口,或者如果你确定只加载一个容器,EXPOSE会很有用。

大多数人使用docker组合网络。留档声明:

Docker网络功能支持创建网络,而无需暴露网络中的端口,有关详细信息,请参阅此功能的概述)。

这意味着如果您使用网络在容器之间进行通信,则无需担心暴露端口。

您可以使用Dockerfile中的EXPOSE关键字或 --将标志暴露给docker run。暴露端口是记录其中的一种方式 使用端口,但实际上并不映射或打开任何端口 是可选的。

图片来源:github提交

简短的回答:

  • EXPOSE记录的一种方式
  • --publish(或-p)是映射主机端口到运行中的集装箱港口的一种方式

下面注意:

  • EXPOSEDockerfiles记录)有关
  • --publishdocker run ...执行时长)有关

暴露和发布端口

在Docker网络中,有两种不同的机制直接涉及网络端口:公开和发布端口。这适用于默认桥接网络和用户定义的桥接网络。

  • 您可以使用Dockerfile中的EXPOSE关键字或--expose标志公开端口以运行docker。公开端口是记录使用哪些端口的一种方式,但实际上并没有映射或打开任何端口。公开端口是可选的。

  • 您使用--publish--publish-all标志将端口发布到docker run。这会告诉Docker在容器的网络接口上打开哪些端口。发布端口时,它会映射到主机上可用的高阶端口(高于30000),除非您在运行时指定要映射到主机上的端口。当您构建映像时(在Dockerfile中),您无法指定要映射到主机上的端口,因为有无法保证端口在运行映像的主机上可用

来自:Docker容器网络

2019年10月更新:上面的一段文本不再在文档中,但存档版本在这里:docs.docker.com/v17.09/engine/userguide/networking/#exposing-and-publishing-ports

也许目前的留档是以下:

发布的端口

默认情况下,当您创建容器时,它不会向外部世界发布任何端口。要使端口可用于Docker以外的服务,或未连接到容器网络的Docker容器,请使用--publish-p标志。这会创建一个防火墙规则,将容器端口映射到Docker主机上的端口。

可以在这里找到:docs.docker.com/config/containers/container-networking/#published-ports

还有,

曝光

EXPOSE指令并没有实际发布端口。它在构建映像的人和运行容器的人之间充当留档的一种类型,关于打算发布哪些端口。

来自:dockerfile引用






未定义EXPOSE/--publish时的服务访问:

@Golo Roden's的回答中,::

"如果您没有指定其中任何一个,则除了容器本身之外,无法从任何地方访问容器中的服务。"

也许在编写答案时就是这种情况,但现在看来,即使您不使用EXPOSE--publish,同一网络的host和其他containers也能够访问您可以在该容器内启动的服务。

如何测试这个:

我使用了以下Dockerfile。基本上,我从ubuntu开始并安装一个小型Web服务器:

FROM ubuntu
RUN apt-get update && apt-get install -y mini-httpd

build将图像作为“test的暴露”,run是一个新的容器:

docker run --rm -it testexpose bash

在容器内部,我启动了mini-httpd的几个实例:

root@fb8f7dd1322d:/# mini_httpd -p 80
root@fb8f7dd1322d:/# mini_httpd -p 8080
root@fb8f7dd1322d:/# mini_httpd -p 8090

然后,我可以从主机或其他容器中使用curl来获取mini-httpd的主页。


进一步阅读

关于Ivan Pepelnjak主题的非常详细的文章:

EXPOSE用于映射本地港口集装箱港口 即:如果您在docker文件中指定暴露,如

EXPOSE 8090

它将如何映射localhost端口8090到容器端口8090

EXPOSE关键字允许所有者通知其他人容器将主要使用哪些端口。

您可以发布任何端口,即使您没有在EXPOSE中指定端口。

例如,我们使用nginx镜像创建一个暴露端口1234的Dockerfile

FROM nginx:latest
EXPOSE 1234

并建造它

docker build-t porttest.

并用发布80端口运行到localhost:80

docker run-p 80:80 porttest运行时间

当您localhost:80时,您将看到nginx默认页面。 nginx默认页面

  1. 公开-将只允许该特定端口与容器连接,并且它将仅用作“容器间通信”
  2. -p(发布)将映射主机端口与容器端口(您已经在步骤1或docker文件中公开了),一旦您公开了它,您将需要映射它,因此我们使用发布,然后它将访问外部世界/互联网的容器。