介绍
在本实验中,我们将探索 Docker 网络的基础知识。Docker 网络使容器能够相互通信并与外部世界进行交互。我们将涵盖各种网络类型、创建自定义网络、连接容器以及管理网络配置。通过动手实践,你将获得 Docker 网络概念和实践的坚实基础。
在本实验中,我们将探索 Docker 网络的基础知识。Docker 网络使容器能够相互通信并与外部世界进行交互。我们将涵盖各种网络类型、创建自定义网络、连接容器以及管理网络配置。通过动手实践,你将获得 Docker 网络概念和实践的坚实基础。
Docker 提供了多种内置的网络驱动。让我们从检查系统上的默认网络开始。
在你的终端中,运行以下命令以列出所有可用的 Docker 网络:
docker network ls
该命令会列出 Docker 在系统上创建的所有网络。你应该会看到类似以下的输出:
NETWORK ID NAME DRIVER SCOPE
79dce413aafd bridge bridge local
91199fc6ad2e host host local
1078d2c781b6 none null local
让我们分解一下默认的网络类型:
bridge
:这是默认的网络驱动。当你启动一个容器而不指定网络时,它会自动连接到桥接网络。同一桥接网络上的容器可以通过它们的 IP 地址相互通信。
host
:该驱动移除了容器与 Docker 主机之间的网络隔离。容器共享主机的网络命名空间,这意味着它直接使用主机的 IP 地址和端口空间。这在某些场景下对优化性能非常有用。
none
:该驱动会禁用容器的所有网络功能。使用此网络类型的容器将无法访问外部网络或其他容器。当你希望完全隔离一个容器时,这非常有用。
SCOPE
列表示网络是仅限于单个主机(local
),还是可以跨越 Docker Swarm 中的多个主机(swarm
)。
现在我们已经看到了网络列表,接下来让我们更详细地了解一下默认的桥接网络。这个网络是由 Docker 自动创建的,除非另有指定,否则容器会使用它。
运行以下命令来检查桥接网络:
docker network inspect bridge
该命令提供了关于桥接网络的详细信息,包括其子网、网关和连接的容器。你会看到类似以下的输出(为简洁起见已截断):
[
{
"Name": "bridge",
"Id": "79dce413aafdd7934fa3c1d0cc97decb823891ce406442b7d51be6126ef06a5e",
"Created": "2024-08-22T09:58:39.747333789+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
让我们分解一下输出中的一些关键信息:
Subnet
:此网络中容器使用的子网是 172.17.0.0/16
。这意味着容器将被分配在此范围内的 IP 地址。Gateway
:此网络的网关是 172.17.0.1
。这是容器用于与外部网络通信的 IP 地址。Containers
:此字段为空,因为我们尚未启动任何容器。Options
:这些是桥接网络的各种配置选项。例如,enable_icc
设置为 "true" 表示允许此网络上的容器间通信。理解这些信息对于排查网络问题或配置容器以与特定 IP 范围通信至关重要。
虽然默认的桥接网络适用于许多场景,但创建自定义网络可以实现更好的隔离和控制。当你希望将相关的容器分组或需要控制哪些容器可以相互通信时,自定义网络特别有用。
让我们创建一个名为 my-network
的自定义桥接网络:
docker network create --driver bridge my-network
该命令创建了一个新的桥接网络。--driver bridge
选项在这里实际上是可选的,因为桥接是默认驱动,但为了清晰起见,我们将其包含在内。
现在,让我们验证新网络是否已创建:
docker network ls
你应该会在网络列表中看到 my-network
:
NETWORK ID NAME DRIVER SCOPE
1191cb61c989 bridge bridge local
91199fc6ad2e host host local
47ac4e684a72 my-network bridge local
1078d2c781b6 none null local
我们的新网络 my-network
出现在列表中,确认它已成功创建。现在,这个网络可供容器连接。
现在我们已经有了自定义网络,接下来让我们创建两个容器并将它们连接到该网络。我们将使用 nginx
镜像作为示例,它提供了一个轻量级的 Web 服务器。
运行以下命令来创建两个容器:
docker run -d --name container1 --network my-network nginx
docker run -d --name container2 --network my-network nginx
让我们分解这些命令:
-d
:此标志以分离模式运行容器,意味着它在后台运行。--name
:为容器分配一个名称,便于后续引用。--network
:指定容器应连接到的网络。nginx
:这是我们用于创建容器的镜像名称。这些命令创建了两个以分离模式(-d
)运行的容器,分别命名为 container1
和 container2
,并且都连接到了我们的 my-network
。
让我们验证这些容器是否正在运行并连接到我们的网络:
docker ps
该命令列出了所有正在运行的容器。你应该会在输出中看到这两个容器:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1234567890ab nginx "/docker-entrypoint.…" 10 seconds ago Up 9 seconds 80/tcp container2
abcdef123456 nginx "/docker-entrypoint.…" 20 seconds ago Up 19 seconds 80/tcp container1
此输出显示两个容器都在运行 Nginx 镜像,并在容器内部暴露了端口 80。
Docker 网络的一个关键优势是,同一网络上的容器可以使用其容器名称作为主机名相互通信。这使得在服务之间建立通信变得非常容易,而无需知道它们的 IP 地址。
让我们通过使用 container1
向 container2
发送请求来测试这一点:
docker exec container1 curl -s container2
让我们分解这个命令:
docker exec
:这告诉 Docker 在正在运行的容器内执行命令。container1
:这是我们要在其中执行命令的容器名称。curl -s container2
:这是我们在容器内执行的命令。它向 container2
发送一个 GET 请求,-s
标志使 curl 以静默模式运行。该命令在 container1
内执行 curl
命令,向 container2
发送请求。你应该会在输出中看到默认的 Nginx 欢迎页面 HTML:
<!doctype html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html {
color-scheme: light dark;
}
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
</p>
<p>
For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br />
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.
</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
这个成功的响应表明 container1
可以使用其容器名称与 container2
通信。Docker 的内置 DNS 服务器将容器名称解析为网络内的 IP 地址。
默认情况下,自定义网络中的容器可以相互通信,但它们无法从 Docker 主机外部访问。要使容器可以从主机或外部网络访问,我们需要暴露其端口。
让我们创建一个暴露端口的新容器:
docker run -d --name exposed-container -p 8080:80 --network my-network nginx
让我们分解这个命令:
-d
:以分离模式运行容器。--name exposed-container
:将容器命名为 "exposed-container"。-p 8080:80
:将容器内的端口 80 映射到主机的端口 8080。--network my-network
:将容器连接到我们的自定义网络。nginx
:使用 Nginx 镜像。该命令创建了一个名为 exposed-container
的新容器,将容器的端口 80 映射到主机的端口 8080,并将其连接到我们的 my-network
。
现在,你可以通过打开 Web 浏览器并访问 http://localhost:8080
,或使用 curl 从主机访问该容器的 Nginx 服务器:
curl localhost:8080
你应该会看到与之前相同的 Nginx 欢迎页面 HTML。不过,这次我们是从主机直接访问容器,而不是从另一个容器访问。
在某些场景中,如果你希望容器共享主机的网络栈,可以使用主机网络驱动。这会移除容器与主机之间的网络隔离,这对于某些应用程序非常有用,但由于可能导致端口冲突,因此应谨慎使用。
让我们创建一个使用主机网络的容器:
docker run -d --name host-networked --network host nginx
该命令创建了一个名为 host-networked
的新容器,并使用主机网络。请注意,在使用主机网络时不能使用 -p
参数,因为容器已经使用了主机的网络接口。
为了验证容器是否使用了主机网络,我们可以检查其网络设置:
docker inspect --format '{{.HostConfig.NetworkMode}}' host-networked
该命令检查容器并格式化输出以仅显示 NetworkMode。它应该输出 host
,确认容器正在使用主机网络。
当使用主机网络时,容器共享主机的 IP 地址,并可以直接访问主机的所有网络接口。这对于最大化性能非常有用,但也意味着容器使用的任何端口都会直接在主机上打开,如果不小心处理,可能会导致冲突。
在本实验中,我们探索了 Docker 网络的基础知识。我们涵盖了各种网络类型、创建自定义网络、连接容器、测试容器间通信、暴露容器端口以及使用主机网络。这些概念构成了 Docker 网络的基础,对于设计和管理的容器化应用程序至关重要。
我们学习了如何:
理解这些网络概念将帮助你设计更健壮和安全的容器化应用程序。随着你继续使用 Docker,你会遇到更高级的网络场景,但我们在这里介绍的原则将为你进入容器编排和微服务架构的旅程奠定坚实的基础。
请记住,Docker 网络是一个强大的工具,它允许你为应用程序创建隔离的环境,同时仍然允许容器之间以及与外部世界进行受控的通信。练习这些概念并探索更高级的主题,以熟练掌握管理 Docker 网络。