我正在尝试使用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

这可能吗?


当前回答

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

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

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

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

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

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

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

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

其他回答

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

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

例如,我们用nginx镜像创建一个Dockerfile,公开端口1234

FROM nginx:latest
EXPOSE 1234

然后建造它

Docker build -t porttest。

并将80端口发布到localhost:80来运行它

Docker运行-p 80:80 porttest

当你进入localhost:80,你会看到nginx默认页面。 Nginx默认页面

大多数人使用docker组合网络。文件说明:

Docker网络特性支持创建网络而不需要暴露网络中的端口,详细信息请参阅此特性的概述)。

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

基本上,你有三(四)种选择:

不要指定EXPOSE或-p 只指定EXPOSE 指定EXPOSE和-p 只指定隐式执行EXPOSE的-p

If you specify neither EXPOSE nor -p, the service in the container will only be accessible from inside the container itself. If you EXPOSE a port, the service in the container is not accessible from outside Docker, but from inside other Docker containers. So this is good for inter-container communication. If you EXPOSE and -p a port, the service in the container is accessible from anywhere, even outside Docker. If you do -p, but do not EXPOSE, Docker does an implicit EXPOSE. This is because if a port is open to the public, it is automatically also open to other Docker containers. Hence -p includes EXPOSE. This is effectively same as 3).

以我之见,两者分开的原因是:

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

文档明确指出:

EXPOSE指令公开在链接中使用的端口。

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

简短的回答:

EXPOSE是一种记录的方式 ——publish(或-p)用于将主机端口映射到正在运行的容器端口


注意如下:

EXPOSE与Dockerfiles相关(文档化) ——publish与docker run相关…(执行/运行时)

Exposing and publishing ports In Docker networking, there are two different mechanisms that directly involve network ports: exposing and publishing ports. This applies to the default bridge network and user-defined bridge networks. You expose ports using the EXPOSE keyword in the Dockerfile or the --expose flag to docker run. Exposing ports is a way of documenting which ports are used, but does not actually map or open any ports. Exposing ports is optional. You publish ports using the --publish or --publish-all flag to docker run. This tells Docker which ports to open on the container’s network interface. When a port is published, it is mapped to an available high-order port (higher than 30000) on the host machine, unless you specify the port to map to on the host machine at runtime. You cannot specify the port to map to on the host machine when you build the image (in the Dockerfile), because there is no way to guarantee that the port will be available on the host machine where you run the image. from: Docker container networking Update October 2019: the above piece of text is no longer in the docs but an archived version is here: docs.docker.com/v17.09/engine/userguide/networking/#exposing-and-publishing-ports Maybe the current documentation is the below: Published ports By default, when you create a container, it does not publish any of its ports to the outside world. To make a port available to services outside of Docker, or to Docker containers which are not connected to the container's network, use the --publish or -p flag. This creates a firewall rule which maps a container port to a port on the Docker host. and can be found here: docs.docker.com/config/containers/container-networking/#published-ports

同时,

暴露 ...EXPOSE指令实际上并不发布端口。它作为构建映像的人员和运行容器的人员之间的一种文档类型,用于发布关于哪些端口的信息。 来自:Dockerfile引用






没有定义EXPOSE /——publish时的服务访问:

在@Golo Roden的回答中,它指出::

“如果你不指定其中任何一个,容器中的服务将不能从容器本身之外的任何地方访问。”

也许在编写答案时就是这样,但现在看来,即使您不使用EXPOSE或——publish,同一网络的主机和其他容器也能够访问您可能在该容器中启动的服务。

如何测试:

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

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

我将映像构建为“testexpose”,并运行一个新的容器:

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关于这个主题的非常详细的文章:

暴露的港口 发表的港口

可以使用Dockerfile中的expose关键字或 ——将标志暴露给docker运行。公开端口是一种记录端口的方法 使用了端口,但实际上没有映射或打开任何端口。暴露的港口 是可选的。

来源:github提交