Moby 遗留网络架构详解:libnetwork 之前基于 Bridge + iptables 的单主机容器网络设计
本文围绕 Moby(Docker 引擎)仓库中的 legacy.md 展开,系统梳理 libnetwork 引入之前(Docker v1.6 时期)的容器网络设计:Docker Engine 与 libcontainer 各自的职责分工、Linux Bridge 与 iptables 如何协作实现容器互联、--link/--expose 抽象与 NAT 端口映射的对外连接方案,并结合当前仓库中 bridge 驱动、iptabler 等源码说明这套"遗留"机制在现代 Moby 中的存续形态。读完本文,你可以准确理解 Docker 单主机网络的底层工作流,并知道现代 libnetwork 架构为何被提出。
文档定位:libnetwork 设计文档的"前传"
daemon/libnetwork/docs/legacy.md 是 libnetwork 文档族中的一份"TL;DR"文档,其开头明确说明:本文档是 Docker v1.6 时代网络设计的浓缩版本,更详细的操作设计请参阅对应的完整设计文档。它与 design.md 构成对照关系——后者在开篇即指出:libnetwork 的许多设计决策"汲取自 Docker v1.6 网络设计的经验教训",并引导读者阅读 legacy.md 了解旧架构。
因此,理解 legacy.md 的价值在于:它回答了"在 CNM(Container Network Model)出现之前,Docker 是如何做网络的",这也是理解 libnetwork 为何要拆分成 Controller/Driver/Network/Endpoint/Sandbox 等抽象的历史基线。
Docker v1.6 时代的网络架构:职责分工
根据 legacy.md 的描述,libnetwork 出现之前,Docker 的网络能力由两个组件共同承担:
- Docker Engine:负责提供容器网络栈的配置(configuration for the container's networking stack)。它使用 Bridge Driver(桥接驱动)来提供单主机网络方案,底层依赖 Linux Bridge(Linux 网桥)和 iptables 实现。
- Libcontainer:接收 Docker Engine 给出的配置信息,负责创建必要的网络设备并将这些设备移入容器的网络命名空间(network namespace)。容器启动时使用的正是这个已配置好的命名空间。
用一句话概括旧架构的分工:Docker Engine 出"设计图纸",libcontainer 干"施工落地"。前者决定容器应该有什么样的网络栈,后者真正把 veth 设备、IP 地址、路由表写进容器的 netns 里。
Docker Engine 在这一架构中还承担了一个关键的"抽象"职责:通过 --link、--expose 等简单的命令行配置项,让同一主机内的容器之间可以直接连通,且把网络配置的细节完全从容器中屏蔽掉——容器内不需要关心自己对端的 IP 是什么,也不需要手工配置路由。
单主机连接:Linux Bridge 的落地细节
legacy.md 提到 Bridge Driver 依赖 linux bridge,结合当前仓库中更完整的 network.md 文档(其内容与 v1.6 时代实现一致,描述了容器接入 docker0 网桥的完整步骤),可以把遗留架构的具体数据面还原如下:
- 创建网络命名空间:为容器创建一个新的 Linux 网络命名空间,该命名空间与宿主机的网络栈隔离,容器内部只能看到自己的网络设备。
- 创建 veth pair:创建一个 veth 设备对,一端挂到宿主机的
docker0网桥上,另一端被移入容器的新命名空间。 - 分配 IP 并配置路由:在新的命名空间内,从
docker0网段的子网中为容器分配一个 IP 地址,并把默认路由的网关设置为docker0的 IP 地址。
至此,bridge 模式下容器的网络配置即告完成。出站流量的路径为:容器命名空间内的路由 → veth pair → 宿主机命名空间中的 docker0 网桥 → 宿主机路由 → 宿主机物理网卡(如 eth0)→ 外部网络;入站流量则沿相反方向流动。
上述机制在当前仓库中仍有直接对应:
- bridge.md 明确说明 bridge 驱动"默认创建一个名为
docker0的网桥,并在网桥与每个 endpoint 之间挂一对 veth",且该驱动基于 Linux Bridging 与 iptables 提供容器连接性; - bridge_store.go 与 interface_linux.go 中依然使用
docker0作为默认桥接设备名; - daemon_unix.go 中关于
docker0与fixed-cidr的注释表明,引擎至今仍区分"docker 管理的网桥(docker0)"与用户自管网桥,并在地址查找失败时回退到默认网桥逻辑——这正是遗留架构在现代代码中的直接延续。
对外连接:NAT 与端口映射
legacy.md 指出,旧架构对外部连接性(external connectivity)完全依赖 NAT 与端口映射(port-mapping)。
network.md 进一步解释了其中的关键一环——masquerade(伪装)规则:容器在 docker0 子网上被分配的 IP(例如 172.17.0.2)对宿主机外部是不可见的。因此,在 Docker 引擎初始化时,会在宿主机命名空间的 nat 表 POSTROUTING 链中添加一条默认的 masquerading 规则:凡经过路由阶段、源 IP 落在 docker0 子网(172.17.0.0/16)内的出站流量,其源 IP 都被替换为路由决定的出站接口的 IP(例如 eth0 的 172.31.2.1)。文档强调:所谓 masquerade,本质上就是"把替换源 IP 固定设置为出站接口 IP 的 SNAT"。
端口映射则是这一 NAT 能力的反向运用:通过 -p 把宿主机端口转发到容器端口,使外部客户端能够触达容器内监听的服务。
这套 iptables 规则的实现,在当前仓库中由 iptabler 包统一维护,包含 network.go(网桥级 NAT/转发规则)、endpoint.go(endpoint 级端口映射规则)与 cleaner.go(规则清理)等文件,并配有 iptabler_test.go 等测试。从源码结构看,现代 bridge 驱动复用的正是 legacy 架构所依赖的那套"bridge + iptables + masquerade + port-mapping"组合,只是规则的管理权从 Docker Engine 直接操作移入了驱动内部。
遗留架构留下的"接口"
legacy 设计中面向用户的几个配置点,如今都以更规范化的形式保留在 Moby 中:
--net=none(即无网络模式):libnetwork 的 design.md 在"Implementations"一节中说明,内置的 null 驱动是一个"noop"实现,专门用于不需要任何网络的场景,其存在目的正是"提供对 Docker--net=none选项的向后兼容"——这是对遗留行为最典型的兼容承诺;- bridge 作为默认网络:bridge.md 说明 bridge 驱动"仅支持默认的 bridge 网络,不能用于其他网络",与 legacy 时代"引擎内置一个 docker0 网桥"的形态一脉相承;
--link/--expose的服务抽象:旧架构中"把网络配置完全从容器中抽象掉"的理念,在 libnetwork 中被形式化为 Endpoint(服务端点)对象,design.md 在 CNM 生命周期中解释了"为什么 CreateEndpoint 与 Join 要分成两个 API"——Endpoint 代表的是可能被容器背书的"服务",资源在创建时即被保留,从而保证容器 Stop/Start 后网络行为一致。
从 Legacy 到 CNM:为什么需要 libnetwork
legacy.md 虽短,但它是理解 design.md 全部抽象的钥匙。旧架构的局限在于:网络能力被硬编码在 Docker Engine 与 libcontainer 两个具体组件中,只支持单主机场景,且驱动不可插拔。libnetwork 的 Container Network Model 用三个核心构件重构了这一模型:
- Sandbox:容纳容器网络栈配置(接口、路由表、DNS),Linux 上的实现就是一个网络命名空间——对应 legacy 架构中 libcontainer 负责的那一层,一个 Sandbox 可以承载来自多个网络的多个 endpoint;
- Endpoint:把 Sandbox 连接到 Network 的桥梁,实现可以是 veth pair——对应 legacy 架构中"移入命名空间的 veth 设备",一个 endpoint 只能属于一个网络和一个 Sandbox;
- Network:能够相互直接通信的一组 Endpoint,实现可以是 Linux 网桥、VLAN 等——对应 legacy 架构中的 docker0 网桥,但被提升为可由多种 Driver 实现的对象。
design.md 同时列出了驱动生命周期(driver.CreateNetwork、driver.CreateEndpoint、driver.Join、driver.Leave 等 Driver 侧 API),内置驱动包括 null、bridge、overlay 与 remote——其中 bridge 驱动继承自 legacy 时代的设计,overlay 驱动则补上了旧架构完全缺失的多主机能力。
小结
legacy.md 用一段精炼的文字固化了 Moby 网络演进史上一个关键基线:Docker Engine 出配置、libcontainer 落设备,Linux Bridge + iptables 实现单主机互联,NAT + 端口映射打通对外连接,--link/--expose 完成用户侧抽象。当前仓库中的 bridge 驱动、iptabler 规则包、null 驱动以及 bridge.md / network.md 文档,共同印证了这套遗留设计在现代 Moby 中既被兼容保留、又被 CNM 抽象所超越的事实。对需要深入排查容器网络问题的工程师而言,理解 legacy 架构是读懂 docker0、masquerade 规则和 bridge 驱动行为的前置知识。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00