Docker 网络硬核实战:抛弃 docker0,手写网桥验证 Veth Pair 底层原理
本文由 Gemini-3-pro-preview 生成
在 Docker 的学习中,很多教程都基于默认的 docker0 网桥进行讲解。但在实际的生产环境中,我们几乎从不使用默认网桥,而是为每个项目创建独立的自定义网桥(User-defined Bridge)。
这样做不仅是为了安全隔离,更是为了实现容器间的 DNS 自动解析(通过容器名互通)。
今天,我们就来玩点硬核的:手动创建一个自定义网络,并像“网络侦探”一样,一步步验证容器是如何通过 Veth Pair 技术挂载到这个私有网桥上的。
一、 为什么我们要自己建网桥?
在开始之前,先说明为什么今天的实验不使用默认的 docker0。
当我们执行 docker network create my-net 时,Docker 会在底层创建一个全新的 Linux Bridge。相比于大杂烩般的 docker0,自定义网桥有两大核心优势:
- 自动 DNS 解析:在自定义网络里,容器之间可以直接通过“容器名”ping 通,而
docker0只能用 IP。 - 隔离性:只有加入这个网桥的容器才能互通,防止了不相关的服务互相干扰。
今天的实验,我们就来看看这个“私有交换机”是如何通过 Veth Pair 连接容器的。
二、 核心原理:Veth Pair 穿墙术
无论网桥怎么变,Docker 连接容器的核心技术 Veth Pair 是不变的。
你可以把 Veth Pair 想象成一根**“跨维度的双头网线”**:
- 一头(eth0):插在容器这个“密封房间”内部。
- 另一头(vethXXX):插在宿主机的“自定义网桥”上。
- 特性:数据从一头进,立马从另一头出。
我们的目标,就是找到这根网线的两头,证明它们是连通的,并且确认它们插在了我们创建的那个自定义网桥上。
三、 实战验证步骤
第一步:创建自定义网络(购买私有交换机)
首先,我们创建一个名为 my-custom-net 的网络:
docker network create my-custom-net
此时,Docker 会在宿主机上创建一个新的网桥接口。我们可以通过 ip addr 看到它,通常命名为 br-<网络ID的前12位>:
# 查看宿主机网卡
ip addr | grep br-
你会看到类似 br-123456abcdef 这样的网卡,这就是我们刚刚创建的自定义网桥。
第二步:启动容器并加入网络(插上网线)
启动一个 alpine 容器,指定连接到我们创建的网络:
docker run -d --name veth-test --network my-custom-net alpine sleep 3600
第三步:寻找网线的“容器端”
我们要进入容器内部,看看它的网卡长什么样。
docker exec -it veth-test ip addr
输出示例(请记录下你的数字):
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 ...
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
🔍 侦探线索 1:
- 容器里的网卡叫
eth0。 - 关键信息是
eth0@if29。 - 这个
29是宿主机端那头网线的 Index ID。这意味着,我们要去宿主机上找编号为 29 的设备。
第四步:寻找网线的“宿主机端”
回到宿主机,执行 ip addr,瞪大眼睛找编号为 29 的网卡:
ip addr
输出示例:
...
29: veth8a7b6c5@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> ... master br-123456abcdef
...
🔍 侦探线索 2:
- 找到了!编号 29 的设备叫
veth8a7b6c5(名字是随机生成的)。 - 它后面写着
@if28,这正好对应容器里那个编号为 28 的eth0。配对成功!
第五步:验证它插在“自定义网桥”上
这是最关键的一步。我们要证明这根 veth 线没有插在默认的 docker0 上,而是插在了我们自己的 my-custom-net 上。
这里我们使用经典的 Linux 网桥管理工具 brctl 来查看(如果未安装,通常包含在 bridge-utils 包中)。
执行命令:
brctl show
输出示例:
bridge name bridge id STP enabled interfaces
br-123456abcdef 8000.024255cfXXXX no veth8a7b6c5
docker0 8000.0242d6XXXXXX no
🔍 最终结论: 请注意看第一行:
- bridge name:
br-123456abcdef(这是我们自定义网络的 ID)。 - interfaces:
veth8a7b6c5(这是我们刚刚找到的宿主机端网线)。
这清晰地表明:这根 veth 网线已经被稳稳地插在了我们的自定义网桥上,而旁边的 docker0 的 interfaces 栏是空的(或者没有这个 veth),证明它们是完全隔离的。
四、 架构总结图
通过上面的实验,我们可以画出这样一张更加符合生产环境的网络拓扑图:
+-------------------------------------------------------+
| 宿主机 (Host) |
| |
| [默认网桥 docker0] (闲置中...) |
| |
| +---------------------------+ |
| | 自定义网桥 (br-xxxxxx) | <--- 我们创建的 my-custom-net
| +---------------------------+ |
| ^ |
| | 插在这里 |
| [veth宿主端] |
| (veth8a7b6c5) |
+-----------------|-------------------------------------+
| <-- Veth Pair (虚拟网线)
+-----------------|-------------------------------------+
| [veth容器端] |
| (重命名为 eth0) |
| |
| 容器 (Container: veth-test) |
+-------------------------------------------------------+
五、 总结
- Veth Pair 是桥梁:无论你用什么网络模式(只要是 Bridge 类),Docker 都是通过 Veth Pair 技术,像拉网线一样把容器和外部连通。
- 自定义网桥更专业:在本文中,我们验证了 Veth Pair 的一端是连接在以
br-开头的自定义网桥上。这解释了为什么在这个网络里的容器可以互相解析域名——因为这个独立的网桥不仅负责数据转发,还内置了 DNS 服务发现功能。 - 排查思路:以后遇到容器网络问题,先看
docker network ls找到网桥 ID,再通过brctl show查看 veth 是否正确挂载,网络链路一目了然。