Docker 网络硬核实战:抛弃 docker0,手写网桥验证 Veth Pair 底层原理

29 阅读5分钟

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,自定义网桥有两大核心优势:

  1. 自动 DNS 解析:在自定义网络里,容器之间可以直接通过“容器名”ping 通,而 docker0 只能用 IP。
  2. 隔离性:只有加入这个网桥的容器才能互通,防止了不相关的服务互相干扰。

今天的实验,我们就来看看这个“私有交换机”是如何通过 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)                    |
+-------------------------------------------------------+

五、 总结

  1. Veth Pair 是桥梁:无论你用什么网络模式(只要是 Bridge 类),Docker 都是通过 Veth Pair 技术,像拉网线一样把容器和外部连通。
  2. 自定义网桥更专业:在本文中,我们验证了 Veth Pair 的一端是连接在以 br- 开头的自定义网桥上。这解释了为什么在这个网络里的容器可以互相解析域名——因为这个独立的网桥不仅负责数据转发,还内置了 DNS 服务发现功能。
  3. 排查思路:以后遇到容器网络问题,先看 docker network ls 找到网桥 ID,再通过 brctl show 查看 veth 是否正确挂载,网络链路一目了然。