1. 概述
如我们所知,Docker Compose 是一个用于定义和管理多个容器的工具。默认情况下,Docker Compose 为定义的容器设置了一个专用网络,允许它们之间的通信。因此,我们可以使用单个命令创建并运行具有给定配置文件的服务。
在本教程中,我们将关注两个 YAML 属性,它们允许我们自定义容器之间的网络,即 expose 和 ports。我们将详细介绍它们,探索基本用例,并突出它们的关键区别。
2. expose 部分
首先,让我们看看 expose 配置。此属性定义了 Docker Compose 从容器暴露的端口。
这些 端口仅由连接到同一网络的其他服务访问,但不会发布到主机机器。
可以通过在 services 部分指定端口号来暴露端口:
services:
myapp1:
.
expose:
- "3000"
- "8000"
myapp2:
...
expose:
- "5000"
如您所见,我们可以为每个服务指定多个值。我们刚刚从 myapp1 容器暴露了端口 3000 和 8000,以及 myapp2 容器的端口 5000。现在,同一网络中的其他容器可以使用这些端口访问服务。
现在让我们检查已暴露的端口:
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8673c14f18d1 ... ... ... ... 3000/tcp, 8000/tcp bael_myapp1
bc044e180131 ... ... ... ... 5000/tcp bael_myapp2
在 docker ps 命令输出中,我们可以在 PORTS 列中找到已暴露的端口。
最后,让我们验证容器之间的通信:
> docker exec -it bc044e180131 /bin/bash
bash-5.1$ nc -vz myapp1 3000
myapp1 (172.18.0.1:3000) open
bash-5.1$ nc -vz myapp1 8000
myapp1 (172.18.0.1:8000) open
我们刚刚连接到了 myapp2 的 CLI。使用 netcat 命令,我们检查了 myapp1 中暴露的端口是否可达。
3. ports 部分
现在让我们查看 ports 部分。与 expose 相同,此属性定义了我们希望从容器暴露的端口。但是,与 expose 配置不同,这些端口不仅对内部可用,还会发布到主机机器。
再次,就像之前一样,我们可以在专门的区域为每个服务定义端口,但配置可能更复杂。首先,我们需要在两种语法(简短和长)之间选择来定义配置。
3.1. 简短语法
首先分析简短语法。简短语法是用于设置主机 IP 地址、主机端口和容器端口的冒号分隔字符串:
[HOST:]CONTAINER[/PROTOCOL]
这里HOST 是主机端口号或端口号范围,可以在 IP 地址之后。如果我们不指定 IP 地址,Docker Compose 将绑定端口到所有网络接口。
CONTAINER 定义容器端口号或端口号范围。
PROTOCOL 限制容器端口到指定协议或如果为空则设置为 TCP。仅 CONTAINER 部分是必需的。
了解了语法后,让我们在 Docker Compose 文件中定义端口:
services:
myapp1:
...
ports:
- "3000" # container port (3000), assigned to random host port
- "3001-3005" # container port range (3001-3005), assigned to random host ports
- "8000:8000" # container port (8000), assigned to given host port (8000)
- "9090-9091:8080-8081" # container port range (8080-8081), assigned to given host port range (9090-9091)
- "127.0.0.1:8002:8002" # container port (8002), assigned to given host port (8002) and bind to 127.0.0.1
- "6060:6060/udp" # container port (6060) restricted to UDP protocol, assigned to given host (6060)
如上所示,我们也可以一次使用不同的简短语法变体并更精确地配置它,将所有指定的容器端口暴露出来,使它们在本地机器内外均可访问。
如之前所述,让我们使用 docker ps 命令检查已暴露的端口:
> docker ps -a
CONTAINER ID ... PORTS NAMES
e8c65b9eec91 ... 0.0.0.0:51060->3000/tcp, 0.0.0.0:51063->3001/tcp, 0.0.0.0:51064->3002/tcp, bael_myapp1
0.0.0.0:51065->3003/tcp, 0.0.0.0:51061->3004/tcp, 0.0.0.0:51062->3005/tcp,
0.0.0.0:8000->8000/tcp, 0.0.0.0:9090->8080/tcp, 0.0.0.0:9091->8081/tcp
127.0.0.1:8002->8002/tcp, 0.0.0.0:6060->6060/udp
再次,在 PORTS 列中,我们可以找到所有已暴露的端口。箭头左侧的值显示了外部可以从主机机器访问容器的位置。
3.2. 长语法
使用长语法,我们可以以相同的方式配置端口。但是,而不是使用冒号分隔的字符串,我们将单独定义每个属性:
services:
myapp1:
...
ports:
# - "127.0.0.1:6060:6060/udp"
- target: 6060
host_ip: 127.0.0.1
published: 6060
protocol: udp
mode: host
在这里,target 是必需的,并且指定了将被暴露的容器端口(或端口范围),这等同于简短语法中的 CONTAINER。
host_ip 和 published 是简短语法中 HOST 的部分,其中我们可以定义主机的 IP 地址和端口。
protocol 与简短语法中的 PROTOCOL 相同,限制容器端口到指定协议(或如果为空则设置为 TCP)。
mode 是枚举类型,包含两种值,用于指定端口发布规则。我们应该使用 host 值来本地发布端口。第二个值 ingress 保留给更复杂的容器环境,表示端口将进行负载平衡。
总之,任何简短语法字符串都可以轻松转换为长结构。然而,并非所有长语法配置都可以移动到简短语法中,因为缺少 mode 属性。
4. 总结
在本文中,我们覆盖了 Docker Compose 中的部分网络配置。我们分析并比较了使用 expose 和 ports 部分的端口配置。
expose 部分允许我们仅将特定端口从我们的容器暴露给同一网络上的其他服务。我们只需指定容器端口就可以做到这一点。
ports 部分也允许从容器暴露指定的端口。与之前的部分不同,端口不仅对同一网络上的其他服务可用,还可以对外部主机。配置稍微复杂一些,我们可以配置暴露的端口、本地绑定地址和限制协议。最后,根据我们的偏好,我们可以选择两种不同的语法之一。