Docker网络核心:单机自定义网络实战指南
大家好!在容器化的世界里,Docker已经成为不可或缺的工具。除了容器本身,容器间的网络通信同样至关重要。虽然Docker自带了bridge、host、none等默认网络模式,但在实际开发和生产环境中,默认的bridge网络存在一些局限性(比如容器间无法通过容器名直接通信,需要依赖旧的--link方式或手动管理IP)。为了更灵活、更可靠地管理容器网络,自定义网络成为了我们的首选。
本文将通过一系列实战操作,带你一步步了解如何在Docker单机环境下创建、管理和使用自定义网络,让你轻松驾驭容器间的通信。
阅读本文,你将学会:
- 查看和管理Docker网络。
- 创建基础的自定义桥接网络。
- 在自定义网络中运行容器,并实现容器名互访。
- 创建具有指定网段、网关、IP范围的高级自定义网络。
- 为容器分配静态IP地址。
- 清理不再使用的Docker网络。
准备工作:
- 一台安装了Docker的主机(本文以
root@docker102为例)。 - 基本的Linux命令行操作知识。
一、初识Docker网络:查看现有网络
在开始创建自定义网络之前,我们先看看当前Docker环境中已有的网络。
root@docker102:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
870e105321fc bridge bridge local # 默认桥接网络
d97f63ed6d57 host host local # Host网络模式
2646275ea427 none null local # None网络模式
通常,你会看到至少这三个默认网络:
bridge: 容器默认连接的网络。启动容器时不指定--network,就会连接到这个网络。同一bridge网络内的容器可以通过IP地址互相访问,但默认不支持通过容器名访问。host: 容器共享宿主机的网络命名空间,拥有宿主机的IP和端口。性能好,但隔离性差。none: 容器拥有独立的网络命名空间,但不进行任何网络配置,没有网络连接。
二、创建第一个自定义网络
默认bridge网络的主要痛点在于容器间无法通过服务名(容器名)直接通信。自定义网络完美解决了这个问题。让我们创建一个名为oldboyedu的自定义网络。
# 1. 创建自定义网络(默认使用 bridge 驱动)
root@docker102:~# docker network create oldboyedu
797363b947c9529c8d14964d70bb9752e135600300a4fd9cb5099d5d330f6760
# 2. 再次查看网络列表,确认已创建
root@docker102:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
870e105321fc bridge bridge local
d97f63ed6d57 host host local
2646275ea427 none null local
797363b947c9 oldboyedu bridge local # 新创建的网络
我们看到oldboyedu网络已经创建成功,它使用的驱动也是bridge,但这是一个用户自定义的bridge网络,与默认的bridge网络是隔离的。
三、深入了解网络详情:检查网络配置
我们可以使用docker network inspect命令查看网络的详细信息,包括它的网段、网关等。
root@docker102:~# docker network inspect oldboyedu
[
{
"Name": "oldboyedu",
"Id": "797363b947c9...", // ID已省略
"Created": "2024-07-23T09:13:59.264513362+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": { // IP地址管理配置
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16", // 自动分配的子网
"Gateway": "172.19.0.1" // 自动分配的网关
}
]
},
"Internal": false,
"Attachable": false, // 在Swarm模式下,控制非服务容器是否能连接
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {}, // 当前连接到此网络的容器列表(现在为空)
"Options": {},
"Labels": {}
}
]
可以看到,Docker自动为oldboyedu网络分配了子网172.19.0.0/16和网关172.19.0.1。
四、连接容器:在自定义网络中运行应用
现在,我们在oldboyedu网络中启动两个容器c1和c2。
# 使用的镜像是包含基本网络工具(如ping)的简单Web应用镜像
IMAGE_NAME="registry.cn-hangzhou.aliyuncs.com/yinzhengjie-k8s/apps:v1"
# 1. 启动容器 c1,并将其连接到 oldboyedu 网络
root@docker102:~# docker run -d --name c1 --network oldboyedu $IMAGE_NAME
d5e6af85962a...
# 2. 启动容器 c2,同样连接到 oldboyedu 网络
root@docker102:~# docker run -d --name c2 --network oldboyedu $IMAGE_NAME
e6bba8b4701d...
# 3. 查看容器 c1 的 IP 地址
root@docker102:~# docker container inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" c1
172.19.0.2
# 4. 查看容器 c2 的 IP 地址
root@docker102:~# docker container inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" c2
172.19.0.3
注意:我们使用了--network oldboyedu参数将容器连接到了我们创建的网络。Docker会从网络的可用IP池(172.19.0.0/16)中自动为c1和c2分配IP地址(172.19.0.2和172.19.0.3)。
五、验证通信:容器名解析的魔力
自定义网络最核心的优势之一就是内置了DNS服务。这意味着同一自定义网络内的容器可以通过容器名直接互相访问。让我们进入c1容器来验证一下。
# 1. 进入 c1 容器的 shell 环境
root@docker102:~# docker exec -it c1 sh
# 2. 查看 c1 容器内的 hosts 文件
# 注意:Docker会自动将同网络容器名和IP的映射关系添加到hosts文件(或者通过内置DNS解析)
/ # cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.19.0.3 c2 # Docker 自动添加了 c2 的映射 (在较新版本中可能不直接写入hosts,而是通过内置DNS)
172.19.0.2 d5e6af85962a # c1 自身的映射
# 3. 在 c1 容器内,直接通过容器名 ping c2
/ # ping c2 -c 3
PING c2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.134 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.079 ms
64 bytes from 172.19.0.3: seq=2 ttl=64 time=0.077 ms
--- c2 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.077/0.096/0.134 ms
# 4. 退出容器
/ # exit
成功了!c1可以直接ping通c2,无需关心c2的具体IP地址。这对于构建微服务应用或任何需要容器间通信的场景来说,都极大地简化了配置。
六、网络管理:清理不再使用的网络
随着时间的推移,可能会创建一些测试或临时的网络。Docker提供了清理这些未使用网络的方法。
6.1 批量删除所有未使用的网络
# 1. 先创建两个临时的未使用网络
root@docker102:~# docker network create xixi
71040ade4e22...
root@docker102:~# docker network create haha
c890b2f9fc1f...
# 2. 查看当前网络列表
root@docker102:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
...
c890b2f9fc1f haha bridge local # 新网络
797363b947c9 oldboyedu bridge local # 正在使用
71040ade4e22 xixi bridge local # 新网络
...
# 3. 使用 prune 命令批量删除所有未被容器连接的网络
# -f 参数表示强制执行,不进行确认提示,生产环境慎用!
root@docker102:~# docker network prune -f
Deleted Networks:
xixi
haha
# 4. 再次查看,确认未使用网络已被删除
root@docker102:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
...
797363b947c9 oldboyedu bridge local # 正在使用的 oldboyedu 仍然存在
...
重要提示:
docker network prune只会删除未被任何容器(包括已停止的容器)使用的自定义网络。- 默认的
bridge,host,none网络不会被prune命令删除。 - 在生产环境中使用
prune命令(尤其带-f)前,请务必确认不会误删重要网络。
6.2 删除指定的网络
如果你只想删除某个或某几个特定的网络,可以使用docker network rm命令。
# 1. 再次创建两个临时网络
root@docker102:~# docker network create xixi
1b64707304a0...
root@docker102:~# docker network create haha
f8e49cbe8464...
# 2. 查看列表
root@docker102:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
...
f8e49cbe8464 haha bridge local
797363b947c9 oldboyedu bridge local
1b64707304a0 xixi bridge local
...
# 3. 删除指定的 xixi 和 haha 网络
root@docker102:~# docker network rm xixi haha
xixi
haha
# 4. 确认已删除
root@docker102:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
...
797363b947c9 oldboyedu bridge local
...
注意: 如果有任何容器(即使是已停止的)连接到你尝试删除的网络,docker network rm命令会失败报错,需要先断开或删除这些容器。
七、高级定制:创建具有特定参数的网络
有时,自动分配的网段可能与你现有的网络环境冲突,或者你希望对容器的IP地址范围进行更精细的控制。docker network create提供了丰富的参数来实现这些需求。
7.1 创建自定义子网、网关和IP范围
假设我们想创建一个网络linux92,要求:
- 使用
bridge驱动。 - 整个子网是
172.21.0.0/16。 - 网关地址是
172.21.0.254。 - Docker自动分配给容器的IP地址必须在
172.21.100.0/24这个范围内。
root@docker102:~# docker network create \
-d bridge \ # 指定驱动为 bridge (虽然默认就是)
--subnet 172.21.0.0/16 \ # 定义整个网络的子网范围
--gateway 172.21.0.254 \ # 指定网关 IP
--ip-range 172.21.100.0/24 \ # 指定容器自动获取 IP 的范围
linux92 # 网络名称
ee50da66577d...
# 查看新网络的配置
root@docker102:~# docker network inspect linux92
[
{
"Name": "linux92",
...
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.21.0.0/16", # 确认子网
"IPRange": "172.21.100.0/24", # 确认IP范围
"Gateway": "172.21.0.254" # 确认网关
}
]
},
...
"Containers": {},
...
}
]
7.2 在定制网络中运行容器并验证IP
现在,我们在这个linux92网络中启动容器,看看它们的IP地址是否符合预期。
# 清理之前的同名容器(如果存在)
# docker rm -f c1 c2 c3 || true
# 启动 c1 和 c2 到 linux92 网络
root@docker102:~# docker container run -d --name c1 --network linux92 $IMAGE_NAME
3d461764e6bf...
root@docker102:~# docker container run -d --name c2 --network linux92 $IMAGE_NAME
5bbf3289b885...
# 查看 c1 的网络接口信息
root@docker102:~# docker exec c1 ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:AC:15:64:00
inet addr:172.21.100.0 Bcast:172.21.255.255 Mask:255.255.0.0 # IP 在 172.21.100.0/24 范围内
...
# 查看 c2 的网络接口信息
root@docker102:~# docker exec c2 ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:AC:15:64:01
inet addr:172.21.100.1 Bcast:172.21.255.255 Mask:255.255.0.0 # IP 也在 172.21.100.0/24 范围内
...
正如预期,c1和c2自动获取的IP地址172.21.100.0和172.21.100.1都落在了我们通过--ip-range指定的172.21.100.0/24范围内。
7.3 手动为容器分配固定IP地址
在某些场景下,比如配置依赖特定IP的服务或防火墙规则时,我们希望容器拥有一个固定的IP地址。可以使用--ip参数在docker run时指定。
# 启动 c3,并手动指定 IP 为 172.21.1.102
root@docker102:~# docker container run -d --name c3 --network linux92 --ip 172.21.1.102 $IMAGE_NAME
2f0cfe0936dd...
# 查看 c3 的网络接口信息
root@docker102:~# docker exec c3 ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:AC:15:01:66
inet addr:172.21.1.102 Bcast:172.21.255.255 Mask:255.255.0.0 # IP 是我们手动指定的
...
关键点:
- 手动指定的
--ip地址必须位于网络的--subnet范围内(172.21.0.0/16)。 - 手动指定的
--ip地址不必位于--ip-range范围内(172.21.100.0/24)。--ip-range仅用于Docker自动分配IP地址。 - 指定的IP地址不能已被其他容器或网关使用。
7.4 最终验证:固定IP与自动分配IP容器间的通信
最后,我们验证一下手动分配IP的c3能否与自动分配IP的c1通信。
# 在 c3 容器内 ping c1 (通过容器名)
root@docker102:~# docker exec c3 ping c1 -c 3
PING c1 (172.21.100.0): 56 data bytes
64 bytes from 172.21.100.0: seq=0 ttl=64 time=0.239 ms
64 bytes from 172.21.100.0: seq=1 ttl=64 time=0.097 ms
64 bytes from 172.21.100.0: seq=2 ttl=64 time=0.079 ms
--- c1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.079/0.138/0.239 ms
通信完全正常!这证明了在同一个自定义网络中,无论是自动分配IP还是手动指定IP的容器,都可以利用Docker提供的DNS服务通过容器名进行无缝通信。
八、总结与生产建议
通过本文的实战演练,我们掌握了Docker单机自定义网络的核心用法:
- 创建自定义网络 (
docker network create) 是实现容器间可靠通信(尤其是通过容器名)的基础。 - 检查网络配置 (
docker network inspect) 帮助我们了解网络的拓扑结构。 - 连接容器到网络 (
docker run --network) 是应用自定义网络的关键步骤。 - 内置DNS解析 是自定义网络相较于默认
bridge网络的最大优势。 - 高级定制 (
--subnet,--gateway,--ip-range) 提供了对网络地址规划的精细控制。 - 静态IP分配 (
--ip) 满足了特定场景下固定IP的需求。 - 网络清理 (
docker network prune,docker network rm) 是保持环境整洁的必要操作。
生产环境建议:
- 优先使用自定义网络: 对于任何需要多个容器协作的应用(如微服务架构),强烈建议使用自定义网络,而不是依赖默认的
bridge网络。 - 有意义地命名网络: 给网络起一个能反映其用途的名字(如
app-backend-net,database-net),方便管理和识别。 - 规划IP段: 在复杂的环境中,提前规划好自定义网络的子网和IP范围,避免与宿主机或其他网络环境产生冲突。
- 使用Docker Compose: 对于多容器应用,使用
docker-compose.yml来定义服务和网络是更推荐的方式。Compose可以自动创建和管理网络,简化部署流程。 - 了解其他驱动: 除了
bridge,Docker还支持overlay(用于Docker Swarm集群跨主机通信)、macvlan(让容器在物理网络上拥有独立MAC和IP)等网络驱动,根据需求选用。
希望这篇详细的指南能帮助你更好地理解和运用Docker的自定义网络功能,为你的容器化应用构建稳定、高效的网络基础。如果你有任何问题或想法,欢迎在评论区交流!
浙公网安备 33010602011771号