在多容器设置中,容器中运行的服务通过公共网络相互通信。 在相同的设置中,一些容器还与外部世界交互。
这种内部和外部通信分别由 Docker 中公开和发布的端口处理。
在本教程中,我将讨论在 Docker 中处理端口。 我将继续讨论公开端口和发布端口之间的区别、使用它们的原因以及如何使用它们。
在 Docker 中公开与发布端口
在 Docker 中有两种处理端口的方法:公开端口和发布端口。
暴露一个端口 仅仅意味着让其他人知道容器化应用程序将在哪个端口上侦听或接受连接。 这是为了与其他容器通信,而不是与外界通信。
发布端口 更像是将容器的端口与主机的端口进行映射。 通过这种方式,容器能够与外部系统、现实世界、互联网进行通信。
该图形将帮助您理解。
你看,SERVER容器的80端口是怎么映射到主机系统的80端口的? 这样,容器就能够使用主机系统的公共 IP 地址与外界通信。
另一方面,无法从容器世界外部直接访问暴露的端口。
要记住:
- 暴露的端口用于容器世界内的内部容器通信。
- 发布的端口用于与容器世界之外的系统进行通信。
在 Docker 中暴露端口
首先,暴露一个端口并不是绝对必要的。 为什么? 因为您在设置中使用的大多数 docker 映像已经在其配置中公开了一个默认端口。
为了 example,容器化的前端应用程序可以通过简单地指定容器的 IP 和 MariaDB 接受连接的端口(默认 3306)与 MariaDB 数据库通信。
暴露在不使用默认值的情况下会有所帮助,例如在这种情况下,如果 MariaDB 不接受端口 3306 上的连接,则应通过暴露它来提及替代端口。
有两种方法可以公开端口:
- 使用
EXPOSE
Dockerfile 指令。 - 使用
--expose
使用 docker CLI 或expose
键入 docker-compose。
是时候深入研究两者了。
方法一:通过 Dockerfile 暴露端口
您可以在 Dockerfile 中添加一个简单的指令,让其他人知道您的应用程序将在哪个端口接受连接。
关于本说明,您必须了解以下内容:-
EXPOSE
才不是 向生成的 docker 图像添加额外的层。 它只是添加元数据。EXPOSE
是一种记录应用程序端口的方法。 它的唯一作用是在可读性或理解应用程序方面。
您可以看到 Expose 如何与我为此目的而构建的简单容器映像一起工作。 这个图像没有任何作用。
拉出图像。
docker pull debdutdeb/expose-demo:v1
此镜像一共暴露了四个端口,使用以下命令列出镜像。
docker image ls --filter=reference=debdutdeb/expose-demo:v1
看看 SIZE
列,它会说 0 个字节。
➟ docker image ls --filter=reference=debdutdeb/expose-demo:v1
REPOSITORY TAG IMAGE ID CREATED SIZE
debdutdeb/expose-demo v1 ad3d8ffa9bfe N/A 0B
原因很简单,这张图片中没有图层,所有添加的暴露指令都是元数据,没有真正的图层。
您还可以使用以下命令获取可用层数:-
docker image inspect -f '{{len .RootFS.Layers}}' debdutdeb/expose-demo:v1
你应该看到这样的输出:-
➟ docker image inspect -f '{{len .RootFS.Layers}}' debdutdeb/expose-demo:v1
0
正如我之前所说,暴露的端口被添加为图像元数据,让我们知道应用程序正在使用哪些端口。
在不查看 Dockerfile 的情况下如何查看这些信息? 您需要检查图像。 更具体地说,请参见下面的命令,您可以在任何图像上使用类似的命令来列出暴露的端口。
我正在使用我的示例图像进行演示。
docker image inspect -f
'{{range $exposed, $_ := .Config.ExposedPorts}}{{printf "%sn" $exposed}}{{end}}'
debdutdeb/expose-demo:v1
样本输出:
➟ docker image inspect -f '{{range $exposed, $_ := .Config.ExposedPorts}}{{printf "%sn" $exposed}}{{end}}' debdutdeb/expose-demo:v1
443/tcp
80/tcp
8080/tcp
9090/tcp
您还可以将这个暴露了几个端口的图像与 debdutdeb/noexpose-demo:v1
图像,这没有暴露任何端口。 在该图像上运行相同的命令集并注意差异。
方法 2:通过 CLI 或 docker-compose 暴露端口
有时应用程序开发人员会回避包含额外的 EXPOSE
他们的 Dockerfile 中的说明。
在这种情况下,为了确保其他容器(通过 docker API)可以轻松检测到正在使用的端口,您可以在构建后公开多个端口,作为部署过程的一部分。
要么选择命令式方法,即 CLI,要么选择声明式方法,即编写文件。
命令行方法
在这种方法中,在创建容器时,您所要做的就是使用 --expose
带有端口号的选项(根据需要多次)和可选的带有协议的协议 /
. 这是一个 example:-
docker container run
--expose 80
--expose 90
--expose 70/udp
-d --name port-expose busybox:latest sleep 1d
我正在使用 busybox
默认情况下不公开任何端口的图像。
编写文件方法
如果您使用的是撰写文件,则可以添加一个数组 expose
在服务定义中。 您可以将以前的部署转换为 compose 文件,如下所示:-
version: "3.7"
services:
PortExpose:
image: busybox
command: sleep 1d
container_name: port-expose
expose:
- 80
- 90
- 70/udp
一旦你让容器运行,就像你可以检查它以了解哪些端口正在暴露之前一样。 该命令看起来很相似。
docker container inspect -f
'{{range $exposed, $_ := .NetworkSettings.Ports}}
{{printf "%sn" $exposed}}{{end}}'
port-expose
样本输出:-
➟ docker container inspect -f '{{range $exposed, $_ := .NetworkSettings.Ports}}{{printf "%sn" $exposed}}{{end}}' port-expose
70/udp
80/tcp
90/tcp
在 Docker 中发布端口
端口发布是端口转发的同义词,其中来自公共端口上的传入连接的请求被转发到容器的端口。
类似地,容器通过其端口发送的响应通过将流量转发到主机端口空间中的指定端口来发送到客户端。
有两种发布端口的方法,一种是通过 CLI,另一种是使用 compose 文件。 这两种方法也有一种长语法和一种短语法。
方法一:通过 Docker 命令发布端口
两种语法如下:
-p [optional_host_ip]:[host_port]:[container_port]/[optional_protocol]
--publish target=[container_port],published=[optional_host_ip]:[host_port],protocol=[optional_protocol]
对于可选的主机 IP,您可以使用与任何 NIC 关联的任何 IP 地址。 如果省略 IP,docker 会将端口与所有可用的 IP 地址绑定。
您将最常使用第一个。 第二个更具可读性。 让我们看一个 example 使用 nginx 容器。 运行以下命令:-
docker container run --rm --name nginx
--publish target=80,published=127.0.0.1:8081,protocol=tcp
-d nginx
使用此命令,我只是将容器的端口 80 绑定到本地主机上主机的 8081 端口。 现在,如果你前往 https://localhost:8081 你会看到 nginx 正在运行。
前面的命令可以很容易地转换为更短的形式,就像这样
docker container run --rm --name nginx
-p 80:127.0.0.1:8081/tcp -d nginx
虽然它更短,但更难阅读。
你也可以使用 -p
或者 --publish
多次发布多个端口。
方法二:通过compose文件发布端口
要使用 compose 文件发布端口,您需要一个名为 ports
在服务定义中。 该数组可以是类似于 CLI 短语法的字符串列表,也可以使用类似于长语法的对象列表。
如果我要使用 compose 文件转换以前的 nginx 部署,该文件带有端口部分的字符串数组,它将如下所示:-
version: "3.7"
services:
Nginx:
image: nginx
container_name: nginx
ports:
- 80:127.0.0.1:8081/tcp
让我也展示如何使用对象数组语法。
version: "3.7"
services:
Nginx:
image: nginx
container_name: nginx
ports:
- target: 80
published: 127.0.0.1:8081
protocol: tcp
要查看所有已发布端口的列表,您可以像这样检查容器 –
docker container inspect -f '{{range $container, $host := .NetworkSettings.Ports}}{{printf "%s -> %sn" $container $host}}{{end}}' nginx
如果运行,您将看到以下输出:-
➟ docker container inspect -f '{{range $container, $host := .NetworkSettings.Ports}}{{printf "%s -> %sn" $container $host}}{{end}}' nginx
80/tcp -> [{127.0.0.1 8081}]
还有另一种更简单的方法来列出已发布的端口,使用 docker container port
命令。
docker container port nginx
示例输出:-
➟ docker container port nginx
80/tcp -> 127.0.0.1:8081
什么时候公开一个端口,什么时候发布它?
这是一个公平的问题。 曝光和出版不应该是竞争对手。 每个都有不同的目的。 如果您是图像的开发人员,您将公开端口,以便用户可以更好地确定尝试连接的位置。 另一方面,如果您正在使用图像并且需要将其提供给外部世界,那么您将发布必要的端口。
我希望这篇文章对你有所帮助。 如果您有任何疑问,请在下面的评论中告诉我。