我正在试验 Dockerfiles,我想我理解了大部分逻辑。但是,在这种情况下,我看不出“公开”和“发布”端口之间的区别。
我首先看到的所有教程都在 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
这可能吗?
基本上,您有三个选择:
既不指定 EXPOSE 也不指定 -p 只指定 EXPOSE 指定 EXPOSE 和 -p
1) 如果您既不指定 EXPOSE
也不指定 -p
,则容器中的服务将只能从容器本身内部访问。
2)如果您EXPOSE
一个端口,容器中的服务不能从 Docker 外部访问,但可以从其他 Docker 容器内部访问。所以这有利于容器间的通信。
3)如果你EXPOSE
和-p
一个端口,容器中的服务可以从任何地方访问,甚至在Docker之外。
两者分开的原因是恕我直言,因为:
选择主机端口取决于主机,因此不属于 Dockerfile(否则将取决于主机),
如果容器中的服务可以从其他容器访问,通常就足够了。
documentation 明确指出:
EXPOSE 指令公开端口以供在链接中使用。
它还为您指出如何link containers,这基本上是我谈到的容器间通信。
PS:如果你做-p
,但不做EXPOSE
,Docker 会做一个隐含的EXPOSE
。这是因为如果一个端口对公众开放,它也会自动对其他 Docker 容器开放。因此 -p
包括 EXPOSE
。这就是为什么我没有将其列为第四种情况。
简短的回答:
EXPOSE 是一种记录方式
--publish(或-p)是一种将主机端口映射到正在运行的容器端口的方法
请注意以下:
EXPOSE 与 Dockerfiles 相关(文档化)
--publish 与 docker run ...(执行/运行时)有关
暴露和发布端口 在 Docker 网络中,有两种不同的机制直接涉及网络端口:暴露和发布端口。这适用于默认桥接网络和用户定义的桥接网络。您可以使用 Dockerfile 中的 EXPOSE 关键字或 --expose 标志向 docker run 公开端口。公开端口是一种记录使用了哪些端口的方法,但实际上并不映射或打开任何端口。暴露端口是可选的。您使用 --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 ...EXPOSE 指令实际上并不发布端口。它充当构建映像的人和运行容器的人之间的一种文档类型,关于打算发布哪些端口。来自:Dockerfile 参考
未定义 EXPOSE / --publish 时的服务访问:
在 @Golo Roden's 的回答中指出:
“如果您不指定其中任何一项,则容器中的服务将无法从容器本身内部以外的任何地方访问。”
可能在写答案的时候是这样,但现在看来,即使不使用EXPOSE
或--publish
,同一网络的host
和其他containers
也可以访问您可以在该容器内启动的服务。
如何测试这个:
我使用了以下 Dockerfile
。基本上,我从 ubuntu 开始并安装一个小型网络服务器:
FROM ubuntu
RUN apt-get update && apt-get install -y mini-httpd
我build
将图像作为“testexpose”并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
确实做了一些事情,而不仅仅是文档。如果您使用 -P
标志run
,它会发布“all exposed ports to random ports
”。
EXPOSE
/ --publish
时的服务访问 仍然有效并且按上述方式工作(我刚刚再次对其进行了测试)。也许您在 docker for Windows 或 Mac 上尝试过(网络更复杂)?在 Linux VM 上,它完全按照编写的方式工作,示例是完整且可验证的。
请参阅官方文档参考: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 run 上使用 -P
)或 -p
,则不会暴露任何端口。
如果您总是在 docker run
处使用 -p
,则不需要 EXPOSE
,但如果您使用 EXPOSE
,您的 docker run
命令可能会更简单,如果您不在乎哪个端口会使用 EXPOSE
,那么 EXPOSE
会很有用暴露在主机上,或者如果您确定只会加载一个容器。
您可以使用 Dockerfile 中的 EXPOSE 关键字或 --expose 标志向 docker run 公开端口。公开端口是一种记录使用了哪些端口的方法,但实际上并不映射或打开任何端口。暴露端口是可选的。
大多数人使用 docker compose 和网络。 documentation 指出:
Docker 网络功能支持创建网络而无需暴露网络内的端口,有关详细信息,请参阅此功能的概述)。
这意味着如果您使用网络在容器之间进行通信,则无需担心暴露端口。
EXPOSE 关键字让所有者可以通知其他人容器将主要使用哪些端口。
即使您没有在 EXPOSE 中指定端口,您也可以发布任何端口。
例如,我们使用暴露端口 1234 的 nginx 映像创建 Dockerfile
FROM nginx:latest
EXPOSE 1234
并建造它
码头工人构建 -t 端口测试。
并通过将 80 端口发布到 localhost:80 来运行它
docker run -p 80:80 端口测试
当你去 localhost:80 时,你会看到 nginx 默认页面。 Nginx default page
EXPOSE 用于映射本地端口容器端口,即:如果您在 docker 文件中指定暴露,例如
EXPOSE 8090
它将 localhost 端口 8090 映射到容器端口 8090 会做什么
EXPOSE
和-p
而不是前面的三个要点,将会很有用。让我有点困惑。